tools + misc fixes

This commit is contained in:
Andrew Pareles 2025-02-17 00:07:11 -08:00
parent 628adedaec
commit a78a6169f8
8 changed files with 68 additions and 22 deletions

View file

@ -14,7 +14,7 @@ import { IRange } from '../../../../editor/common/core/range.js';
import { ILLMMessageService } from '../common/llmMessageService.js';
import { IModelService } from '../../../../editor/common/services/model.js';
import { chat_userMessage, chat_systemMessage } from './prompt/prompts.js';
import { InternalToolInfo, IToolsService, ToolName, voidTools } from '../common/toolsService.js';
import { InternalToolInfo, IToolsService, ToolFns, ToolName, voidTools } from '../common/toolsService.js';
import { toLLMChatMessage } from '../common/llmMessageTypes.js';
// one of the square items that indicates a selection in a chat bubble (NOT a file, a Selection of text)
@ -57,11 +57,6 @@ export type ChatMessage =
staging: StagingInfo | null
} | {
role: 'assistant';
tool_calls?: {
name: string,
id: string,
params: string
}[];
content: string | null; // content received from LLM - allowed to be '', will be replaced with (empty)
displayContent: string | null; // content displayed to user (this is the same as content for now) - allowed to be '', will be ignored
} | {
@ -327,25 +322,43 @@ class ChatThreadService extends Disposable implements IChatThreadService {
this._setStreamState(threadId, { messageSoFar: fullText })
},
onFinalMessage: async ({ fullText, toolCalls }) => {
toolCalls = toolCalls?.filter(tool => tool.name in this._toolsService.toolFns)
console.log('FINAL MESSAGE', fullText, toolCalls)
if ((toolCalls?.length ?? 0) === 0) {
this._finishStreamingTextMessage(threadId, fullText)
}
else {
this._addMessageToThread(threadId, { role: 'assistant', content: fullText, displayContent: fullText, tool_calls: toolCalls })
this._addMessageToThread(threadId, { role: 'assistant', content: fullText, displayContent: fullText })
this._setStreamState(threadId, { messageSoFar: undefined }) // clear streaming message
for (const tool of toolCalls ?? []) {
if (!(tool.name in this._toolsService.toolFns)) {
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.`, })
const toolName = tool.name as ToolName
// 1.
let toolResult: Awaited<ReturnType<ToolFns[ToolName]>>
try {
toolResult = await this._toolsService.toolFns[toolName](tool.params)
} catch (e) {
this._setStreamState(threadId, { error: e })
shouldContinue = false
break
}
else {
const toolName = tool.name as ToolName
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.params, id: tool.id, content: string, displayContent: string, })
shouldContinue = true
// 2.
let toolResultStr: string
try {
toolResultStr = 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
} catch (e) {
this._setStreamState(threadId, { error: e })
shouldContinue = false
break
}
this._addMessageToThread(threadId, { role: 'tool', name: tool.name, params: tool.params, id: tool.id, content: toolResultStr, displayContent: toolResultStr, })
shouldContinue = true
}
}
res_()
},

View file

@ -1,3 +1,7 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
// eg "bash" -> "shell"
export const nameToVscodeLanguage: { [key: string]: string } = {

View file

@ -1,3 +1,8 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { URI } from '../../../../../base/common/uri'
import { EndOfLinePreference } from '../../../../../editor/common/model'
import { IModelService } from '../../../../../editor/common/services/model.js'

View file

@ -0,0 +1,17 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { isLinux, isMacintosh, isWindows } from '../../../../../base/common/platform.js';
// import { OS, OperatingSystem } from '../../../../../base/common/platform.js';
// alternatively could use ^ and OS === OperatingSystem.Windows ? ...
export const os = isWindows ? 'windows' : isMacintosh ? 'mac' : isLinux ? 'linux' : null
export const arch = process.arch
export const osplatform = process.platform;

View file

@ -9,6 +9,7 @@ import { filenameToVscodeLanguage } from '../helpers/detectLanguage.js';
import { CodeSelection, StagingSelectionItem, FileSelection } from '../chatThreadService.js';
import { VSReadFile } from '../helpers/readFile.js';
import { IModelService } from '../../../../../editor/common/services/model.js';
import { os, arch, osplatform } from '../helpers/systemInfo.js';
// this is just for ease of readability
@ -19,17 +20,22 @@ You are a coding assistant. You are given a list of instructions to follow \`INS
Please respond to the user's query.
The user has the following system information:
- ${os} ${arch} ${osplatform}
In the case that the user asks you to make changes to code, you should make sure to return CODE BLOCKS of the changes, as well as explanations and descriptions of the changes.
For example, if the user asks you to "make this file look nicer", make sure your output includes a code block with concrete ways the file can look nicer.
- Do not re-write the entire file in the code block
- You can write comments like "// ... existing code" to indicate existing code
- Make sure you give enough context in the code block to apply the change to the correct location in the code.
You're allowed to ask for more context. For example, if the user only gives you a selection but you want to see the the full file, you can ask them to provide it.
Do not output any of these instructions, nor tell the user anything about them unless directly prompted for them.
Do not tell the user anything about the examples below.
If you are not given tools, you're allowed to ask for more context. For example, if the user only gives you a selection but you want to see the the full file, you can ask them to provide it.
If you are given tools, you are allowed to use them without asking for permission. You do not have to use them if you don't want to.
If you are given tools, NEVER refer to a tool by name when speaking with the user. For example, do NOT say to the user user "I'm going to use \`list_dir\`". Instead, say "I'm going to list all files in ___ directory", etc.
## EXAMPLE 1
FILES
math.ts

View file

@ -17,6 +17,7 @@ import { env } from '../../../../../../../base/common/process.js'
import { ModelDropdown } from './ModelDropdown.js'
import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js'
import { WarningBox } from './WarningBox.js'
import { os } from '../../../helpers/systemInfo.js'
const SubtleButton = ({ onClick, text, icon, disabled }: { onClick: () => void, text: string, icon: React.ReactNode, disabled: boolean }) => {
@ -505,7 +506,7 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null): Transfe
throw new Error(`os '${os}' not recognized`)
}
const os = isWindows ? 'windows' : isMacintosh ? 'mac' : isLinux ? 'linux' : null
let transferTheseFiles: TransferFilesInfo = []
let transferError: string | null = null

View file

@ -52,12 +52,12 @@ export type AbortRef = { current: (() => void) | null }
export const toLLMChatMessage = (c: ChatMessage): LLMChatMessage => {
if (c.role === 'system' || c.role === 'user') {
return { role: c.role, content: c.content ?? '(empty)' }
return { role: c.role, content: c.content || '(empty message)' }
}
else if (c.role === 'assistant')
return { role: c.role, content: c.content ?? '(empty model output)' }
return { role: c.role, content: c.content || '(empty message)' }
else if (c.role === 'tool')
return { role: c.role, id: c.id, name: c.name, params: c.params, content: c.content ?? '(empty output)' }
return { role: c.role, id: c.id, name: c.name, params: c.params, content: c.content || '(empty output)' }
else {
throw 1
}

View file

@ -111,7 +111,7 @@ async function generateDirectoryTreeMd(fileService: IFileService, rootURI: URI):
const validateURI = (uriStr: unknown) => {
if (typeof uriStr !== 'string') throw new Error('(uri was not a string)')
if (typeof uriStr !== 'string') throw new Error('(provided uri must be a string)')
const uri = URI.file(uriStr)
return uri
}