tool improvements

This commit is contained in:
Andrew Pareles 2025-02-16 03:23:37 -08:00
parent 7244d433dd
commit af41d6a439
6 changed files with 41 additions and 33 deletions

View file

@ -326,19 +326,21 @@ class ChatThreadService extends Disposable implements IChatThreadService {
this._setStreamState(threadId, { messageSoFar: fullText })
},
onFinalMessage: async ({ fullText, tools }) => {
if (tools.length === 0) {
this._addMessageToThread(threadId, { role: 'assistant', content: fullText, displayContent: fullText, tool_calls: tools })
if ((tools?.length ?? 0) === 0) {
this._finishStreamingTextMessage(threadId, fullText)
}
else {
for (const tool of tools) {
for (const tool of tools ?? []) {
if (!(tool.name in this._toolsService.toolFns)) {
this._addMessageToThread(threadId, { role: 'tool', name: tool.name, params: tool.args, id: tool.id, content: `Error: This tool was not recognized, so it was not called.`, displayContent: `Error: tool not recognized.`, })
this._addMessageToThread(threadId, { role: 'tool', name: tool.name, params: tool.params, id: tool.id, content: `Error: This tool was not recognized, so it was not called.`, displayContent: `Error: tool not recognized.`, })
}
else {
const toolName = tool.name as ToolName
const toolResult = await this._toolsService.toolFns[toolName](tool.args)
const toolResult = await this._toolsService.toolFns[toolName](tool.params)
const string = this._toolsService.toolResultToString[toolName](toolResult as any) // typescript is so bad it doesn't even couple the type of ToolResult with the type of the function being called here
this._addMessageToThread(threadId, { role: 'tool', name: tool.name, params: tool.args, id: tool.id, content: string, displayContent: string, })
this._addMessageToThread(threadId, { role: 'tool', name: tool.name, params: tool.params, id: tool.id, content: string, displayContent: string, })
shouldContinue = true
}
}

View file

@ -23,7 +23,7 @@ export const errorDetails = (fullError: Error | null): string | null => {
}
export type OnText = (p: { newText: string, fullText: string }) => void
export type OnFinalMessage = (p: { fullText: string, tools: { name: string, args: string, id: string, }[] }) => void // id is tool_use_id
export type OnFinalMessage = (p: { fullText: string, tools?: { name: string, params: string, id: string, }[] }) => void // id is tool_use_id
export type OnError = (p: { message: string, fullError: Error | null }) => void
export type AbortRef = { current: (() => void) | null }

View file

@ -18,7 +18,7 @@ export type DeveloperInfoAtModel = {
// UNUSED (coming soon):
recognizedModelName: RecognizedModelName, // used to show user if model was auto-recognized
supportsTools: boolean, // we will just do a string of tool use if it doesn't support
supportsSystemMessage: 'developer' | 'system' | false, // if null, we will just do a string of system message
supportsSystemMessageRole: 'developer' | 'system' | false, // if null, we will just do a string of system message. this is independent from separateSystemMessage, which takes priority and is passed directly in each provider's implementation.
supportsAutocompleteFIM: boolean, // we will just do a description of FIM if it doens't support <|fim_hole|>
supportsStreaming: boolean, // (o1 does NOT) we will just dump the final result if doesn't support it
maxTokens: number, // required
@ -98,7 +98,7 @@ export function recognizedModelOfModelName(modelName: string): RecognizedModelNa
const developerInfoAtProvider: { [providerName in ProviderName]: DeveloperInfoAtProvider } = {
'anthropic': {
overrideSettingsForAllModels: {
supportsSystemMessage: 'system',
supportsSystemMessageRole: 'system',
supportsTools: true,
supportsAutocompleteFIM: false,
supportsStreaming: true,
@ -106,7 +106,7 @@ const developerInfoAtProvider: { [providerName in ProviderName]: DeveloperInfoAt
},
'deepseek': {
overrideSettingsForAllModels: {
supportsSystemMessage: false,
supportsSystemMessageRole: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: true,
@ -137,15 +137,15 @@ export const developerInfoOfProviderName = (providerName: ProviderName): Partial
// providerName is optional, but gives some extra fallbacks if provided
const developerInfoOfRecognizedModelName: { [recognizedModel in RecognizedModelName]: Omit<DeveloperInfoAtModel, 'recognizedModelName'> } = {
'OpenAI 4o': {
supportsSystemMessage: false,
supportsTools: false,
supportsSystemMessageRole: 'system',
supportsTools: true,
supportsAutocompleteFIM: false,
supportsStreaming: false,
supportsStreaming: true,
maxTokens: 4096,
},
'Anthropic Claude': {
supportsSystemMessage: false,
supportsSystemMessageRole: 'system',
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
@ -153,7 +153,7 @@ const developerInfoOfRecognizedModelName: { [recognizedModel in RecognizedModelN
},
'Llama 3.x': {
supportsSystemMessage: false,
supportsSystemMessageRole: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
@ -161,7 +161,7 @@ const developerInfoOfRecognizedModelName: { [recognizedModel in RecognizedModelN
},
'Deepseek Chat': {
supportsSystemMessage: false,
supportsSystemMessageRole: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
@ -169,7 +169,7 @@ const developerInfoOfRecognizedModelName: { [recognizedModel in RecognizedModelN
},
'Alibaba Qwen2.5 Coder Instruct': {
supportsSystemMessage: false,
supportsSystemMessageRole: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
@ -177,7 +177,7 @@ const developerInfoOfRecognizedModelName: { [recognizedModel in RecognizedModelN
},
'Mistral Codestral': {
supportsSystemMessage: false,
supportsSystemMessageRole: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
@ -185,7 +185,7 @@ const developerInfoOfRecognizedModelName: { [recognizedModel in RecognizedModelN
},
'OpenAI o1, o3': {
supportsSystemMessage: false,
supportsSystemMessageRole: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
@ -193,7 +193,7 @@ const developerInfoOfRecognizedModelName: { [recognizedModel in RecognizedModelN
},
'Deepseek R1': {
supportsSystemMessage: false,
supportsSystemMessageRole: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
@ -201,7 +201,7 @@ const developerInfoOfRecognizedModelName: { [recognizedModel in RecognizedModelN
},
'<GENERAL>': {
supportsSystemMessage: false,
supportsSystemMessageRole: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,

View file

@ -5,7 +5,7 @@
import Anthropic from '@anthropic-ai/sdk';
import { _InternalSendLLMChatMessageFnType } from '../../common/llmMessageTypes.js';
import { anthropicMaxPossibleTokens } from '../../common/voidSettingsTypes.js';
import { anthropicMaxPossibleTokens, developerInfoOfModelName, developerInfoOfProviderName } from '../../common/voidSettingsTypes.js';
import { InternalToolInfo } from '../../common/toolsService.js';
import { addSystemMessageAndToolSupport } from './processMessages.js';
@ -39,11 +39,14 @@ export const sendAnthropicChat: _InternalSendLLMChatMessageFnType = ({ messages:
return
}
const { messages, separateSystemMessageStr, devInfo } = addSystemMessageAndToolSupport(modelName, providerName, messages_, aiInstructions, { separateSystemMessage: true })
const { messages, separateSystemMessageStr } = addSystemMessageAndToolSupport(modelName, providerName, messages_, aiInstructions, { separateSystemMessage: true })
const { overrideSettingsForAllModels } = developerInfoOfProviderName(providerName)
const { supportsTools } = developerInfoOfModelName(modelName, overrideSettingsForAllModels)
const anthropic = new Anthropic({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true });
const tools = devInfo?.supportsTools && (tools_?.length ?? 0) !== 0 ? tools_?.map(tool => toAnthropicTool(tool)) : undefined
const tools = (supportsTools && ((tools_?.length ?? 0) !== 0)) ? tools_?.map(tool => toAnthropicTool(tool)) : undefined
const stream = anthropic.messages.stream({
system: separateSystemMessageStr,

View file

@ -8,6 +8,7 @@ import { _InternalModelListFnType, _InternalSendLLMFIMMessageFnType, _InternalSe
import { Model } from 'openai/resources/models.js';
import { InternalToolInfo } from '../../common/toolsService.js';
import { addSystemMessageAndToolSupport } from './processMessages.js';
import { developerInfoOfModelName, developerInfoOfProviderName } from '../../common/voidSettingsTypes.js';
// import { parseMaxTokensStr } from './util.js';
@ -147,12 +148,14 @@ export const sendOpenAIFIM: _InternalSendLLMFIMMessageFnType = ({ messages, onTe
export const sendOpenAIChat: _InternalSendLLMChatMessageFnType = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName, aiInstructions, tools: tools_ }) => {
let fullText = ''
const toolCallOfIndex: { [index: string]: { name: string, args: string, id: string } } = {}
const toolCallOfIndex: { [index: string]: { name: string, params: string, id: string } } = {}
const { messages, devInfo } = addSystemMessageAndToolSupport(modelName, providerName, messages_, aiInstructions, { separateSystemMessage: false })
const { overrideSettingsForAllModels } = developerInfoOfProviderName(providerName)
const { supportsTools } = developerInfoOfModelName(modelName, overrideSettingsForAllModels)
const tools = devInfo?.supportsTools && (tools_?.length ?? 0) !== 0 ? tools_?.map(tool => toOpenAITool(tool)) : undefined
const { messages } = addSystemMessageAndToolSupport(modelName, providerName, messages_, aiInstructions, { separateSystemMessage: false })
const tools = (supportsTools && ((tools_?.length ?? 0) !== 0)) ? tools_?.map(tool => toOpenAITool(tool)) : undefined
const openai: OpenAI = newOpenAI({ providerName, settingsOfProvider })
const options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = {
@ -175,9 +178,9 @@ export const sendOpenAIChat: _InternalSendLLMChatMessageFnType = ({ messages: me
// tool call
for (const tool of chunk.choices[0]?.delta?.tool_calls ?? []) {
const index = tool.index
if (!toolCallOfIndex[index]) toolCallOfIndex[index] = { name: '', args: '', id: '' }
if (!toolCallOfIndex[index]) toolCallOfIndex[index] = { name: '', params: '', id: '' }
toolCallOfIndex[index].name += tool.function?.name ?? ''
toolCallOfIndex[index].args += tool.function?.arguments ?? '';
toolCallOfIndex[index].params += tool.function?.arguments ?? '';
toolCallOfIndex[index].id = tool.id ?? ''
}

View file

@ -1,7 +1,7 @@
import { LLMChatMessage } from '../../common/llmMessageTypes.js';
import { DeveloperInfoAtModel, developerInfoOfModelName, developerInfoOfProviderName, ProviderName } from '../../common/voidSettingsTypes.js';
import { developerInfoOfModelName, developerInfoOfProviderName, ProviderName } from '../../common/voidSettingsTypes.js';
import { deepClone } from '../../../../../base/common/objects.js';
@ -9,13 +9,12 @@ import { deepClone } from '../../../../../base/common/objects.js';
// no matter whether the model supports a system message or not (or what format it supports), add it in some way
// also take into account tools if the model doesn't support tool use
export const addSystemMessageAndToolSupport = (modelName: string, providerName: ProviderName, messages_: LLMChatMessage[], aiInstructions: string, { separateSystemMessage }: { separateSystemMessage: boolean }): { separateSystemMessageStr?: string, messages: any[], devInfo: DeveloperInfoAtModel } => {
export const addSystemMessageAndToolSupport = (modelName: string, providerName: ProviderName, messages_: LLMChatMessage[], aiInstructions: string, { separateSystemMessage }: { separateSystemMessage: boolean }): { separateSystemMessageStr?: string, messages: any[] } => {
const messages = deepClone(messages_).map(m => ({ ...m, content: m.content.trim(), }))
const { overrideSettingsForAllModels } = developerInfoOfProviderName(providerName)
const devInfo = developerInfoOfModelName(modelName, overrideSettingsForAllModels)
const { supportsSystemMessage, supportsTools } = devInfo
const { supportsSystemMessageRole: supportsSystemMessage, supportsTools } = developerInfoOfModelName(modelName, overrideSettingsForAllModels)
// 1. SYSTEM MESSAGE
// find system messages and concatenate them
@ -236,12 +235,13 @@ export const addSystemMessageAndToolSupport = (modelName: string, providerName:
// TODO!!!
console.log('SYSMG', separateSystemMessage)
console.log('FINAL MESSAGES', finalMessages)
return {
separateSystemMessageStr,
messages: finalMessages,
devInfo,
}
}