mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
checkpoints work! need to add user_temp_modifications now and deal with diffzones
This commit is contained in:
parent
7decc8e146
commit
a94ce5d474
4 changed files with 82 additions and 41 deletions
|
|
@ -70,8 +70,11 @@ const toLLMChatMessages = (chatMessages: ChatMessage[]): LLMChatMessage[] => {
|
|||
llmChatMessages.push({ role: c.role, content: c.content, anthropicReasoning: c.anthropicReasoning })
|
||||
else if (c.role === 'tool')
|
||||
llmChatMessages.push({ role: c.role, id: c.id, name: c.name, params: c.paramsStr, content: c.content })
|
||||
else if (c.role === 'tool_request') {
|
||||
// pass
|
||||
else if (c.role === 'tool_request') { // pass
|
||||
}
|
||||
else if (c.role === 'checkpoint') { // pass
|
||||
}
|
||||
else if (c.role === 'checkpoint_modification') { // pass
|
||||
}
|
||||
else {
|
||||
throw new Error(`Role ${(c as any).role} not recognized.`)
|
||||
|
|
@ -212,6 +215,9 @@ export interface IChatThreadService {
|
|||
// approve/reject
|
||||
approveLatestToolRequest(threadId: string): void;
|
||||
rejectLatestToolRequest(threadId: string): void;
|
||||
|
||||
// jump to history
|
||||
jumpToCheckpointAfterMessageIdx(opts: { threadId: string, messageIdx: number }): void;
|
||||
}
|
||||
|
||||
export const IChatThreadService = createDecorator<IChatThreadService>('voidChatThreadService');
|
||||
|
|
@ -613,6 +619,9 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
this._setStreamState(threadId, { isRunning: 'tool' }, 'merge')
|
||||
let interrupted = false
|
||||
try {
|
||||
// add the original file if it wasn't seen before in this thread
|
||||
if (toolName === 'edit') { this._trackOriginalFileInURI({ threadId, uri: (toolParams as ToolCallParams['edit']).uri }) }
|
||||
|
||||
const { result, interruptTool } = await this._toolsService.callTool[toolName](toolParams as any)
|
||||
this._currentlyRunningToolInterruptor[threadId] = () => {
|
||||
interrupted = true;
|
||||
|
|
@ -620,6 +629,8 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
delete this._currentlyRunningToolInterruptor[threadId];
|
||||
}
|
||||
toolResult = await result // ts is bad... await is needed
|
||||
|
||||
if (toolName === 'edit') { this._addOrUpdateToolEditCheckpoint({ threadId, uri: (toolParams as ToolCallParams['edit']).uri }) }
|
||||
}
|
||||
catch (error) {
|
||||
if (interrupted) {
|
||||
|
|
@ -751,9 +762,28 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
}
|
||||
|
||||
|
||||
private _trackOriginalFileInURI({ threadId, uri }: { threadId: string, uri: URI }) {
|
||||
const thread = this.state.allThreads[threadId]
|
||||
if (!thread) return
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
if (!model) return
|
||||
if (!(uri.fsPath in thread.firstStrOfURI)) {
|
||||
thread.firstStrOfURI[uri.fsPath] = model.getValue()
|
||||
}
|
||||
}
|
||||
|
||||
private _addCheckpoint(threadId: string, checkpoint: CheckpointEntry) {
|
||||
this._addMessageToThread(threadId, checkpoint)
|
||||
// update latest checkpoint idx to the one we just added
|
||||
const newThread = this.state.allThreads[threadId]
|
||||
if (!newThread) return // should never happen
|
||||
const latestCheckpointIdx = newThread.messages.length - 1
|
||||
this._setThreadState(threadId, { latestCheckpointIdx })
|
||||
}
|
||||
|
||||
// merge any LLM checkpoint before this one (and after a user checkpoint if one exists), and add the checkpoint
|
||||
// call this right after LLM edits a file
|
||||
addOrUpdateToolEditCheckpoint({ threadId, uri, }: { threadId: string, uri: URI }) {
|
||||
private _addOrUpdateToolEditCheckpoint({ threadId, uri, }: { threadId: string, uri: URI }) {
|
||||
const thread = this.state.allThreads[threadId]
|
||||
if (!thread) return
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
|
|
@ -777,15 +807,15 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
[uri.fsPath]: afterStr,
|
||||
},
|
||||
}
|
||||
console.log('NEW LLM CHECKPOINT', newLLMCheckpoint, JSON.stringify(this.state.allThreads[threadId], null, 2))
|
||||
this._addMessageToThread(threadId, newLLMCheckpoint)
|
||||
this._addCheckpoint(threadId, newLLMCheckpoint)
|
||||
|
||||
}
|
||||
|
||||
|
||||
// user checkpoints are always computed JIT
|
||||
// we assume there are no messages after the checkpoint we're adding here
|
||||
// call this right before user sends message
|
||||
addOrUpdateUserMessageCheckpoint({ threadId, }: { threadId: string, }) {
|
||||
private _addOrUpdateUserMessageCheckpoint({ threadId, }: { threadId: string, }) {
|
||||
const thread = this.state.allThreads[threadId]
|
||||
if (!thread) return
|
||||
|
||||
|
|
@ -798,9 +828,9 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
// first get the last user checkpoint
|
||||
const lastNonUserCheckpointIdx = findLastIdx(thread.messages, (m) => m.role === 'checkpoint' && m.type !== 'after_user_edits')
|
||||
|
||||
// merge all recent user checkpoints
|
||||
// merge all recent user checkpoints and delete them
|
||||
const latestAfterStrOfURI: { [fsPath: string]: string } = {} // helps merge user edits
|
||||
for (let k = 0; k <= thread.messages.length; k += 1) {
|
||||
for (let k = 0; k < thread.messages.length; k += 1) {
|
||||
const message = thread.messages[k]
|
||||
if (message.role !== 'checkpoint') continue
|
||||
for (const uri in message.afterStrOfURI)
|
||||
|
|
@ -811,32 +841,23 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
this._removeMessageFromThread(threadId, k)
|
||||
}
|
||||
|
||||
// compute afterStr of all files we detected, and if they're different, add them as a user edit
|
||||
for (const fsPath in latestAfterStrOfURI) {
|
||||
const uri = URI.file(fsPath)
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
|
||||
// add a change for all the files where we detect a user change
|
||||
const allURIs = this._getAllChangedCheckpointURIs({ threadId, loIdx: 0, hiIdx: thread.messages.length - 1, })
|
||||
for (const fsPath of allURIs ?? []) {
|
||||
const { model } = this._voidModelService.getModelFromFsPath(fsPath)
|
||||
if (!model) continue
|
||||
const oldAfterStr = latestAfterStrOfURI[uri.fsPath]
|
||||
const oldAfterStr = latestAfterStrOfURI[fsPath]
|
||||
const currentAfterStr = model.getValue()
|
||||
if (oldAfterStr === currentAfterStr) continue
|
||||
// if there was a change, add it as a user edit
|
||||
newUserCheckpoint.afterStrOfURI = {
|
||||
...newUserCheckpoint.afterStrOfURI,
|
||||
[uri.fsPath]: currentAfterStr
|
||||
[fsPath]: currentAfterStr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this._addMessageToThread(threadId, newUserCheckpoint)
|
||||
|
||||
// update latest checkpoint idx to the one we just added
|
||||
const newThread = this.state.allThreads[threadId]
|
||||
if (!newThread) return // should never happen
|
||||
const latestCheckpointIdx = newThread.messages.length - 1
|
||||
this._setThreadState(threadId, { latestCheckpointIdx })
|
||||
|
||||
|
||||
console.log('NEW USER CHECKPOINT', latestCheckpointIdx, newUserCheckpoint, JSON.stringify(this.state.allThreads[threadId], null, 2))
|
||||
this._addCheckpoint(threadId, newUserCheckpoint)
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -852,11 +873,11 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
return undefined
|
||||
}
|
||||
|
||||
private _getAllChangedCheckpointURIs({ threadId, fromIdx, toIdx }: { threadId: string, fromIdx: number, toIdx: number }) {
|
||||
private _getAllChangedCheckpointURIs({ threadId, loIdx, hiIdx }: { threadId: string, loIdx: number, hiIdx: number }) {
|
||||
const thread = this.state.allThreads[threadId]
|
||||
if (!thread) return null // should never happen
|
||||
const fsPaths: Set<string> = new Set()
|
||||
for (let i = fromIdx; i <= toIdx; i += 1) {
|
||||
for (let i = loIdx; i <= hiIdx; i += 1) {
|
||||
const message = thread.messages[i]
|
||||
if (message.role !== 'checkpoint') continue
|
||||
for (const fsPath in message.afterStrOfURI) {
|
||||
|
|
@ -889,6 +910,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
text
|
||||
}])
|
||||
}
|
||||
console.log(`going from ${fromIdx} to ${toIdx}`)
|
||||
|
||||
/*
|
||||
if undoing
|
||||
|
|
@ -912,7 +934,7 @@ We need to revert anything that happened between to+1 and from.
|
|||
We only need to do it for files that were edited since `to`, ie files between to+1...from.
|
||||
*/
|
||||
if (toIdx < fromIdx) {
|
||||
const checkpointURIs = this._getAllChangedCheckpointURIs({ threadId, toIdx: toIdx + 1, fromIdx })
|
||||
const checkpointURIs = this._getAllChangedCheckpointURIs({ threadId, loIdx: toIdx + 1, hiIdx: fromIdx })
|
||||
for (const fsPath of checkpointURIs ?? []) {
|
||||
let found = false
|
||||
|
||||
|
|
@ -953,7 +975,7 @@ We need to apply latest change for anything that happened between from+1 and to.
|
|||
We only need to do it for files that were edited since `from`, ie files between from+1...to.
|
||||
*/
|
||||
if (toIdx > fromIdx) {
|
||||
const checkpointURIs = this._getAllChangedCheckpointURIs({ threadId, fromIdx: fromIdx + 1, toIdx })
|
||||
const checkpointURIs = this._getAllChangedCheckpointURIs({ threadId, loIdx: fromIdx + 1, hiIdx: toIdx })
|
||||
for (const fsPath of checkpointURIs ?? []) {
|
||||
// apply lowest down content for each uri
|
||||
// (do not need to apply original since we're only applying to files that changed)
|
||||
|
|
@ -991,8 +1013,8 @@ We only need to do it for files that were edited since `from`, ie files between
|
|||
const userMessageContent = await chat_userMessageContent(instructions, currSelns) // user message + names of files (NOT content)
|
||||
const userHistoryElt: ChatMessage = { role: 'user', content: userMessageContent, displayContent: instructions, selections: currSelns, state: defaultMessageState }
|
||||
this._addMessageToThread(threadId, userHistoryElt)
|
||||
this._addOrUpdateUserMessageCheckpoint({ threadId })
|
||||
|
||||
this.addOrUpdateUserMessageCheckpoint({ threadId })
|
||||
this._runChatAgent({ prevSelns, currSelns, threadId, userMessageContent, ...this._currentModelSelectionProps(), })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1757,14 +1757,15 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
return
|
||||
}
|
||||
|
||||
console.log('---------adding-------')
|
||||
console.log('CURRENT TEXT!!!', { current: model?.getValue() })
|
||||
console.log('block', deepClone(block))
|
||||
console.log('origBounds', originalBounds)
|
||||
|
||||
|
||||
const [startLine, endLine] = convertOriginalRangeToFinalRange(originalBounds)
|
||||
console.log('start end', startLine, endLine)
|
||||
|
||||
// console.log('---------adding-------')
|
||||
// console.log('CURRENT TEXT!!!', { current: model?.getValue() })
|
||||
// console.log('block', deepClone(block))
|
||||
// console.log('origBounds', originalBounds)
|
||||
// console.log('start end', startLine, endLine)
|
||||
|
||||
// otherwise if no error, add the position as a diffarea
|
||||
const adding: Omit<TrackingZone<SearchReplaceDiffAreaMetadata>, 'diffareaid'> = {
|
||||
|
|
@ -1821,7 +1822,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
onFinalMessage: async (params) => {
|
||||
const { fullText } = params
|
||||
|
||||
console.log('DONE - editCode!', { fullText })
|
||||
|
||||
// 1. wait 500ms and fix lint errors - call lint error workflow
|
||||
// (update react state to say "Fixing errors")
|
||||
|
|
@ -1836,10 +1836,11 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
// IMPORTANT - sort by lineNum
|
||||
addedTrackingZoneOfBlockNum.sort((a, b) => a.metadata.originalBounds[0] - b.metadata.originalBounds[0])
|
||||
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
console.log('CURRENT TEXT!!!', { current: model?.getValue() })
|
||||
console.log('addedTrackingZoneOfBlockNum', addedTrackingZoneOfBlockNum)
|
||||
console.log('blocks', deepClone(blocks))
|
||||
// const { model } = this._voidModelService.getModel(uri)
|
||||
// console.log('DONE - editCode!', { fullText })
|
||||
// console.log('CURRENT TEXT!!!', { current: model?.getValue() })
|
||||
// console.log('addedTrackingZoneOfBlockNum', addedTrackingZoneOfBlockNum)
|
||||
// console.log('blocks', deepClone(blocks))
|
||||
|
||||
for (let blockNum = addedTrackingZoneOfBlockNum.length - 1; blockNum >= 0; blockNum -= 1) {
|
||||
const { originalBounds } = addedTrackingZoneOfBlockNum[blockNum].metadata
|
||||
|
|
|
|||
|
|
@ -1811,6 +1811,18 @@ const toolNameToComponent: { [T in ToolName]: ToolComponent<T> } = {
|
|||
};
|
||||
|
||||
|
||||
const Checkpoint = ({ threadId, messageIdx }: { threadId: string; messageIdx: number }) => {
|
||||
const accessor = useAccessor()
|
||||
const chatThreadService = accessor.get('IChatThreadService')
|
||||
|
||||
return <button onClick={() => {
|
||||
chatThreadService.jumpToCheckpointAfterMessageIdx({ threadId, messageIdx })
|
||||
}}>
|
||||
jump
|
||||
</button>
|
||||
|
||||
}
|
||||
|
||||
type ChatBubbleMode = 'display' | 'edit'
|
||||
type ChatBubbleProps = {
|
||||
chatMessage: ChatMessage,
|
||||
|
|
@ -1866,6 +1878,13 @@ const ChatBubble = ({ chatMessage, isCommitted, messageIdx, isLast, chatIsRunnin
|
|||
return null
|
||||
}
|
||||
|
||||
else if (role === 'checkpoint') {
|
||||
return <Checkpoint threadId={threadId} messageIdx={messageIdx} />
|
||||
}
|
||||
|
||||
else if (role === 'checkpoint_modification') {
|
||||
return <Checkpoint threadId={threadId} messageIdx={messageIdx} />
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -399,7 +399,6 @@ export class ToolsService implements IToolsService {
|
|||
return `URI ${params.uri.fsPath} successfully deleted.`
|
||||
},
|
||||
edit: (params, result) => {
|
||||
console.log('STR OF RESULT', params)
|
||||
return `Change successfully made to ${params.uri.fsPath}.`
|
||||
},
|
||||
terminal_command: (params, result) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue