apply improvements

This commit is contained in:
Andrew Pareles 2025-03-17 20:55:37 -07:00
parent a320323aa1
commit b1e50798d8
4 changed files with 100 additions and 84 deletions

View file

@ -21,7 +21,6 @@ import { ToolName, ToolCallParams, ToolResultType, InternalToolInfo, voidTools,
import { IToolsService } from './toolsService.js';
import { CancellationToken } from '../../../../base/common/cancellation.js';
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js';
import { ITextModelService } from '../../../../editor/common/services/resolverService.js';
import { ChatMessage, CodespanLocationLink, StagingSelectionItem, ToolMessage, ToolRequestApproval } from '../common/chatThreadServiceTypes.js';
import { Position } from '../../../../editor/common/core/position.js';
import { ITerminalToolService } from './terminalToolService.js';
@ -207,7 +206,6 @@ class ChatThreadService extends Disposable implements IChatThreadService {
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
@IVoidSettingsService private readonly _settingsService: IVoidSettingsService,
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
@ITextModelService private readonly _textModelService: ITextModelService,
@ITerminalToolService private readonly _terminalToolService: ITerminalToolService,
@IMetricsService private readonly _metricsService: IMetricsService,
) {
@ -615,6 +613,8 @@ class ChatThreadService extends Disposable implements IChatThreadService {
const llmCancelToken = this.streamState[threadId]?.streamingToken
if (llmCancelToken !== undefined) this._llmMessageService.abort(llmCancelToken)
this._setStreamState(threadId, {}, 'set')
}
@ -817,6 +817,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
onAbort: () => {
// stop the loop to free up the promise, but don't modify state (already handled by whatever stopped it)
resMessageIsDonePromise()
this._metricsService.capture('Agent Loop Done (Aborted)', { nMessagesSent, chatMode })
aborted = true
},
})
@ -832,10 +833,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
this._setStreamState(threadId, { streamingToken: llmCancelToken }, 'merge') // new stream token for the new message
await messageIsDonePromise
if (aborted) {
this._metricsService.capture('Agent Loop Done', { nMessagesSent, chatMode })
return
}
if (aborted) { return }
} // end while
// if awaiting user approval, keep isRunning true, else end isRunning
@ -978,91 +976,87 @@ class ChatThreadService extends Disposable implements IChatThreadService {
// check all prevUris for the target
for (const uri of prevUris) {
const modelRef = await this._textModelService.createModelReference(uri);
const model = modelRef.object.textEditorModel;
const modelRef = await this._voidModelService.getModelSafe(uri)
const { model } = modelRef
if (!model) continue
try {
const matches = model.findMatches(
target,
false, // searchOnlyEditableRange
false, // isRegex
true, // matchCase
' ', // wordSeparators
true // captureMatches
);
const matches = model.findMatches(
target,
false, // searchOnlyEditableRange
false, // isRegex
true, // matchCase
' ', // wordSeparators
true // captureMatches
);
const firstThree = matches.slice(0, 3);
const firstThree = matches.slice(0, 3);
// take first 3 occurences, attempt to goto definition on them
for (const match of firstThree) {
const position = new Position(match.range.startLineNumber, match.range.startColumn);
const definitionProviders = this._languageFeaturesService.definitionProvider.ordered(model);
// take first 3 occurences, attempt to goto definition on them
for (const match of firstThree) {
const position = new Position(match.range.startLineNumber, match.range.startColumn);
const definitionProviders = this._languageFeaturesService.definitionProvider.ordered(model);
for (const provider of definitionProviders) {
for (const provider of definitionProviders) {
const _definitions = await provider.provideDefinition(model, position, CancellationToken.None);
const _definitions = await provider.provideDefinition(model, position, CancellationToken.None);
if (!_definitions) continue;
if (!_definitions) continue;
const definitions = Array.isArray(_definitions) ? _definitions : [_definitions];
const definitions = Array.isArray(_definitions) ? _definitions : [_definitions];
for (const definition of definitions) {
for (const definition of definitions) {
return {
uri: definition.uri,
selection: {
startLineNumber: definition.range.startLineNumber,
startColumn: definition.range.startColumn,
endLineNumber: definition.range.endLineNumber,
endColumn: definition.range.endColumn,
},
displayText: _codespanStr,
};
return {
uri: definition.uri,
selection: {
startLineNumber: definition.range.startLineNumber,
startColumn: definition.range.startColumn,
endLineNumber: definition.range.endLineNumber,
endColumn: definition.range.endColumn,
},
displayText: _codespanStr,
};
// const defModelRef = await this._textModelService.createModelReference(definition.uri);
// const defModel = defModelRef.object.textEditorModel;
// const defModelRef = await this._textModelService.createModelReference(definition.uri);
// const defModel = defModelRef.object.textEditorModel;
// try {
// const symbolProviders = this._languageFeaturesService.documentSymbolProvider.ordered(defModel);
// try {
// const symbolProviders = this._languageFeaturesService.documentSymbolProvider.ordered(defModel);
// for (const symbolProvider of symbolProviders) {
// const symbols = await symbolProvider.provideDocumentSymbols(
// defModel,
// CancellationToken.None
// );
// for (const symbolProvider of symbolProviders) {
// const symbols = await symbolProvider.provideDocumentSymbols(
// defModel,
// CancellationToken.None
// );
// if (symbols) {
// const symbol = symbols.find(s => {
// const symbolRange = s.range;
// return symbolRange.startLineNumber <= definition.range.startLineNumber &&
// symbolRange.endLineNumber >= definition.range.endLineNumber &&
// (symbolRange.startLineNumber !== definition.range.startLineNumber || symbolRange.startColumn <= definition.range.startColumn) &&
// (symbolRange.endLineNumber !== definition.range.endLineNumber || symbolRange.endColumn >= definition.range.endColumn);
// });
// if (symbols) {
// const symbol = symbols.find(s => {
// const symbolRange = s.range;
// return symbolRange.startLineNumber <= definition.range.startLineNumber &&
// symbolRange.endLineNumber >= definition.range.endLineNumber &&
// (symbolRange.startLineNumber !== definition.range.startLineNumber || symbolRange.startColumn <= definition.range.startColumn) &&
// (symbolRange.endLineNumber !== definition.range.endLineNumber || symbolRange.endColumn >= definition.range.endColumn);
// });
// // if we got to a class/function get the full range and return
// if (symbol?.kind === SymbolKind.Function || symbol?.kind === SymbolKind.Method || symbol?.kind === SymbolKind.Class) {
// return {
// uri: definition.uri,
// selection: {
// startLineNumber: definition.range.startLineNumber,
// startColumn: definition.range.startColumn,
// endLineNumber: definition.range.endLineNumber,
// endColumn: definition.range.endColumn,
// }
// };
// }
// }
// }
// } finally {
// defModelRef.dispose();
// }
}
// // if we got to a class/function get the full range and return
// if (symbol?.kind === SymbolKind.Function || symbol?.kind === SymbolKind.Method || symbol?.kind === SymbolKind.Class) {
// return {
// uri: definition.uri,
// selection: {
// startLineNumber: definition.range.startLineNumber,
// startColumn: definition.range.startColumn,
// endLineNumber: definition.range.endLineNumber,
// endColumn: definition.range.endColumn,
// }
// };
// }
// }
// }
// } finally {
// defModelRef.dispose();
// }
}
}
} finally {
modelRef.object.dispose();
modelRef.dispose();
}
}

View file

@ -1681,6 +1681,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
let nMessagesSent = 0
let currStreamingBlockNum = 0
let aborted = false
let weAreAborting = false
while (shouldSendAnotherMessage) {
shouldSendAnotherMessage = false
nMessagesSent += 1
@ -1739,7 +1740,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
const originalBounds = findTextInCode(block.orig, originalFileCode)
// if error
if (typeof originalBounds === 'string') {
console.log('TEXT NOT FOUND, RETRYING')
const content = errMsgOfInvalidStr(originalBounds, block.orig)
messages.push(
{ role: 'assistant', content: fullText, anthropicReasoning: null }, // latest output
@ -1751,13 +1751,17 @@ class EditCodeService extends Disposable implements IEditCodeService {
latestStreamLocationMutable = null
shouldUpdateOrigStreamStyle = true
blocks.splice(blockNum, Infinity) // remove all blocks at and after this one
oldBlocks = blocks
oldBlocks = deepClone(blocks)
// abort and resolve
shouldSendAnotherMessage = true
if (streamRequestIdRef.current) this._llmMessageService.abort(streamRequestIdRef.current)
if (streamRequestIdRef.current) {
weAreAborting = true
this._llmMessageService.abort(streamRequestIdRef.current)
weAreAborting = false
resMessageDonePromise()
}
this._refreshStylesAndDiffsInURI(uri)
resMessageDonePromise()
return
}
@ -1800,7 +1804,10 @@ class EditCodeService extends Disposable implements IEditCodeService {
// write the added text to the file
if (!latestStreamLocationMutable) continue
const deltaFinalText = block.final.substring((oldBlocks[blockNum]?.final ?? '').length, Infinity)
const oldBlock = oldBlocks[blockNum]
const oldFinalLen = (oldBlock?.final ?? '').length
const deltaFinalText = block.final.substring(oldFinalLen, Infinity)
this._writeStreamedDiffZoneLLMText(uri, block.orig, block.final, deltaFinalText, latestStreamLocationMutable)
oldBlocks = blocks // oldblocks is only used if writingFinal
@ -1857,6 +1864,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
resMessageDonePromise()
},
onAbort: () => {
if (weAreAborting) return
// stop the loop to free up the promise, but don't modify state (already handled by whatever stopped it)
resMessageDonePromise()
aborted = true
@ -1866,7 +1874,9 @@ class EditCodeService extends Disposable implements IEditCodeService {
// should never happen, just for safety
if (streamRequestIdRef.current === null) { break }
console.log('awaiting...')
await messageDonePromise
console.log('done awaiting, aborted=', aborted)
if (aborted) { return }
} // end while
@ -1874,6 +1884,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
} // end retryLoop
retryLoop().then(() => {
console.log('resolving Apply Done')
resApplyDonePromise();
// this._noLongerNeedModelReference(uri)
}).catch((e) => rejApplyDonePromise(e))

View file

@ -273,6 +273,7 @@ export class ToolsService implements IToolsService {
this.callTool = {
read_file: async ({ uri, pageNumber }) => {
await voidModelService.initializeModel(uri)
const { model } = await voidModelService.getModelSafe(uri)
if (model === null) { throw new Error(`Contents were empty. There may have been an error, or the file may not exist.`) }
const readFileContents = model.getValue(EndOfLinePreference.LF)
@ -281,6 +282,7 @@ export class ToolsService implements IToolsService {
const toIdx = MAX_FILE_CHARS_PAGE * pageNumber - 1
const fileContents = readFileContents.slice(fromIdx, toIdx + 1) // paginate
const hasNextPage = (readFileContents.length - 1) - toIdx >= 1
return { fileContents, hasNextPage }
},
@ -290,7 +292,9 @@ export class ToolsService implements IToolsService {
},
pathname_search: async ({ queryStr, pageNumber }) => {
const query = queryBuilder.file(workspaceContextService.getWorkspace().folders.map(f => f.uri), { filePattern: queryStr, })
const query = queryBuilder.file(workspaceContextService.getWorkspace().folders.map(f => f.uri), {
filePattern: queryStr,
})
const data = await searchService.fileSearch(query, CancellationToken.None)
const fromIdx = MAX_CHILDREN_URIs_PAGE * (pageNumber - 1)
@ -304,7 +308,11 @@ export class ToolsService implements IToolsService {
},
search: async ({ queryStr, pageNumber }) => {
const query = queryBuilder.text({ pattern: queryStr, }, workspaceContextService.getWorkspace().folders.map(f => f.uri))
const query = queryBuilder.text({
pattern: queryStr,
isRegExp: true,
}, workspaceContextService.getWorkspace().folders.map(f => f.uri))
const data = await searchService.textSearch(query, CancellationToken.None)
const fromIdx = MAX_CHILDREN_URIs_PAGE * (pageNumber - 1)
@ -322,8 +330,10 @@ export class ToolsService implements IToolsService {
create_uri: async ({ uri, isFolder }) => {
if (isFolder)
await fileService.createFolder(uri)
else
else {
await voidModelService.initializeModel(uri)
await fileService.createFile(uri)
}
return {}
},
@ -333,6 +343,7 @@ export class ToolsService implements IToolsService {
},
edit: async ({ uri, changeDescription }) => {
await voidModelService.initializeModel(uri)
const res = await editCodeService.startApplying({
uri,
applyStr: changeDescription,

View file

@ -71,7 +71,7 @@ export const voidTools = {
search: {
name: 'search',
description: `Returns all code excerpts containing the given string or grep query. This does NOT search pathname. As a follow-up, you may want to use read_file to view the full file contents of the results. ${paginationHelper.desc}`,
description: `Returns pathnames of files with an exact match of the query. The query can be any regex. This does NOT search pathname. As a follow-up, you may want to use read_file to view the full file contents of the results. ${paginationHelper.desc}`,
params: {
query: { type: 'string', description: undefined },
...paginationHelper.param,