This commit is contained in:
Andrew Pareles 2025-02-13 23:54:22 -08:00
parent 343ee5eb94
commit 0bcd88dad6
6 changed files with 110 additions and 75 deletions

View file

@ -5,10 +5,10 @@
import { Emitter, Event } from '../../../../base/common/event.js'; import { Emitter, Event } from '../../../../base/common/event.js';
import { Disposable } from '../../../../base/common/lifecycle.js'; import { Disposable } from '../../../../base/common/lifecycle.js';
import { URI } from '../../../../base/common/uri.js'; // import { URI } from '../../../../base/common/uri.js';
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { IToolService, ToolService } from '../common/toolsService.js'; // import { IToolService, ToolService } from '../common/toolsService.js';
@ -54,7 +54,7 @@ class VoidFastApplyService extends Disposable implements IFastApplyService {
// state: ApplyState // state: ApplyState
constructor( constructor(
@IToolService private readonly toolService: ToolService // @IToolService private readonly toolService: ToolService
) { ) {
super() super()
@ -88,97 +88,97 @@ class VoidFastApplyService extends Disposable implements IFastApplyService {
// -iterate on syntax errors (all files can be changed from a syntax error, not just the one with the error) // -iterate on syntax errors (all files can be changed from a syntax error, not just the one with the error)
private async _searchUsingAI({ searchClause }: { searchClause: string }) { // private async _searchUsingAI({ searchClause }: { searchClause: string }) {
const relevantURIs: URI[] = [] // // const relevantURIs: URI[] = []
const gatherPrompt = `\ // // const gatherPrompt = `\
asdasdas // // asdasdas
` // // `
const filterPrompt = `\ // // const filterPrompt = `\
Is this file relevant? // // Is this file relevant?
` // // `
// optimizations (DO THESE LATER!!!!!!) // // // optimizations (DO THESE LATER!!!!!!)
// if tool includes a uri in uriSet, skip it obviously // // // if tool includes a uri in uriSet, skip it obviously
let uriSet = new Set<URI>() // // let uriSet = new Set<URI>()
// gather // // // gather
let messages = [] // // let messages = []
while (true) { // // while (true) {
const result = await new Promise((res, rej) => { // // const result = await new Promise((res, rej) => {
sendLLMMessage({ // // sendLLMMessage({
messages, // // messages,
tools: ['search'], // // tools: ['search'],
onFinalMessage: ({ result: r, }) => { // // onFinalMessage: ({ result: r, }) => {
res(r) // // res(r)
}, // // },
onError: (error) => { // // onError: (error) => {
rej(error) // // rej(error)
} // // }
}) // // })
}) // // })
messages.push({ role: 'tool', content: turnToString(result) }) // // messages.push({ role: 'tool', content: turnToString(result) })
sendLLMMessage({ // // sendLLMMessage({
messages: { 'Output ': result }, // // messages: { 'Output ': result },
onFinalMessage: (r) => { // // onFinalMessage: (r) => {
// output is file1\nfile2\nfile3\n... // // // output is file1\nfile2\nfile3\n...
} // // }
}) // // })
uriSet.add(...) // // uriSet.add(...)
} // // }
// writes // // // writes
if (!replaceClause) return // // if (!replaceClause) return
for (const uri of uriSet) { // // for (const uri of uriSet) {
// in future, batch these // // // in future, batch these
applyWorkflow({ uri, applyStr: replaceClause }) // // applyWorkflow({ uri, applyStr: replaceClause })
} // // }
// while (true) { // // while (true) {
// const result = new Promise((res, rej) => { // // const result = new Promise((res, rej) => {
// sendLLMMessage({ // // sendLLMMessage({
// messages, // // messages,
// tools: ['search'], // // tools: ['search'],
// onResult: (r) => { // // onResult: (r) => {
// res(r) // // res(r)
// } // // }
// }) // // })
// }) // // })
// messages.push(result) // // messages.push(result)
// } // // }
} // }
private async _replaceUsingAI({ searchClause, replaceClause, relevantURIs }: { searchClause: string, replaceClause: string, relevantURIs: URI[] }) { // private async _replaceUsingAI({ searchClause, replaceClause, relevantURIs }: { searchClause: string, replaceClause: string, relevantURIs: URI[] }) {
for (const uri of relevantURIs) { // for (const uri of relevantURIs) {
uri // uri
} // }
// should I change this file? // // should I change this file?
// if so what changes to make? // // if so what changes to make?
// fast apply the changes // // fast apply the changes
} // }

View file

@ -3,7 +3,7 @@
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------*/
import { DIVIDER, FINAL, ORIGINAL } from '../prompt/prompts' import { DIVIDER, FINAL, ORIGINAL } from '../prompt/prompts.js'
class SurroundingsRemover { class SurroundingsRemover {
readonly originalS: string readonly originalS: string

View file

@ -224,7 +224,7 @@ Please finish writing the new file by applying the change to the original file.
const aiRegex_computeReplacementsForFile_systemMessage = `\ export const aiRegex_computeReplacementsForFile_systemMessage = `\
You are a "search and replace" coding assistant. You are a "search and replace" coding assistant.
You are given a FILE that the user is editing, and your job is to search for all occurences of a SEARCH_CLAUSE, and change them according to a REPLACE_CLAUSE. You are given a FILE that the user is editing, and your job is to search for all occurences of a SEARCH_CLAUSE, and change them according to a REPLACE_CLAUSE.
@ -246,7 +246,7 @@ For example, if the user is asking you to "make this variable a better name", ma
- Make sure you give enough context in the code block to apply the changes to the correct location in the code` - Make sure you give enough context in the code block to apply the changes to the correct location in the code`
const aiRegex_computeReplacementsForFile_userMessage = async ({ searchClause, replaceClause, fileURI, modelService }: { searchClause: string, replaceClause: string, fileURI: URI, modelService: IModelService }) => { export const aiRegex_computeReplacementsForFile_userMessage = async ({ searchClause, replaceClause, fileURI, modelService }: { searchClause: string, replaceClause: string, fileURI: URI, modelService: IModelService }) => {
// we may want to do this in batches // we may want to do this in batches
const fileSelection: FileSelection = { type: 'File', fileURI, selectionStr: null, range: null } const fileSelection: FileSelection = { type: 'File', fileURI, selectionStr: null, range: null }
@ -273,7 +273,7 @@ Please return the changes you want to make to the file in a codeblock, or return
// don't have to tell it it will be given the history; just give it to it // don't have to tell it it will be given the history; just give it to it
const aiRegex_search_systemMessage = `\ export const aiRegex_search_systemMessage = `\
You are a coding assistant that executes the SEARCH part of a user's search and replace query. You are a coding assistant that executes the SEARCH part of a user's search and replace query.
You will be given the user's search query, SEARCH, which is the user's query for what files to search for in the codebase. You may also be given the user's REPLACE query for additional context. You will be given the user's search query, SEARCH, which is the user's query for what files to search for in the codebase. You may also be given the user's REPLACE query for additional context.

View file

@ -162,7 +162,7 @@ export class ToolService implements IToolService {
const data = await searchService.textSearch(query, CancellationToken.None); const data = await searchService.textSearch(query, CancellationToken.None);
const str = data.results.map(({ resource, results }) => resource) const str = data.results.map(({ resource, results }) => resource)
return str return str as any
}, },
} }

View file

@ -53,11 +53,37 @@ export const sendAnthropicChat: _InternalSendLLMChatMessageFnType = ({ messages,
onText({ newText, fullText }) onText({ newText, fullText })
}) })
// can do tool use streaming
const toolCallOfIndex: { [index: string]: { name: string, args: string } } = {}
stream.on('streamEvent', e => {
if (e.type === 'content_block_start') {
if (e.content_block.type !== 'tool_use') return
const index = e.index
const tool = e.content_block
if (!toolCallOfIndex[index])
toolCallOfIndex[index] = { name: '', args: '' }
toolCallOfIndex[index].name += tool.name ?? ''
toolCallOfIndex[index].args += tool.input ?? ''
}
else if (e.type === 'content_block_delta') {
if (e.delta.type !== 'input_json_delta') return
toolCallOfIndex[e.index].args += e.delta.partial_json
}
// TODO!!!!!
// onText({})
})
// when we get the final message on this stream (or when error/fail) // when we get the final message on this stream (or when error/fail)
stream.on('finalMessage', (claude_response) => { stream.on('finalMessage', (response) => {
// stringify the response's content // stringify the response's content
const content = claude_response.content.map(c => c.type === 'text' ? c.text : c.type).join('\n'); const content = response.content.map(c => c.type === 'text' ? c.text : '').join('\n')
onFinalMessage({ fullText: content }) const tools = response.content.map(c => c.type === 'tool_use' ? { name: c.name, input: c.input } : null)
console.log("TOOLS!!!!", typeof tools[0]?.input, JSON.stringify(tools, null, 2))
onFinalMessage({ fullText: content, })
}) })
stream.on('error', (error) => { stream.on('error', (error) => {

View file

@ -121,6 +121,7 @@ export const sendOpenAIFIM: _InternalSendLLMFIMMessageFnType = ({ messages, onTe
export const sendOpenAIChat: _InternalSendLLMChatMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }) => { export const sendOpenAIChat: _InternalSendLLMChatMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }) => {
let fullText = '' let fullText = ''
const toolCallOfIndex: { [index: string]: { name: string, args: string } } = {}
const openai: OpenAI = newOpenAI({ providerName, settingsOfProvider }) const openai: OpenAI = newOpenAI({ providerName, settingsOfProvider })
const options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = { const options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = {
@ -137,11 +138,19 @@ export const sendOpenAIChat: _InternalSendLLMChatMessageFnType = ({ messages, on
// when receive text // when receive text
for await (const chunk of response) { for await (const chunk of response) {
// tool call
for (const tool of chunk.choices[0]?.delta?.tool_calls ?? []) {
const index = tool.index
if (!toolCallOfIndex[index]) toolCallOfIndex[index] = { name: '', args: '' }
toolCallOfIndex[index].name += tool.function?.name ?? ''
toolCallOfIndex[index].args += tool.function?.arguments ?? ''
}
// message
let newText = '' let newText = ''
newText += chunk.choices[0]?.delta?.tool_calls?.[0]?.function?.name ?? ''
newText += chunk.choices[0]?.delta?.tool_calls?.[0]?.function?.arguments ?? ''
newText += chunk.choices[0]?.delta?.content ?? '' newText += chunk.choices[0]?.delta?.content ?? ''
fullText += newText; fullText += newText;
onText({ newText, fullText }); onText({ newText, fullText });
} }
onFinalMessage({ fullText }); onFinalMessage({ fullText });