New tool calling framework
| @@ -1365,44 +1365,3 @@ declare namespace moment { | |||
| 1365 | declare global { | 1365 | declare global { |
| 1366 | const moment: typeof moment; | 1366 | const moment: typeof moment; |
| 1367 | } | 1367 | } |
| 1368 | |||
| 1369 | /** | ||
| 1370 | * Callback data for the `LLM_FUNCTION_TOOL_REGISTER` event type that is triggered when a function tool can be registered. | ||
| 1371 | */ | ||
| 1372 | interface FunctionToolRegister { | ||
| 1373 | /** | ||
| 1374 | * The type of generation that is being used | ||
| 1375 | */ | ||
| 1376 | type?: string; | ||
| 1377 | /** | ||
| 1378 | * Generation data, including messages and sampling parameters | ||
| 1379 | */ | ||
| 1380 | data: Record<string, object>; | ||
| 1381 | /** | ||
| 1382 | * Callback to register an LLM function tool. | ||
| 1383 | */ | ||
| 1384 | registerFunctionTool: typeof registerFunctionTool; | ||
| 1385 | } | ||
| 1386 | |||
| 1387 | /** | ||
| 1388 | * Callback data for the `LLM_FUNCTION_TOOL_REGISTER` event type that is triggered when a function tool is registered. | ||
| 1389 | * @param name Name of the function tool to register | ||
| 1390 | * @param description Description of the function tool | ||
| 1391 | * @param params JSON schema for the parameters of the function tool | ||
| 1392 | * @param required Whether the function tool should be forced to be used | ||
| 1393 | */ | ||
| 1394 | declare function registerFunctionTool(name: string, description: string, params: object, required: boolean): Promise<void>; | ||
| 1395 | |||
| 1396 | /** | ||
| 1397 | * Callback data for the `LLM_FUNCTION_TOOL_CALL` event type that is triggered when a function tool is called. | ||
| 1398 | */ | ||
| 1399 | interface FunctionToolCall { | ||
| 1400 | /** | ||
| 1401 | * Name of the function tool to call | ||
| 1402 | */ | ||
| 1403 | name: string; | ||
| 1404 | /** | ||
| 1405 | * JSON object with the parameters to pass to the function tool | ||
| 1406 | */ | ||
| 1407 | arguments: string; | ||
| 1408 | } | ||
| @@ -246,6 +246,7 @@ import { initInputMarkdown } from './scripts/input-md-formatting.js'; | |||
| 246 | import { AbortReason } from './scripts/util/AbortReason.js'; | 246 | import { AbortReason } from './scripts/util/AbortReason.js'; |
| 247 | import { initSystemPrompts } from './scripts/sysprompt.js'; | 247 | import { initSystemPrompts } from './scripts/sysprompt.js'; |
| 248 | import { registerExtensionSlashCommands as initExtensionSlashCommands } from './scripts/extensions-slashcommands.js'; | 248 | import { registerExtensionSlashCommands as initExtensionSlashCommands } from './scripts/extensions-slashcommands.js'; |
| 249 | import { ToolManager } from './scripts/tool-calling.js'; | ||
| 249 | 250 | ||
| 250 | //exporting functions and vars for mods | 251 | //exporting functions and vars for mods |
| 251 | export { | 252 | export { |
| @@ -463,8 +464,6 @@ export const event_types = { | |||
| 463 | FILE_ATTACHMENT_DELETED: 'file_attachment_deleted', | 464 | FILE_ATTACHMENT_DELETED: 'file_attachment_deleted', |
| 464 | WORLDINFO_FORCE_ACTIVATE: 'worldinfo_force_activate', | 465 | WORLDINFO_FORCE_ACTIVATE: 'worldinfo_force_activate', |
| 465 | OPEN_CHARACTER_LIBRARY: 'open_character_library', | 466 | OPEN_CHARACTER_LIBRARY: 'open_character_library', |
| 466 | LLM_FUNCTION_TOOL_REGISTER: 'llm_function_tool_register', | ||
| 467 | LLM_FUNCTION_TOOL_CALL: 'llm_function_tool_call', | ||
| 468 | ONLINE_STATUS_CHANGED: 'online_status_changed', | 467 | ONLINE_STATUS_CHANGED: 'online_status_changed', |
| 469 | IMAGE_SWIPED: 'image_swiped', | 468 | IMAGE_SWIPED: 'image_swiped', |
| 470 | CONNECTION_PROFILE_LOADED: 'connection_profile_loaded', | 469 | CONNECTION_PROFILE_LOADED: 'connection_profile_loaded', |
| @@ -2921,6 +2920,7 @@ class StreamingProcessor { | |||
| 2921 | this.swipes = []; | 2920 | this.swipes = []; |
| 2922 | /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */ | 2921 | /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */ |
| 2923 | this.messageLogprobs = []; | 2922 | this.messageLogprobs = []; |
| 2923 | this.toolCalls = []; | ||
| 2924 | } | 2924 | } |
| 2925 | 2925 | ||
| 2926 | #checkDomElements(messageId) { | 2926 | #checkDomElements(messageId) { |
| @@ -3139,7 +3139,7 @@ class StreamingProcessor { | |||
| 3139 | } | 3139 | } |
| 3140 | 3140 | ||
| 3141 | /** | 3141 | /** |
| 3142 | * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs }, void, void>} | 3142 | * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[] }, void, void>} |
| 3143 | */ | 3143 | */ |
| 3144 | *nullStreamingGeneration() { | 3144 | *nullStreamingGeneration() { |
| 3145 | throw new Error('Generation function for streaming is not hooked up'); | 3145 | throw new Error('Generation function for streaming is not hooked up'); |
| @@ -3161,12 +3161,13 @@ class StreamingProcessor { | |||
| 3161 | try { | 3161 | try { |
| 3162 | const sw = new Stopwatch(1000 / power_user.streaming_fps); | 3162 | const sw = new Stopwatch(1000 / power_user.streaming_fps); |
| 3163 | const timestamps = []; | 3163 | const timestamps = []; |
| 3164 | for await (const { text, swipes, logprobs } of this.generator()) { | 3164 | for await (const { text, swipes, logprobs, toolCalls } of this.generator()) { |
| 3165 | timestamps.push(Date.now()); | 3165 | timestamps.push(Date.now()); |
| 3166 | if (this.isStopped) { | 3166 | if (this.isStopped) { |
| 3167 | return; | 3167 | return; |
| 3168 | } | 3168 | } |
| 3169 | 3169 | ||
| 3170 | this.toolCalls = toolCalls; | ||
| 3170 | this.result = text; | 3171 | this.result = text; |
| 3171 | this.swipes = Array.from(swipes ?? []); | 3172 | this.swipes = Array.from(swipes ?? []); |
| 3172 | if (logprobs) { | 3173 | if (logprobs) { |
| @@ -4405,6 +4406,20 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 4405 | getMessage = continue_mag + getMessage; | 4406 | getMessage = continue_mag + getMessage; |
| 4406 | } | 4407 | } |
| 4407 | 4408 | ||
| 4409 | if (ToolManager.isFunctionCallingSupported() && Array.isArray(streamingProcessor.toolCalls) && streamingProcessor.toolCalls.length) { | ||
| 4410 | const invocations = await ToolManager.checkFunctionToolCalls(streamingProcessor.toolCalls); | ||
| 4411 | if (invocations.length) { | ||
| 4412 | const lastMessage = chat[chat.length - 1]; | ||
| 4413 | const shouldDeleteMessage = ['', '...'].includes(lastMessage?.mes) && ['', '...'].includes(streamingProcessor.result); | ||
| 4414 | if (shouldDeleteMessage) { | ||
| 4415 | await deleteLastMessage(); | ||
| 4416 | streamingProcessor = null; | ||
| 4417 | } | ||
| 4418 | ToolManager.saveFunctionToolInvocations(invocations); | ||
| 4419 | return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun); | ||
| 4420 | } | ||
| 4421 | } | ||
| 4422 | |||
| 4408 | if (streamingProcessor && !streamingProcessor.isStopped && streamingProcessor.isFinished) { | 4423 | if (streamingProcessor && !streamingProcessor.isStopped && streamingProcessor.isFinished) { |
| 4409 | await streamingProcessor.onFinishStreaming(streamingProcessor.messageId, getMessage); | 4424 | await streamingProcessor.onFinishStreaming(streamingProcessor.messageId, getMessage); |
| 4410 | streamingProcessor = null; | 4425 | streamingProcessor = null; |
| @@ -4440,6 +4455,14 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 4440 | throw new Error(data?.response); | 4455 | throw new Error(data?.response); |
| 4441 | } | 4456 | } |
| 4442 | 4457 | ||
| 4458 | if (ToolManager.isFunctionCallingSupported()) { | ||
| 4459 | const invocations = await ToolManager.checkFunctionToolCalls(data); | ||
| 4460 | if (invocations.length) { | ||
| 4461 | ToolManager.saveFunctionToolInvocations(invocations); | ||
| 4462 | return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun); | ||
| 4463 | } | ||
| 4464 | } | ||
| 4465 | |||
| 4443 | //const getData = await response.json(); | 4466 | //const getData = await response.json(); |
| 4444 | let getMessage = extractMessageFromData(data); | 4467 | let getMessage = extractMessageFromData(data); |
| 4445 | let title = extractTitleFromData(data); | 4468 | let title = extractTitleFromData(data); |
| @@ -7853,7 +7876,7 @@ function openAlternateGreetings() { | |||
| 7853 | if (menu_type !== 'create') { | 7876 | if (menu_type !== 'create') { |
| 7854 | await createOrEditCharacter(); | 7877 | await createOrEditCharacter(); |
| 7855 | } | 7878 | } |
| 7856 | } | 7879 | }, |
| 7857 | }); | 7880 | }); |
| 7858 | 7881 | ||
| 7859 | for (let index = 0; index < getArray().length; index++) { | 7882 | for (let index = 0; index < getArray().length; index++) { |
| @@ -8130,6 +8153,8 @@ window['SillyTavern'].getContext = function () { | |||
| 8130 | registerHelper: () => { }, | 8153 | registerHelper: () => { }, |
| 8131 | registerMacro: MacrosParser.registerMacro.bind(MacrosParser), | 8154 | registerMacro: MacrosParser.registerMacro.bind(MacrosParser), |
| 8132 | unregisterMacro: MacrosParser.unregisterMacro.bind(MacrosParser), | 8155 | unregisterMacro: MacrosParser.unregisterMacro.bind(MacrosParser), |
| 8156 | registerFunctionTool: ToolManager.registerFunctionTool.bind(ToolManager), | ||
| 8157 | unregisterFunctionTool: ToolManager.unregisterFunctionTool.bind(ToolManager), | ||
| 8133 | registerDebugFunction: registerDebugFunction, | 8158 | registerDebugFunction: registerDebugFunction, |
| 8134 | /** @deprecated Use renderExtensionTemplateAsync instead. */ | 8159 | /** @deprecated Use renderExtensionTemplateAsync instead. */ |
| 8135 | renderExtensionTemplate: renderExtensionTemplate, | 8160 | renderExtensionTemplate: renderExtensionTemplate, |
| @@ -9,7 +9,6 @@ import { debounce_timeout } from '../../constants.js'; | |||
| 9 | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; | 9 | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; |
| 10 | import { SlashCommand } from '../../slash-commands/SlashCommand.js'; | 10 | import { SlashCommand } from '../../slash-commands/SlashCommand.js'; |
| 11 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; | 11 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; |
| 12 | import { isFunctionCallingSupported } from '../../openai.js'; | ||
| 13 | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; | 12 | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; |
| 14 | import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; | 13 | import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 15 | import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js'; | 14 | import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js'; |
| @@ -21,7 +20,6 @@ const UPDATE_INTERVAL = 2000; | |||
| 21 | const STREAMING_UPDATE_INTERVAL = 10000; | 20 | const STREAMING_UPDATE_INTERVAL = 10000; |
| 22 | const TALKINGCHECK_UPDATE_INTERVAL = 500; | 21 | const TALKINGCHECK_UPDATE_INTERVAL = 500; |
| 23 | const DEFAULT_FALLBACK_EXPRESSION = 'joy'; | 22 | const DEFAULT_FALLBACK_EXPRESSION = 'joy'; |
| 24 | const FUNCTION_NAME = 'set_emotion'; | ||
| 25 | const DEFAULT_LLM_PROMPT = 'Ignore previous instructions. Classify the emotion of the last message. Output just one word, e.g. "joy" or "anger". Choose only one of the following labels: {{labels}}'; | 23 | const DEFAULT_LLM_PROMPT = 'Ignore previous instructions. Classify the emotion of the last message. Output just one word, e.g. "joy" or "anger". Choose only one of the following labels: {{labels}}'; |
| 26 | const DEFAULT_EXPRESSIONS = [ | 24 | const DEFAULT_EXPRESSIONS = [ |
| 27 | 'talkinghead', | 25 | 'talkinghead', |
| @@ -1017,10 +1015,6 @@ async function getLlmPrompt(labels) { | |||
| 1017 | return ''; | 1015 | return ''; |
| 1018 | } | 1016 | } |
| 1019 | 1017 | ||
| 1020 | if (isFunctionCallingSupported()) { | ||
| 1021 | return ''; | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | const labelsString = labels.map(x => `"${x}"`).join(', '); | 1018 | const labelsString = labels.map(x => `"${x}"`).join(', '); |
| 1025 | const prompt = substituteParamsExtended(String(extension_settings.expressions.llmPrompt), { labels: labelsString }); | 1019 | const prompt = substituteParamsExtended(String(extension_settings.expressions.llmPrompt), { labels: labelsString }); |
| 1026 | return prompt; | 1020 | return prompt; |
| @@ -1056,41 +1050,6 @@ function parseLlmResponse(emotionResponse, labels) { | |||
| 1056 | throw new Error('Could not parse emotion response ' + emotionResponse); | 1050 | throw new Error('Could not parse emotion response ' + emotionResponse); |
| 1057 | } | 1051 | } |
| 1058 | 1052 | ||
| 1059 | /** | ||
| 1060 | * Registers the function tool for the LLM API. | ||
| 1061 | * @param {FunctionToolRegister} args Function tool register arguments. | ||
| 1062 | */ | ||
| 1063 | function onFunctionToolRegister(args) { | ||
| 1064 | if (inApiCall && extension_settings.expressions.api === EXPRESSION_API.llm && isFunctionCallingSupported()) { | ||
| 1065 | // Only trigger on quiet mode | ||
| 1066 | if (args.type !== 'quiet') { | ||
| 1067 | return; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | const emotions = DEFAULT_EXPRESSIONS.filter((e) => e != 'talkinghead'); | ||
| 1071 | const jsonSchema = { | ||
| 1072 | $schema: 'http://json-schema.org/draft-04/schema#', | ||
| 1073 | type: 'object', | ||
| 1074 | properties: { | ||
| 1075 | emotion: { | ||
| 1076 | type: 'string', | ||
| 1077 | enum: emotions, | ||
| 1078 | description: `One of the following: ${JSON.stringify(emotions)}`, | ||
| 1079 | }, | ||
| 1080 | }, | ||
| 1081 | required: [ | ||
| 1082 | 'emotion', | ||
| 1083 | ], | ||
| 1084 | }; | ||
| 1085 | args.registerFunctionTool( | ||
| 1086 | FUNCTION_NAME, | ||
| 1087 | substituteParams('Sets the label that best describes the current emotional state of {{char}}. Only select one of the enumerated values.'), | ||
| 1088 | jsonSchema, | ||
| 1089 | true, | ||
| 1090 | ); | ||
| 1091 | } | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | function onTextGenSettingsReady(args) { | 1053 | function onTextGenSettingsReady(args) { |
| 1095 | // Only call if inside an API call | 1054 | // Only call if inside an API call |
| 1096 | if (inApiCall && extension_settings.expressions.api === EXPRESSION_API.llm && isJsonSchemaSupported()) { | 1055 | if (inApiCall && extension_settings.expressions.api === EXPRESSION_API.llm && isJsonSchemaSupported()) { |
| @@ -1164,18 +1123,9 @@ export async function getExpressionLabel(text, expressionsApi = extension_settin | |||
| 1164 | 1123 | ||
| 1165 | const expressionsList = await getExpressionsList(); | 1124 | const expressionsList = await getExpressionsList(); |
| 1166 | const prompt = substituteParamsExtended(customPrompt, { labels: expressionsList }) || await getLlmPrompt(expressionsList); | 1125 | const prompt = substituteParamsExtended(customPrompt, { labels: expressionsList }) || await getLlmPrompt(expressionsList); |
| 1167 | let functionResult = null; | ||
| 1168 | eventSource.once(event_types.TEXT_COMPLETION_SETTINGS_READY, onTextGenSettingsReady); | 1126 | eventSource.once(event_types.TEXT_COMPLETION_SETTINGS_READY, onTextGenSettingsReady); |
| 1169 | eventSource.once(event_types.LLM_FUNCTION_TOOL_REGISTER, onFunctionToolRegister); | ||
| 1170 | eventSource.once(event_types.LLM_FUNCTION_TOOL_CALL, (/** @type {FunctionToolCall} */ args) => { | ||
| 1171 | if (args.name !== FUNCTION_NAME) { | ||
| 1172 | return; | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | functionResult = args?.arguments; | ||
| 1176 | }); | ||
| 1177 | const emotionResponse = await generateRaw(text, main_api, false, false, prompt); | 1127 | const emotionResponse = await generateRaw(text, main_api, false, false, prompt); |
| 1178 | return parseLlmResponse(functionResult || emotionResponse, expressionsList); | 1128 | return parseLlmResponse(emotionResponse, expressionsList); |
| 1179 | } | 1129 | } |
| 1180 | // Extras | 1130 | // Extras |
| 1181 | default: { | 1131 | default: { |
| @@ -70,6 +70,7 @@ import { renderTemplateAsync } from './templates.js'; | |||
| 70 | import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; | 70 | import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; |
| 71 | import { Popup, POPUP_RESULT } from './popup.js'; | 71 | import { Popup, POPUP_RESULT } from './popup.js'; |
| 72 | import { t } from './i18n.js'; | 72 | import { t } from './i18n.js'; |
| 73 | import { ToolManager } from './tool-calling.js'; | ||
| 73 | 74 | ||
| 74 | export { | 75 | export { |
| 75 | openai_messages_count, | 76 | openai_messages_count, |
| @@ -1863,8 +1864,8 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 1863 | generate_data['seed'] = oai_settings.seed; | 1864 | generate_data['seed'] = oai_settings.seed; |
| 1864 | } | 1865 | } |
| 1865 | 1866 | ||
| 1866 | if (isFunctionCallingSupported() && !stream) { | 1867 | if (!canMultiSwipe && ToolManager.isFunctionCallingSupported()) { |
| 1867 | await registerFunctionTools(type, generate_data); | 1868 | await ToolManager.registerFunctionToolsOpenAI(generate_data); |
| 1868 | } | 1869 | } |
| 1869 | 1870 | ||
| 1870 | if (isOAI && oai_settings.openai_model.startsWith('o1-')) { | 1871 | if (isOAI && oai_settings.openai_model.startsWith('o1-')) { |
| @@ -1911,6 +1912,7 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 1911 | return async function* streamData() { | 1912 | return async function* streamData() { |
| 1912 | let text = ''; | 1913 | let text = ''; |
| 1913 | const swipes = []; | 1914 | const swipes = []; |
| 1915 | const toolCalls = []; | ||
| 1914 | while (true) { | 1916 | while (true) { |
| 1915 | const { done, value } = await reader.read(); | 1917 | const { done, value } = await reader.read(); |
| 1916 | if (done) return; | 1918 | if (done) return; |
| @@ -1926,7 +1928,9 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 1926 | text += getStreamingReply(parsed); | 1928 | text += getStreamingReply(parsed); |
| 1927 | } | 1929 | } |
| 1928 | 1930 | ||
| 1929 | yield { text, swipes: swipes, logprobs: parseChatCompletionLogprobs(parsed) }; | 1931 | ToolManager.parseToolCalls(toolCalls, parsed); |
| 1932 | |||
| 1933 | yield { text, swipes: swipes, logprobs: parseChatCompletionLogprobs(parsed), toolCalls: toolCalls }; | ||
| 1930 | } | 1934 | } |
| 1931 | }; | 1935 | }; |
| 1932 | } | 1936 | } |
| @@ -1948,147 +1952,10 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 1948 | delay(1).then(() => saveLogprobsForActiveMessage(logprobs, null)); | 1952 | delay(1).then(() => saveLogprobsForActiveMessage(logprobs, null)); |
| 1949 | } | 1953 | } |
| 1950 | 1954 | ||
| 1951 | if (isFunctionCallingSupported()) { | ||
| 1952 | await checkFunctionToolCalls(data); | ||
| 1953 | } | ||
| 1954 | |||
| 1955 | return data; | 1955 | return data; |
| 1956 | } | 1956 | } |
| 1957 | } | 1957 | } |
| 1958 | 1958 | ||
| 1959 | /** | ||
| 1960 | * Register function tools for the next chat completion request. | ||
| 1961 | * @param {string} type Generation type | ||
| 1962 | * @param {object} data Generation data | ||
| 1963 | */ | ||
| 1964 | async function registerFunctionTools(type, data) { | ||
| 1965 | let toolChoice = 'auto'; | ||
| 1966 | const tools = []; | ||
| 1967 | |||
| 1968 | /** | ||
| 1969 | * @type {registerFunctionTool} | ||
| 1970 | */ | ||
| 1971 | const registerFunctionTool = (name, description, parameters, required) => { | ||
| 1972 | tools.push({ | ||
| 1973 | type: 'function', | ||
| 1974 | function: { | ||
| 1975 | name, | ||
| 1976 | description, | ||
| 1977 | parameters, | ||
| 1978 | }, | ||
| 1979 | }); | ||
| 1980 | |||
| 1981 | if (required) { | ||
| 1982 | toolChoice = 'required'; | ||
| 1983 | } | ||
| 1984 | }; | ||
| 1985 | |||
| 1986 | /** | ||
| 1987 | * @type {FunctionToolRegister} | ||
| 1988 | */ | ||
| 1989 | const args = { | ||
| 1990 | type, | ||
| 1991 | data, | ||
| 1992 | registerFunctionTool, | ||
| 1993 | }; | ||
| 1994 | |||
| 1995 | await eventSource.emit(event_types.LLM_FUNCTION_TOOL_REGISTER, args); | ||
| 1996 | |||
| 1997 | if (tools.length) { | ||
| 1998 | console.log('Registered function tools:', tools); | ||
| 1999 | |||
| 2000 | data['tools'] = tools; | ||
| 2001 | data['tool_choice'] = toolChoice; | ||
| 2002 | } | ||
| 2003 | } | ||
| 2004 | |||
| 2005 | async function checkFunctionToolCalls(data) { | ||
| 2006 | const oaiCompat = [ | ||
| 2007 | chat_completion_sources.OPENAI, | ||
| 2008 | chat_completion_sources.CUSTOM, | ||
| 2009 | chat_completion_sources.MISTRALAI, | ||
| 2010 | chat_completion_sources.OPENROUTER, | ||
| 2011 | chat_completion_sources.GROQ, | ||
| 2012 | ]; | ||
| 2013 | if (oaiCompat.includes(oai_settings.chat_completion_source)) { | ||
| 2014 | if (!Array.isArray(data?.choices)) { | ||
| 2015 | return; | ||
| 2016 | } | ||
| 2017 | |||
| 2018 | // Find a choice with 0-index | ||
| 2019 | const choice = data.choices.find(choice => choice.index === 0); | ||
| 2020 | |||
| 2021 | if (!choice) { | ||
| 2022 | return; | ||
| 2023 | } | ||
| 2024 | |||
| 2025 | const toolCalls = choice.message.tool_calls; | ||
| 2026 | |||
| 2027 | if (!Array.isArray(toolCalls)) { | ||
| 2028 | return; | ||
| 2029 | } | ||
| 2030 | |||
| 2031 | for (const toolCall of toolCalls) { | ||
| 2032 | if (typeof toolCall.function !== 'object') { | ||
| 2033 | continue; | ||
| 2034 | } | ||
| 2035 | |||
| 2036 | /** @type {FunctionToolCall} */ | ||
| 2037 | const args = toolCall.function; | ||
| 2038 | console.log('Function tool call:', toolCall); | ||
| 2039 | await eventSource.emit(event_types.LLM_FUNCTION_TOOL_CALL, args); | ||
| 2040 | } | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | if ([chat_completion_sources.CLAUDE].includes(oai_settings.chat_completion_source)) { | ||
| 2044 | if (!Array.isArray(data?.content)) { | ||
| 2045 | return; | ||
| 2046 | } | ||
| 2047 | |||
| 2048 | for (const content of data.content) { | ||
| 2049 | if (content.type === 'tool_use') { | ||
| 2050 | /** @type {FunctionToolCall} */ | ||
| 2051 | const args = { name: content.name, arguments: JSON.stringify(content.input) }; | ||
| 2052 | await eventSource.emit(event_types.LLM_FUNCTION_TOOL_CALL, args); | ||
| 2053 | } | ||
| 2054 | } | ||
| 2055 | } | ||
| 2056 | |||
| 2057 | if ([chat_completion_sources.COHERE].includes(oai_settings.chat_completion_source)) { | ||
| 2058 | if (!Array.isArray(data?.tool_calls)) { | ||
| 2059 | return; | ||
| 2060 | } | ||
| 2061 | |||
| 2062 | for (const toolCall of data.tool_calls) { | ||
| 2063 | /** @type {FunctionToolCall} */ | ||
| 2064 | const args = { name: toolCall.name, arguments: JSON.stringify(toolCall.parameters) }; | ||
| 2065 | console.log('Function tool call:', toolCall); | ||
| 2066 | await eventSource.emit(event_types.LLM_FUNCTION_TOOL_CALL, args); | ||
| 2067 | } | ||
| 2068 | } | ||
| 2069 | } | ||
| 2070 | |||
| 2071 | export function isFunctionCallingSupported() { | ||
| 2072 | if (main_api !== 'openai') { | ||
| 2073 | return false; | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | if (!oai_settings.function_calling) { | ||
| 2077 | return false; | ||
| 2078 | } | ||
| 2079 | |||
| 2080 | const supportedSources = [ | ||
| 2081 | chat_completion_sources.OPENAI, | ||
| 2082 | chat_completion_sources.COHERE, | ||
| 2083 | chat_completion_sources.CUSTOM, | ||
| 2084 | chat_completion_sources.MISTRALAI, | ||
| 2085 | chat_completion_sources.CLAUDE, | ||
| 2086 | chat_completion_sources.OPENROUTER, | ||
| 2087 | chat_completion_sources.GROQ, | ||
| 2088 | ]; | ||
| 2089 | return supportedSources.includes(oai_settings.chat_completion_source); | ||
| 2090 | } | ||
| 2091 | |||
| 2092 | function getStreamingReply(data) { | 1959 | function getStreamingReply(data) { |
| 2093 | if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) { | 1960 | if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) { |
| 2094 | return data?.delta?.text || ''; | 1961 | return data?.delta?.text || ''; |
| @@ -4019,7 +3886,7 @@ async function onModelChange() { | |||
| 4019 | $('#openai_max_context').attr('max', max_32k); | 3886 | $('#openai_max_context').attr('max', max_32k); |
| 4020 | } else if (value === 'text-bison-001') { | 3887 | } else if (value === 'text-bison-001') { |
| 4021 | $('#openai_max_context').attr('max', max_8k); | 3888 | $('#openai_max_context').attr('max', max_8k); |
| 4022 | // The ultra endpoints are possibly dead: | 3889 | // The ultra endpoints are possibly dead: |
| 4023 | } else if (value.includes('gemini-1.0-ultra') || value === 'gemini-ultra') { | 3890 | } else if (value.includes('gemini-1.0-ultra') || value === 'gemini-ultra') { |
| 4024 | $('#openai_max_context').attr('max', max_32k); | 3891 | $('#openai_max_context').attr('max', max_32k); |
| 4025 | } else { | 3892 | } else { |
| @@ -0,0 +1,381 @@ | |||
| 1 | import { chat, main_api } from '../script.js'; | ||
| 2 | import { chat_completion_sources, oai_settings } from './openai.js'; | ||
| 3 | |||
| 4 | /** | ||
| 5 | * @typedef {object} ToolInvocation | ||
| 6 | * @property {string} id - A unique identifier for the tool invocation. | ||
| 7 | * @property {string} name - The name of the tool. | ||
| 8 | * @property {string} parameters - The parameters for the tool invocation. | ||
| 9 | * @property {string} result - The result of the tool invocation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | /** | ||
| 13 | * A class that represents a tool definition. | ||
| 14 | */ | ||
| 15 | class ToolDefinition { | ||
| 16 | /** | ||
| 17 | * A unique name for the tool. | ||
| 18 | * @type {string} | ||
| 19 | */ | ||
| 20 | #name; | ||
| 21 | |||
| 22 | /** | ||
| 23 | * A description of what the tool does. | ||
| 24 | * @type {string} | ||
| 25 | */ | ||
| 26 | #description; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * A JSON schema for the parameters that the tool accepts. | ||
| 30 | * @type {object} | ||
| 31 | */ | ||
| 32 | #parameters; | ||
| 33 | |||
| 34 | /** | ||
| 35 | * A function that will be called when the tool is executed. | ||
| 36 | * @type {function} | ||
| 37 | */ | ||
| 38 | #action; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Creates a new ToolDefinition. | ||
| 42 | * @param {string} name A unique name for the tool. | ||
| 43 | * @param {string} description A description of what the tool does. | ||
| 44 | * @param {object} parameters A JSON schema for the parameters that the tool accepts. | ||
| 45 | * @param {function} action A function that will be called when the tool is executed. | ||
| 46 | */ | ||
| 47 | constructor(name, description, parameters, action) { | ||
| 48 | this.#name = name; | ||
| 49 | this.#description = description; | ||
| 50 | this.#parameters = parameters; | ||
| 51 | this.#action = action; | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Converts the ToolDefinition to an OpenAI API representation | ||
| 56 | * @returns {object} OpenAI API representation of the tool. | ||
| 57 | */ | ||
| 58 | toFunctionOpenAI() { | ||
| 59 | return { | ||
| 60 | type: 'function', | ||
| 61 | function: { | ||
| 62 | name: this.#name, | ||
| 63 | description: this.#description, | ||
| 64 | parameters: this.#parameters, | ||
| 65 | }, | ||
| 66 | }; | ||
| 67 | } | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Invokes the tool with the given parameters. | ||
| 71 | * @param {object} parameters The parameters to pass to the tool. | ||
| 72 | * @returns {Promise<any>} The result of the tool's action function. | ||
| 73 | */ | ||
| 74 | async invoke(parameters) { | ||
| 75 | return await this.#action(parameters); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | /** | ||
| 80 | * A class that manages the registration and invocation of tools. | ||
| 81 | */ | ||
| 82 | export class ToolManager { | ||
| 83 | /** | ||
| 84 | * A map of tool names to tool definitions. | ||
| 85 | * @type {Map<string, ToolDefinition>} | ||
| 86 | */ | ||
| 87 | static #tools = new Map(); | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Returns an Array of all tools that have been registered. | ||
| 91 | * @type {ToolDefinition[]} | ||
| 92 | */ | ||
| 93 | static get tools() { | ||
| 94 | return Array.from(this.#tools.values()); | ||
| 95 | } | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Registers a new tool with the tool registry. | ||
| 99 | * @param {string} name The name of the tool. | ||
| 100 | * @param {string} description A description of what the tool does. | ||
| 101 | * @param {object} parameters A JSON schema for the parameters that the tool accepts. | ||
| 102 | * @param {function} action A function that will be called when the tool is executed. | ||
| 103 | */ | ||
| 104 | static registerFunctionTool(name, description, parameters, action) { | ||
| 105 | if (this.#tools.has(name)) { | ||
| 106 | console.warn(`A tool with the name "${name}" has already been registered. The definition will be overwritten.`); | ||
| 107 | } | ||
| 108 | |||
| 109 | const definition = new ToolDefinition(name, description, parameters, action); | ||
| 110 | this.#tools.set(name, definition); | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Removes a tool from the tool registry. | ||
| 115 | * @param {string} name The name of the tool to unregister. | ||
| 116 | */ | ||
| 117 | static unregisterFunctionTool(name) { | ||
| 118 | if (!this.#tools.has(name)) { | ||
| 119 | console.warn(`No tool with the name "${name}" has been registered.`); | ||
| 120 | return; | ||
| 121 | } | ||
| 122 | |||
| 123 | this.#tools.delete(name); | ||
| 124 | } | ||
| 125 | |||
| 126 | /** | ||
| 127 | * Invokes a tool by name. Returns the result of the tool's action function. | ||
| 128 | * @param {string} name The name of the tool to invoke. | ||
| 129 | * @param {object} parameters Function parameters. For example, if the tool requires a "name" parameter, you would pass {name: "value"}. | ||
| 130 | * @returns {Promise<string|null>} The result of the tool's action function. If an error occurs, null is returned. Non-string results are JSON-stringified. | ||
| 131 | */ | ||
| 132 | static async invokeFunctionTool(name, parameters) { | ||
| 133 | try { | ||
| 134 | if (!this.#tools.has(name)) { | ||
| 135 | throw new Error(`No tool with the name "${name}" has been registered.`); | ||
| 136 | } | ||
| 137 | |||
| 138 | const invokeParameters = typeof parameters === 'string' ? JSON.parse(parameters) : parameters; | ||
| 139 | const tool = this.#tools.get(name); | ||
| 140 | const result = await tool.invoke(invokeParameters); | ||
| 141 | return typeof result === 'string' ? result : JSON.stringify(result); | ||
| 142 | } catch (error) { | ||
| 143 | console.error(`An error occurred while invoking the tool "${name}":`, error); | ||
| 144 | return null; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | /** | ||
| 149 | * Register function tools for the next chat completion request. | ||
| 150 | * @param {object} data Generation data | ||
| 151 | */ | ||
| 152 | static async registerFunctionToolsOpenAI(data) { | ||
| 153 | const tools = []; | ||
| 154 | |||
| 155 | for (const tool of ToolManager.tools) { | ||
| 156 | tools.push(tool.toFunctionOpenAI()); | ||
| 157 | } | ||
| 158 | |||
| 159 | if (tools.length) { | ||
| 160 | console.log('Registered function tools:', tools); | ||
| 161 | |||
| 162 | data['tools'] = tools; | ||
| 163 | data['tool_choice'] = 'auto'; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | /** | ||
| 168 | * Utility function to parse tool calls from a parsed response. | ||
| 169 | * @param {any[]} toolCalls The tool calls to update. | ||
| 170 | * @param {any} parsed The parsed response from the OpenAI API. | ||
| 171 | * @returns {void} | ||
| 172 | */ | ||
| 173 | static parseToolCalls(toolCalls, parsed) { | ||
| 174 | if (!Array.isArray(parsed?.choices)) { | ||
| 175 | return; | ||
| 176 | } | ||
| 177 | for (const choice of parsed.choices) { | ||
| 178 | const choiceIndex = (typeof choice.index === 'number') ? choice.index : null; | ||
| 179 | const choiceDelta = choice.delta; | ||
| 180 | |||
| 181 | if (choiceIndex === null || !choiceDelta) { | ||
| 182 | continue; | ||
| 183 | } | ||
| 184 | |||
| 185 | const toolCallDeltas = choiceDelta?.tool_calls; | ||
| 186 | |||
| 187 | if (!Array.isArray(toolCallDeltas)) { | ||
| 188 | continue; | ||
| 189 | } | ||
| 190 | |||
| 191 | if (!Array.isArray(toolCalls[choiceIndex])) { | ||
| 192 | toolCalls[choiceIndex] = []; | ||
| 193 | } | ||
| 194 | |||
| 195 | for (const toolCallDelta of toolCallDeltas) { | ||
| 196 | const toolCallIndex = (typeof toolCallDelta?.index === 'number') ? toolCallDelta.index : null; | ||
| 197 | |||
| 198 | if (toolCallIndex === null) { | ||
| 199 | continue; | ||
| 200 | } | ||
| 201 | |||
| 202 | if (toolCalls[choiceIndex][toolCallIndex] === undefined) { | ||
| 203 | toolCalls[choiceIndex][toolCallIndex] = {}; | ||
| 204 | } | ||
| 205 | |||
| 206 | const targetToolCall = toolCalls[choiceIndex][toolCallIndex]; | ||
| 207 | |||
| 208 | ToolManager.#applyToolCallDelta(targetToolCall, toolCallDelta); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | static #applyToolCallDelta(target, delta) { | ||
| 214 | for (const key in delta) { | ||
| 215 | if (!delta.hasOwnProperty(key)) continue; | ||
| 216 | |||
| 217 | const deltaValue = delta[key]; | ||
| 218 | const targetValue = target[key]; | ||
| 219 | |||
| 220 | if (deltaValue === null || deltaValue === undefined) { | ||
| 221 | target[key] = deltaValue; | ||
| 222 | continue; | ||
| 223 | } | ||
| 224 | |||
| 225 | if (typeof deltaValue === 'string') { | ||
| 226 | if (typeof targetValue === 'string') { | ||
| 227 | // Concatenate strings | ||
| 228 | target[key] = targetValue + deltaValue; | ||
| 229 | } else { | ||
| 230 | target[key] = deltaValue; | ||
| 231 | } | ||
| 232 | } else if (typeof deltaValue === 'object' && !Array.isArray(deltaValue)) { | ||
| 233 | if (typeof targetValue !== 'object' || targetValue === null || Array.isArray(targetValue)) { | ||
| 234 | target[key] = {}; | ||
| 235 | } | ||
| 236 | // Recursively apply deltas to nested objects | ||
| 237 | ToolManager.#applyToolCallDelta(target[key], deltaValue); | ||
| 238 | } else { | ||
| 239 | // Assign other types directly | ||
| 240 | target[key] = deltaValue; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | static isFunctionCallingSupported() { | ||
| 246 | if (main_api !== 'openai') { | ||
| 247 | return false; | ||
| 248 | } | ||
| 249 | |||
| 250 | if (!oai_settings.function_calling) { | ||
| 251 | return false; | ||
| 252 | } | ||
| 253 | |||
| 254 | const supportedSources = [ | ||
| 255 | chat_completion_sources.OPENAI, | ||
| 256 | //chat_completion_sources.COHERE, | ||
| 257 | chat_completion_sources.CUSTOM, | ||
| 258 | chat_completion_sources.MISTRALAI, | ||
| 259 | //chat_completion_sources.CLAUDE, | ||
| 260 | chat_completion_sources.OPENROUTER, | ||
| 261 | chat_completion_sources.GROQ, | ||
| 262 | ]; | ||
| 263 | return supportedSources.includes(oai_settings.chat_completion_source); | ||
| 264 | } | ||
| 265 | |||
| 266 | static #getToolCallsFromData(data) { | ||
| 267 | // Parsed tool calls from streaming data | ||
| 268 | if (Array.isArray(data) && data.length > 0) { | ||
| 269 | return data[0]; | ||
| 270 | } | ||
| 271 | |||
| 272 | // Parsed tool calls from non-streaming data | ||
| 273 | if (!Array.isArray(data?.choices)) { | ||
| 274 | return; | ||
| 275 | } | ||
| 276 | |||
| 277 | // Find a choice with 0-index | ||
| 278 | const choice = data.choices.find(choice => choice.index === 0); | ||
| 279 | |||
| 280 | if (!choice) { | ||
| 281 | return; | ||
| 282 | } | ||
| 283 | |||
| 284 | return choice.message.tool_calls; | ||
| 285 | } | ||
| 286 | |||
| 287 | /** | ||
| 288 | * Check for function tool calls in the response data and invoke them. | ||
| 289 | * @param {any} data Reply data | ||
| 290 | * @returns {Promise<ToolInvocation[]>} Successful tool invocations | ||
| 291 | */ | ||
| 292 | static async checkFunctionToolCalls(data) { | ||
| 293 | if (!ToolManager.isFunctionCallingSupported()) { | ||
| 294 | return []; | ||
| 295 | } | ||
| 296 | |||
| 297 | /** @type {ToolInvocation[]} */ | ||
| 298 | const invocations = []; | ||
| 299 | const toolCalls = ToolManager.#getToolCallsFromData(data); | ||
| 300 | const oaiCompat = [ | ||
| 301 | chat_completion_sources.OPENAI, | ||
| 302 | chat_completion_sources.CUSTOM, | ||
| 303 | chat_completion_sources.MISTRALAI, | ||
| 304 | chat_completion_sources.OPENROUTER, | ||
| 305 | chat_completion_sources.GROQ, | ||
| 306 | ]; | ||
| 307 | |||
| 308 | if (oaiCompat.includes(oai_settings.chat_completion_source)) { | ||
| 309 | if (!Array.isArray(toolCalls)) { | ||
| 310 | return; | ||
| 311 | } | ||
| 312 | |||
| 313 | for (const toolCall of toolCalls) { | ||
| 314 | if (typeof toolCall.function !== 'object') { | ||
| 315 | continue; | ||
| 316 | } | ||
| 317 | |||
| 318 | console.log('Function tool call:', toolCall); | ||
| 319 | const id = toolCall.id; | ||
| 320 | const parameters = toolCall.function.arguments; | ||
| 321 | const name = toolCall.function.name; | ||
| 322 | |||
| 323 | toastr.info('Invoking function tool: ' + name); | ||
| 324 | const result = await ToolManager.invokeFunctionTool(name, parameters); | ||
| 325 | toastr.info('Function tool result: ' + result); | ||
| 326 | |||
| 327 | // Save a successful invocation | ||
| 328 | if (result) { | ||
| 329 | invocations.push({ id, name, result, parameters }); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | /* | ||
| 335 | if ([chat_completion_sources.CLAUDE].includes(oai_settings.chat_completion_source)) { | ||
| 336 | if (!Array.isArray(data?.content)) { | ||
| 337 | return; | ||
| 338 | } | ||
| 339 | |||
| 340 | for (const content of data.content) { | ||
| 341 | if (content.type === 'tool_use') { | ||
| 342 | const args = { name: content.name, arguments: JSON.stringify(content.input) }; | ||
| 343 | } | ||
| 344 | } | ||
| 345 | } | ||
| 346 | */ | ||
| 347 | |||
| 348 | /* | ||
| 349 | if ([chat_completion_sources.COHERE].includes(oai_settings.chat_completion_source)) { | ||
| 350 | if (!Array.isArray(data?.tool_calls)) { | ||
| 351 | return; | ||
| 352 | } | ||
| 353 | |||
| 354 | for (const toolCall of data.tool_calls) { | ||
| 355 | const args = { name: toolCall.name, arguments: JSON.stringify(toolCall.parameters) }; | ||
| 356 | console.log('Function tool call:', toolCall); | ||
| 357 | } | ||
| 358 | } | ||
| 359 | */ | ||
| 360 | |||
| 361 | return invocations; | ||
| 362 | } | ||
| 363 | |||
| 364 | /** | ||
| 365 | * Saves function tool invocations to the last user chat message extra metadata. | ||
| 366 | * @param {ToolInvocation[]} invocations | ||
| 367 | */ | ||
| 368 | static saveFunctionToolInvocations(invocations) { | ||
| 369 | for (let index = chat.length - 1; index >= 0; index--) { | ||
| 370 | const message = chat[index]; | ||
| 371 | if (message.is_user) { | ||
| 372 | if (!message.extra || typeof message.extra !== 'object') { | ||
| 373 | message.extra = {}; | ||
| 374 | } | ||
| 375 | message.extra.tool_invocations = invocations; | ||
| 376 | debugger; | ||
| 377 | break; | ||
| 378 | } | ||
| 379 | } | ||
| 380 | } | ||
| 381 | } | ||
| @@ -121,18 +121,20 @@ async function sendClaudeRequest(request, response) { | |||
| 121 | ? [{ type: 'text', text: convertedPrompt.systemPrompt, cache_control: { type: 'ephemeral' } }] | 121 | ? [{ type: 'text', text: convertedPrompt.systemPrompt, cache_control: { type: 'ephemeral' } }] |
| 122 | : convertedPrompt.systemPrompt; | 122 | : convertedPrompt.systemPrompt; |
| 123 | } | 123 | } |
| 124 | /* | ||
| 124 | if (Array.isArray(request.body.tools) && request.body.tools.length > 0) { | 125 | if (Array.isArray(request.body.tools) && request.body.tools.length > 0) { |
| 125 | // Claude doesn't do prefills on function calls, and doesn't allow empty messages | 126 | // Claude doesn't do prefills on function calls, and doesn't allow empty messages |
| 126 | if (convertedPrompt.messages.length && convertedPrompt.messages[convertedPrompt.messages.length - 1].role === 'assistant') { | 127 | if (convertedPrompt.messages.length && convertedPrompt.messages[convertedPrompt.messages.length - 1].role === 'assistant') { |
| 127 | convertedPrompt.messages.push({ role: 'user', content: '.' }); | 128 | convertedPrompt.messages.push({ role: 'user', content: '.' }); |
| 128 | } | 129 | } |
| 129 | additionalHeaders['anthropic-beta'] = 'tools-2024-05-16'; | 130 | additionalHeaders['anthropic-beta'] = 'tools-2024-05-16'; |
| 130 | requestBody.tool_choice = { type: request.body.tool_choice === 'required' ? 'any' : 'auto' }; | 131 | requestBody.tool_choice = { type: request.body.tool_choice }; |
| 131 | requestBody.tools = request.body.tools | 132 | requestBody.tools = request.body.tools |
| 132 | .filter(tool => tool.type === 'function') | 133 | .filter(tool => tool.type === 'function') |
| 133 | .map(tool => tool.function) | 134 | .map(tool => tool.function) |
| 134 | .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters })); | 135 | .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters })); |
| 135 | } | 136 | } |
| 137 | */ | ||
| 136 | if (enableSystemPromptCache) { | 138 | if (enableSystemPromptCache) { |
| 137 | additionalHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31'; | 139 | additionalHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31'; |
| 138 | } | 140 | } |
| @@ -479,7 +481,7 @@ async function sendMistralAIRequest(request, response) { | |||
| 479 | 481 | ||
| 480 | if (Array.isArray(request.body.tools) && request.body.tools.length > 0) { | 482 | if (Array.isArray(request.body.tools) && request.body.tools.length > 0) { |
| 481 | requestBody['tools'] = request.body.tools; | 483 | requestBody['tools'] = request.body.tools; |
| 482 | requestBody['tool_choice'] = request.body.tool_choice === 'required' ? 'any' : 'auto'; | 484 | requestBody['tool_choice'] = request.body.tool_choice; |
| 483 | } | 485 | } |
| 484 | 486 | ||
| 485 | const config = { | 487 | const config = { |
| @@ -549,11 +551,13 @@ async function sendCohereRequest(request, response) { | |||
| 549 | }); | 551 | }); |
| 550 | } | 552 | } |
| 551 | 553 | ||
| 554 | /* | ||
| 552 | if (Array.isArray(request.body.tools) && request.body.tools.length > 0) { | 555 | if (Array.isArray(request.body.tools) && request.body.tools.length > 0) { |
| 553 | tools.push(...convertCohereTools(request.body.tools)); | 556 | tools.push(...convertCohereTools(request.body.tools)); |
| 554 | // Can't have both connectors and tools in the same request | 557 | // Can't have both connectors and tools in the same request |
| 555 | connectors.splice(0, connectors.length); | 558 | connectors.splice(0, connectors.length); |
| 556 | } | 559 | } |
| 560 | */ | ||
| 557 | 561 | ||
| 558 | // https://docs.cohere.com/reference/chat | 562 | // https://docs.cohere.com/reference/chat |
| 559 | const requestBody = { | 563 | const requestBody = { |
| @@ -910,18 +914,6 @@ router.post('/generate', jsonParser, function (request, response) { | |||
| 910 | apiKey = readSecret(request.user.directories, SECRET_KEYS.GROQ); | 914 | apiKey = readSecret(request.user.directories, SECRET_KEYS.GROQ); |
| 911 | headers = {}; | 915 | headers = {}; |
| 912 | bodyParams = {}; | 916 | bodyParams = {}; |
| 913 | |||
| 914 | // 'required' tool choice is not supported by Groq | ||
| 915 | if (request.body.tool_choice === 'required') { | ||
| 916 | if (Array.isArray(request.body.tools) && request.body.tools.length > 0) { | ||
| 917 | request.body.tool_choice = request.body.tools.length > 1 | ||
| 918 | ? 'auto' : | ||
| 919 | { type: 'function', function: { name: request.body.tools[0]?.function?.name } }; | ||
| 920 | |||
| 921 | } else { | ||
| 922 | request.body.tool_choice = 'none'; | ||
| 923 | } | ||
| 924 | } | ||
| 925 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.ZEROONEAI) { | 917 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.ZEROONEAI) { |
| 926 | apiUrl = API_01AI; | 918 | apiUrl = API_01AI; |
| 927 | apiKey = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI); | 919 | apiKey = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI); |
| @@ -958,7 +950,7 @@ router.post('/generate', jsonParser, function (request, response) { | |||
| 958 | controller.abort(); | 950 | controller.abort(); |
| 959 | }); | 951 | }); |
| 960 | 952 | ||
| 961 | if (!isTextCompletion) { | 953 | if (!isTextCompletion && Array.isArray(request.body.tools) && request.body.tools.length > 0) { |
| 962 | bodyParams['tools'] = request.body.tools; | 954 | bodyParams['tools'] = request.body.tools; |
| 963 | bodyParams['tool_choice'] = request.body.tool_choice; | 955 | bodyParams['tool_choice'] = request.body.tool_choice; |
| 964 | } | 956 | } |