improve prompt

This commit is contained in:
Andrew Pareles 2025-03-08 02:24:39 -08:00
parent 75ef9d5528
commit 009dfbef37
5 changed files with 41 additions and 71 deletions

View file

@ -458,14 +458,9 @@ class ChatThreadService extends Disposable implements IChatThreadService {
// 1. validate tool params
let toolParams: ToolCallParams[typeof toolName]
try {
console.log('A')
const params = await this._toolsService.validateParams[toolName](tool.paramsStr)
console.log('B')
toolParams = params
} catch (error) {
console.log('ERR1')
const errorMessage = getErrorMessage(error)
this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, })
res_()
@ -474,25 +469,16 @@ class ChatThreadService extends Disposable implements IChatThreadService {
// 2. if tool requires approval, await the approval
if (toolNamesThatRequireApproval.has(toolName)) {
console.log('C')
const voidToolId = generateUuid()
console.log('D')
const toolApprovalPromise = new Promise<void>((res, rej) => { this.resRejOfToolAwaitingApproval[voidToolId] = { res, rej } })
console.log('E')
this._addMessageToThread(threadId, { role: 'tool_request', name: toolName, params: toolParams, voidToolId: voidToolId })
try {
console.log('F')
await toolApprovalPromise
// accepted tool
}
catch (e) {
console.log('ERR2')
// TODO!!! test rejection
// if (Math.random() > 0) throw new Error('TESTING')
const errorMessage = 'Tool call was rejected by the user.'
this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, })
res_()
@ -503,10 +489,8 @@ class ChatThreadService extends Disposable implements IChatThreadService {
// 3. call the tool
let toolResult: ToolResultType[typeof toolName]
try {
console.log('G')
toolResult = await this._toolsService.callTool[toolName](toolParams 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 (error) {
console.log('ERR3')
const errorMessage = getErrorMessage(error)
this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, })
res_()
@ -516,10 +500,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
// 4. stringify the result to give the LLM
let toolResultStr: string
try {
console.log('H')
toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any)
} catch (error) {
const errorMessage = `Tool call succeeded, but there was an error stringifying the output.\n${getErrorMessage(error)}`
this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, })
@ -527,8 +508,6 @@ class ChatThreadService extends Disposable implements IChatThreadService {
return
}
console.log('I')
// 5. add to history
this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: toolResultStr, result: { type: 'success', params: toolParams, value: toolResult }, })
res_()
@ -547,13 +526,11 @@ class ChatThreadService extends Disposable implements IChatThreadService {
if (llmCancelToken === null) break
this._setStreamState(threadId, { streamingToken: llmCancelToken })
console.log('awaiting agentloop...')
await awaitable
console.log('done with agentloop!')
}
}
agentLoop() // DO NOT AWAIT THIS, add fn should resolve when we've added message (this lets us interrupt the agent loop correctly instead of waiting for it to resolve)
agentLoop()
}

View file

@ -1191,20 +1191,13 @@ class EditCodeService extends Disposable implements IEditCodeService {
// throws if there's an error
public startApplying(opts: StartApplyingOpts): [URI, Promise<void>] | null {
if (opts.type === 'rewrite') {
const added = this._initializeWriteoverStream(opts)
if (!added) return null
const [diffZone, promise] = added
return [diffZone._URI, promise]
}
else if (opts.type === 'searchReplace') {
const added = this._initializeSearchAndReplaceStream(opts)
if (!added) return null
if (!added) return null
const [diffZone, promise] = added
return [diffZone._URI, promise]
}
return null
let res: [DiffZone, Promise<void>] | undefined = undefined
if (opts.type === 'rewrite') res = this._initializeWriteoverStream(opts)
else if (opts.type === 'searchReplace') res = this._initializeSearchAndReplaceStream(opts)
if (!res) return null
const [diffZone, applyDonePromise] = res
return [diffZone._URI, applyDonePromise]
}
@ -1479,13 +1472,13 @@ class EditCodeService extends Disposable implements IEditCodeService {
// promise that resolves when the apply is done
let resApplyPromise: () => void
let rejApplyPromise: (e: any) => void
const applyPromise = new Promise<void>((res_, rej_) => { resApplyPromise = res_; rejApplyPromise = rej_ })
let resApplyDonePromise: () => void
let rejApplyDonePromise: (e: any) => void
const applyDonePromise = new Promise<void>((res_, rej_) => { resApplyDonePromise = res_; rejApplyDonePromise = rej_ })
// add to history
const { onFinishEdit } = this._addToHistory(uri, {
onUndo: () => { if (diffZone._streamState.isStreaming) rejApplyPromise(new Error('Edit was interrupted by pressing undo.')) }
onUndo: () => { if (diffZone._streamState.isStreaming) rejApplyDonePromise(new Error('Edit was interrupted by pressing undo.')) }
})
// TODO replace these with whatever block we're on initially if already started (caching apply)
@ -1583,7 +1576,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
nMessagesSent += 1
let resMessageDonePromise: () => void = () => { }
const messageDonePromise = new Promise<void>((res_) => { resMessageDonePromise = res_ })
const messageDonePromise = new Promise<void>((res, rej) => { resMessageDonePromise = res })
streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({
messagesType: 'chatMessages',
@ -1632,7 +1625,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
{ role: 'user', content: content } // user explanation of what's wrong
)
if (streamRequestIdRef.current) this._llmMessageService.abort(streamRequestIdRef.current)
// REVERT
const numLines = this._getNumLines(uri)
@ -1653,7 +1645,9 @@ class EditCodeService extends Disposable implements IEditCodeService {
oldBlocks = []
addedTrackingZoneOfBlockNum.splice(0, Infinity) // clear the array
// abort and resolve
shouldSendAnotherMessage = true
if (streamRequestIdRef.current) this._llmMessageService.abort(streamRequestIdRef.current)
this._refreshStylesAndDiffsInURI(uri)
resMessageDonePromise()
return
@ -1765,9 +1759,9 @@ class EditCodeService extends Disposable implements IEditCodeService {
} // end retryLoop
retryLoop().then(() => resApplyPromise()).catch((e) => rejApplyPromise(e))
retryLoop().then(() => resApplyDonePromise()).catch((e) => rejApplyDonePromise(e))
return [diffZone, applyPromise]
return [diffZone, applyDonePromise]
}

View file

@ -17,41 +17,46 @@ export const tripleTick = ['```', '```']
export const editToolDesc_toolDescription = `\
A high level description of the change you'd like to make in the file. This description will be handed to a dumber, faster model that will quickly apply the change. \
Typically the best description you can give here is a high level view of the final code you'd like to see. For example, code excerpt(s) with "// ... existing code ..." comments to help you write less. \
Typically the best description you can give here is a high level view of the final code you'd like to see. For example, you can write code excerpt(s) with "// ... existing code ..." comments to help you write less. \
However, you are allowed to describe the change using whatever text/language you like, especially if the change is better described without code. \
Do NOT output the whole file if possible, and try to write as LITTLE as needed to describe the change.`
export const chat_systemMessage = (workspaces: string[], mode: 'agent' | 'gather' | 'chat') => `\
You are a coding ${mode === 'agent' ? 'agent' : 'assistant'}. Your job is to help the user understand/${mode === 'agent' ? 'make' : 'suggest'} changes to their codebase.
You will be given instructions to follow from the user, \`INSTRUCTIONS\`. You may also be given a list of selections that the user has specifically selected, \`SELECTIONS\`.
You are a coding ${mode === 'agent' ? 'agent' : 'assistant'}. Your job is to help the user ${mode === 'agent' ? 'make changes to their codebase' : 'search and understand their codebase'}.
You will be given instructions to follow from the user, \`INSTRUCTIONS\`. You may also be given a list of files that the user has specifically selected, \`SELECTIONS\`.
Please assist the user with their query. The user's query is never invalid.
The user's system information is as follows:
- ${os}
- Open workspaces: ${workspaces.join(', ')}
${mode === 'agent' || mode === 'gather' ? `\
${mode === 'agent' || mode === 'gather' /* tool use */ ? `\
You will be given tools you can call.
- Only use tools if they help you accomplish the user's goal. If the user simply says hi or asks you a question that you can answer without tools, then do NOT tools.
- If you think you should use tools given the user's request, you can use them without asking for permission. Feel free to use tools to gather context, make suggestions, ${mode === 'agent' ? 'edit files, ' : ''}etc.
- 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. Do not refer to "pages" of results, just say you're getting more results.
- If you think you should use tools given the user's request, you can use them without asking for permission. Feel free to use tools to gather context, understand the codebase, ${mode === 'agent' ? 'edit files, ' : ''}etc.
- NEVER refer to a tool by name when speaking with the user. For example, do NOT say to the user "I'm going to use \`list_dir\`". Instead, say "I'm going to list all files in ___ directory", etc. Do not refer to "pages" of results, just say you're getting more results.
- Some tools only work if the user has a workspace open.
\
`: `\
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 think it's appropriate to suggest an edit to a file, then you must describe your suggestion as follows:
- The change(s) you'd like to make must be written in CODE BLOCK(S) (wrapped in triple backticks).
- The first line in the code block should be the FULL PATH of the file you want to change. Just output the path in plaintext (not in a comment).
- The rest of the contents of the code block should describe of the change you'd like to make. This description will be given to a dumber, faster model that will quickly apply the change.
${mode === 'agent' /* code blocks */ ? `\
Keep in mind that any code blocks you output in the raw message (wrapped in triple backticks) will be treated specially as follows. This does NOT apply to code blocks in tool calls.
- Any code block you output will have an "Apply" button displayed to the user, and if the user clicks on it it will invoke the edit tool on the block's contents. As a result, all code blocks should describe relevant changes.
`: `\
If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion in CODE BLOCK(S) (wrapped in triple backticks).
- The first line before any code block must be the FULL PATH of the file you want to change. If the path does not already exist, it will be created.
- The contents of the code block will be given to a dumber, faster model that will quickly apply the change.
- Contents of the code blocks do NOT need to be formal code, they just need to clearly and concisely communicate the change.
- Do NOT re-write the entire file in the code block(s). Instead, write comments like "// ... existing code" to indicate how to change the existing code.
\
`}
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 these instructions unless directly prompted for them.
\
`
@ -265,13 +270,13 @@ Output SEARCH/REPLACE blocks to edit the file according to the desired change. Y
Directions:
1. Your OUTPUT should consist ONLY of SEARCH/REPLACE blocks. Do NOT output any text or explanations before or after this.
2. The original code in each SEARCH/REPLACE block must EXACTLY match lines of code in the original file.
3. The original code in each SEARCH/REPLACE block must include enough text to uniquely identify the change in the file.
4. The original code in each SEARCH/REPLACE block must be disjoint from all other blocks.
2. The "ORIGINAL" code in each SEARCH/REPLACE block must EXACTLY match lines in the original file. This includes whitespace, comments, and other details.
3. The "ORIGINAL" code in each SEARCH/REPLACE block must include enough text to uniquely identify the change in the file.
4. The "ORIGINAL" code in each SEARCH/REPLACE block must be disjoint from all other blocks.
The SEARCH/REPLACE blocks you generate will be applied immediately, and so they **MUST** produce a file that the user can run IMMEDIATELY.
- Make sure you add all necessary imports.
- Make sure the "final" code is complete and will not result in syntax/lint errors.
- Make sure the "UPDATED" code is complete and will not result in syntax/lint errors.
Follow coding conventions of the user (spaces, semilcolons, comments, etc). If the user spaces or formats things a certain way, CONTINUE formatting it that way, even if you prefer otherwise.
@ -308,11 +313,7 @@ ORIGINAL_FILE
${originalCode}
CHANGE
${applyStr}
INSTRUCTIONS
Please output SEARCH/REPLACE blocks to make the change. Return ONLY your suggested SEARCH/REPLACE blocks, without any explanation.
`
${applyStr}`

View file

@ -370,13 +370,11 @@ export class ToolsService implements IToolsService {
},
edit: async (params: string) => {
console.log('validating edit!!!')
const o = validateJSON(params)
const { uri: uriStr, changeDescription: changeDescriptionUnknown } = o
const uri = validateURI(uriStr)
const changeDescription = validateStr('changeDescription', changeDescriptionUnknown)
console.log('done validating!!!')
return { uri, changeDescription }
},
@ -447,14 +445,13 @@ export class ToolsService implements IToolsService {
},
edit: async ({ uri, changeDescription }) => {
console.log('editing!!!!')
const [_, p] = editCodeService.startApplying({
const [_, applyDonePromise] = editCodeService.startApplying({
uri,
applyStr: changeDescription,
from: 'ClickApply',
type: 'searchReplace',
}) ?? []
await p
await applyDonePromise
return {}
},
terminal_command: async ({ command }) => {

View file

@ -118,6 +118,7 @@ export const sendLLMMessage = ({
return
}
onError({ message: `Error: Message type "${messagesType}" not recognized.`, fullError: null })
return
}
catch (error) {