Function calling for Claude and OpenRouter

This commit is contained in:
Cohee
2024-05-29 00:56:55 +03:00
parent 865c48bcc0
commit 309eb80748
3 changed files with 31 additions and 4 deletions

View File

@ -1739,7 +1739,7 @@
</span> </span>
</div> </div>
</div> </div>
<div class="range-block" data-source="openai,cohere,mistralai,custom"> <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,openrouter">
<label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand"> <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">
<input id="openai_function_calling" type="checkbox" /> <input id="openai_function_calling" type="checkbox" />
<span data-i18n="Enable function calling">Enable function calling</span> <span data-i18n="Enable function calling">Enable function calling</span>

View File

@ -1968,7 +1968,7 @@ async function registerFunctionTools(type, data) {
} }
async function checkFunctionToolCalls(data) { async function checkFunctionToolCalls(data) {
if ([chat_completion_sources.OPENAI, chat_completion_sources.CUSTOM, chat_completion_sources.MISTRALAI].includes(oai_settings.chat_completion_source)) { if ([chat_completion_sources.OPENAI, chat_completion_sources.CUSTOM, chat_completion_sources.MISTRALAI, chat_completion_sources.OPENROUTER].includes(oai_settings.chat_completion_source)) {
if (!Array.isArray(data?.choices)) { if (!Array.isArray(data?.choices)) {
return; return;
} }
@ -1999,6 +1999,21 @@ async function checkFunctionToolCalls(data) {
} }
} }
if ([chat_completion_sources.CLAUDE].includes(oai_settings.chat_completion_source)) {
if (!Array.isArray(data?.content)) {
return;
}
for (const content of data.content) {
if (content.type === 'tool_use') {
/** @type {FunctionToolCall} */
const args = { name: content.name, arguments: JSON.stringify(content.input) };
await eventSource.emit(event_types.LLM_FUNCTION_TOOL_CALL, args);
data.allowEmptyResponse = true;
}
}
}
if ([chat_completion_sources.COHERE].includes(oai_settings.chat_completion_source)) { if ([chat_completion_sources.COHERE].includes(oai_settings.chat_completion_source)) {
if (!Array.isArray(data?.tool_calls)) { if (!Array.isArray(data?.tool_calls)) {
return; return;
@ -2028,6 +2043,8 @@ export function isFunctionCallingSupported() {
chat_completion_sources.COHERE, chat_completion_sources.COHERE,
chat_completion_sources.CUSTOM, chat_completion_sources.CUSTOM,
chat_completion_sources.MISTRALAI, chat_completion_sources.MISTRALAI,
chat_completion_sources.CLAUDE,
chat_completion_sources.OPENROUTER,
]; ];
return supportedSources.includes(oai_settings.chat_completion_source); return supportedSources.includes(oai_settings.chat_completion_source);
} }

View File

@ -115,6 +115,7 @@ async function sendClaudeRequest(request, response) {
request.socket.on('close', function () { request.socket.on('close', function () {
controller.abort(); controller.abort();
}); });
const additionalHeaders = {};
let use_system_prompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt; let use_system_prompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt;
let converted_prompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, use_system_prompt, request.body.human_sysprompt_message, request.body.char_name, request.body.user_name); let converted_prompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, use_system_prompt, request.body.human_sysprompt_message, request.body.char_name, request.body.user_name);
// Add custom stop sequences // Add custom stop sequences
@ -136,6 +137,14 @@ async function sendClaudeRequest(request, response) {
if (use_system_prompt) { if (use_system_prompt) {
requestBody.system = converted_prompt.systemPrompt; requestBody.system = converted_prompt.systemPrompt;
} }
if (Array.isArray(request.body.tools) && request.body.tools.length > 0) {
additionalHeaders['anthropic-beta'] = 'tools-2024-05-16';
requestBody.tool_choice = { type: request.body.tool_choice === 'required' ? 'any' : 'auto' };
requestBody.tools = request.body.tools
.filter(tool => tool.type === 'function')
.map(tool => tool.function)
.map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters }));
}
console.log('Claude request:', requestBody); console.log('Claude request:', requestBody);
const generateResponse = await fetch(apiUrl + '/messages', { const generateResponse = await fetch(apiUrl + '/messages', {
@ -146,6 +155,7 @@ async function sendClaudeRequest(request, response) {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'anthropic-version': '2023-06-01', 'anthropic-version': '2023-06-01',
'x-api-key': apiKey, 'x-api-key': apiKey,
...additionalHeaders,
}, },
timeout: 0, timeout: 0,
}); });
@ -163,8 +173,8 @@ async function sendClaudeRequest(request, response) {
const responseText = generateResponseJson.content[0].text; const responseText = generateResponseJson.content[0].text;
console.log('Claude response:', generateResponseJson); console.log('Claude response:', generateResponseJson);
// Wrap it back to OAI format // Wrap it back to OAI format + save the original content
const reply = { choices: [{ 'message': { 'content': responseText } }] }; const reply = { choices: [{ 'message': { 'content': responseText } }], content: generateResponseJson.content };
return response.send(reply); return response.send(reply);
} }
} catch (error) { } catch (error) {