mirror of
https://github.com/voideditor/void
synced 2026-05-24 01:48:25 +00:00
fix running_tool state and double undo on click stop in Apply
This commit is contained in:
parent
78671db5b8
commit
ea65903b3f
6 changed files with 41 additions and 52 deletions
|
|
@ -420,7 +420,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
}
|
||||
return false
|
||||
}
|
||||
private _updateLatestToolTo = (threadId: string, tool: ChatMessage & { role: 'tool' }) => {
|
||||
private _updateLatestTool = (threadId: string, tool: ChatMessage & { role: 'tool' }) => {
|
||||
const swapped = this._swapOutLatestStreamingToolWithResult(threadId, tool)
|
||||
if (swapped) return
|
||||
this._addMessageToThread(threadId, tool)
|
||||
|
|
@ -430,7 +430,6 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
const thread = this.state.allThreads[threadId]
|
||||
if (!thread) return // should never happen
|
||||
|
||||
|
||||
const lastMsg = thread.messages[thread.messages.length - 1]
|
||||
if (!(
|
||||
lastMsg.role === 'tool' && (lastMsg.type === 'tool_request')
|
||||
|
|
@ -438,15 +437,6 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
const callThisToolFirst: ToolMessage<ToolName> = lastMsg
|
||||
|
||||
this._updateLatestToolTo(threadId, {
|
||||
role: 'tool',
|
||||
type: 'running_now',
|
||||
name: lastMsg.name,
|
||||
params: lastMsg.params,
|
||||
content: '(value not received yet...)', // this typically shouldn't ever get read
|
||||
result: null
|
||||
})
|
||||
|
||||
this._wrapRunAgentToNotify(
|
||||
this._runChatAgent({ callThisToolFirst, threadId, ...this._currentModelSelectionProps() })
|
||||
, threadId
|
||||
|
|
@ -467,7 +457,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
const { name } = lastMsg
|
||||
|
||||
const errorMessage = this.errMsgs.rejected
|
||||
this._updateLatestToolTo(threadId, { role: 'tool', type: 'rejected', params: params, name: name, content: errorMessage, result: null })
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', params: params, name: name, content: errorMessage, result: null })
|
||||
this._setStreamState(threadId, {}, 'set')
|
||||
}
|
||||
|
||||
|
|
@ -588,8 +578,6 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
if (!opts.preapproved) { // skip this if pre-approved
|
||||
// 1. validate tool params
|
||||
try {
|
||||
console.log('VALIDATING PARAMS!!!', opts.unvalidatedToolParams)
|
||||
|
||||
const params = await this._toolsService.validateParams[toolName](opts.unvalidatedToolParams)
|
||||
toolParams = params
|
||||
} catch (error) {
|
||||
|
|
@ -601,8 +589,8 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
if (toolName === 'edit_file') { this._addToolEditCheckpoint({ threadId, uri: (toolParams as ToolCallParams['edit_file']).uri }) }
|
||||
|
||||
// 2. if tool requires approval, break from the loop, awaiting approval
|
||||
const requiresApproval = toolNamesThatRequireApproval.has(toolName)
|
||||
if (requiresApproval) {
|
||||
const toolRequiresApproval = toolNamesThatRequireApproval.has(toolName)
|
||||
if (toolRequiresApproval) {
|
||||
const autoApprove = this._settingsService.state.globalSettings.autoApprove
|
||||
// add a tool_request because we use it for UI if a tool is loading (this should be improved in the future)
|
||||
this._addMessageToThread(threadId, { role: 'tool', type: 'tool_request', content: '(never)', result: null, name: toolName, params: toolParams })
|
||||
|
|
@ -617,6 +605,8 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
// 3. call the tool
|
||||
this._setStreamState(threadId, { isRunning: 'tool' }, 'merge')
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'running_now', name: toolName, params: toolParams, content: '(value not received yet...)', result: null })
|
||||
|
||||
let interrupted = false
|
||||
try {
|
||||
const { result, interruptTool } = await this._toolsService.callTool[toolName](toolParams as any)
|
||||
|
|
@ -633,7 +623,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
return { interrupted: true }
|
||||
}
|
||||
const errorMessage = getErrorMessage(error)
|
||||
this._updateLatestToolTo(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, })
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, })
|
||||
return {}
|
||||
}
|
||||
|
||||
|
|
@ -642,12 +632,12 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any)
|
||||
} catch (error) {
|
||||
const errorMessage = this.errMsgs.errWhenStringifying(error)
|
||||
this._updateLatestToolTo(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, })
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, })
|
||||
return {}
|
||||
}
|
||||
|
||||
// 5. add to history and keep going
|
||||
this._updateLatestToolTo(threadId, { role: 'tool', type: 'success', params: toolParams, result: toolResult, name: toolName, content: toolResultStr, })
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'success', params: toolParams, result: toolResult, name: toolName, content: toolResultStr, })
|
||||
|
||||
return {}
|
||||
};
|
||||
|
|
@ -704,7 +694,6 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
...llmMessages
|
||||
]
|
||||
|
||||
console.log('SENDING!!', messages)
|
||||
const llmCancelToken = this._llmMessageService.sendLLMMessage({
|
||||
messagesType: 'chatMessages',
|
||||
chatMode,
|
||||
|
|
@ -718,7 +707,6 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
onFinalMessage: async ({ fullText, fullReasoning, toolCall, anthropicReasoning, }) => {
|
||||
this._addMessageToThread(threadId, { role: 'assistant', displayContent: fullText, reasoning: fullReasoning, toolCall, anthropicReasoning })
|
||||
this._setStreamState(threadId, { displayContentSoFar: undefined, reasoningSoFar: undefined, streamingToken: undefined, toolCallSoFar: undefined }, 'merge')
|
||||
console.log('tool call!!', JSON.stringify(toolCall))
|
||||
resMessageIsDonePromise(toolCall) // resolve with tool calls
|
||||
},
|
||||
onError: (error) => {
|
||||
|
|
|
|||
|
|
@ -303,7 +303,6 @@ class DirectoryStrService extends Disposable implements IDirectoryStrService {
|
|||
|
||||
// Use our new approach with direct explorer service
|
||||
const dirTree = await computeDirectoryTree(eRoot, this.explorerService);
|
||||
console.log('dirtree', dirTree)
|
||||
const { content, wasCutOff } = stringifyDirectoryTree(dirTree, MAX_DIRSTR_CHARS_TOTAL_BEGINNING - str.length);
|
||||
str += content;
|
||||
if (wasCutOff) {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ import { IEditCodeService, AddCtrlKOpts, StartApplyingOpts, CallBeforeStartApply
|
|||
import { IVoidSettingsService } from '../common/voidSettingsService.js';
|
||||
import { FeatureName } from '../common/voidSettingsTypes.js';
|
||||
import { IVoidModelService } from '../common/voidModelService.js';
|
||||
import { ITextFileService } from '../../../services/textfile/common/textfiles.js';
|
||||
import { deepClone } from '../../../../base/common/objects.js';
|
||||
import { acceptBg, acceptBorder, buttonFontSize, buttonTextColor, rejectBg, rejectBorder } from '../common/helpers/colors.js';
|
||||
import { DiffArea, Diff, CtrlKZone, VoidFileSnapshot, DiffAreaSnapshotEntry, diffAreaSnapshotKeys, DiffZone, TrackingZone, ComputedDiff } from '../common/editCodeServiceTypes.js';
|
||||
|
|
@ -190,7 +189,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
@IVoidSettingsService private readonly _settingsService: IVoidSettingsService,
|
||||
// @IFileService private readonly _fileService: IFileService,
|
||||
@IVoidModelService private readonly _voidModelService: IVoidModelService,
|
||||
@ITextFileService private readonly _textFileService: ITextFileService,
|
||||
) {
|
||||
super();
|
||||
|
||||
|
|
@ -720,16 +718,14 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
resource: uri,
|
||||
label: 'Void Agent',
|
||||
code: 'undoredo.editCode',
|
||||
undo: () => { opts?.onWillUndo?.(); this._restoreVoidFileSnapshot(uri, beforeSnapshot); },
|
||||
redo: () => { if (afterSnapshot) this._restoreVoidFileSnapshot(uri, afterSnapshot) }
|
||||
undo: async () => { opts?.onWillUndo?.(); await this._restoreVoidFileSnapshot(uri, beforeSnapshot) },
|
||||
redo: async () => { if (afterSnapshot) await this._restoreVoidFileSnapshot(uri, afterSnapshot) }
|
||||
}
|
||||
this._undoRedoService.pushElement(elt)
|
||||
|
||||
const onFinishEdit = async () => {
|
||||
afterSnapshot = this._getCurrentVoidFileSnapshot(uri)
|
||||
await this._textFileService.save(uri, { // we want [our change] -> [save] so it's all treated as one change.
|
||||
skipSaveParticipants: true // avoid triggering extensions etc (if they reformat the page, it will add another item to the undo stack)
|
||||
})
|
||||
await this._voidModelService.saveModel(uri)
|
||||
}
|
||||
return { onFinishEdit }
|
||||
}
|
||||
|
|
@ -1105,6 +1101,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
const uri = this._getURIBeforeStartApplying(opts)
|
||||
if (!uri) return
|
||||
await this._voidModelService.initializeModel(uri)
|
||||
await this._voidModelService.saveModel(uri) // save the URI
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1878,6 +1875,8 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
|
||||
|
||||
interruptURIStreaming({ uri }: { uri: URI }) {
|
||||
if (!this._uriIsStreaming(uri)) return
|
||||
this._undoHistory(uri)
|
||||
// brute force for now is OK
|
||||
for (const diffareaid of this.diffAreasOfURI[uri.fsPath] || []) {
|
||||
const diffArea = this.diffAreaOfId[diffareaid]
|
||||
|
|
@ -1885,7 +1884,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
if (!diffArea._streamState.isStreaming) continue
|
||||
this._stopIfStreaming(diffArea)
|
||||
}
|
||||
this._undoHistory(uri)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -216,7 +216,10 @@ export const ApplyButtonsHTML = ({ codeStr, applyBoxId, reapplyIcon, uri }: { co
|
|||
const [newApplyingUri, applyDonePromise] = editCodeService.startApplying(opts) ?? []
|
||||
|
||||
// catch any errors by interrupting the stream
|
||||
applyDonePromise?.catch(e => { if (newApplyingUri) editCodeService.interruptURIStreaming({ uri: newApplyingUri }) })
|
||||
applyDonePromise?.catch(e => {
|
||||
const uri = getUriBeingApplied(applyBoxId)
|
||||
if (uri) editCodeService.interruptURIStreaming({ uri: uri })
|
||||
})
|
||||
|
||||
applyingURIOfApplyBoxIdRef.current[applyBoxId] = newApplyingUri ?? undefined
|
||||
|
||||
|
|
|
|||
|
|
@ -773,7 +773,7 @@ const SimplifiedToolHeader = ({
|
|||
|
||||
|
||||
|
||||
const UserMessageComponent = ({ chatMessage, messageIdx, isCheckpointGhost, _scrollToBottom }: { chatMessage: ChatMessage & { role: 'user' }, messageIdx: number, isCheckpointGhost: boolean, _scrollToBottom: (() => void) | null }) => {
|
||||
const UserMessageComponent = ({ chatMessage, messageIdx, isCheckpointGhost, currCheckpointIdx, _scrollToBottom }: { chatMessage: ChatMessage & { role: 'user' }, messageIdx: number, currCheckpointIdx: number | undefined, isCheckpointGhost: boolean, _scrollToBottom: (() => void) | null }) => {
|
||||
|
||||
const accessor = useAccessor()
|
||||
const chatThreadsService = accessor.get('IChatThreadService')
|
||||
|
|
@ -924,7 +924,7 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isCheckpointGhost, _scr
|
|||
</VoidChatArea>
|
||||
}
|
||||
|
||||
|
||||
const isMsgAfterCheckpoint = currCheckpointIdx !== undefined && currCheckpointIdx === messageIdx - 1
|
||||
|
||||
return <div
|
||||
// align chatbubble accoridng to role
|
||||
|
|
@ -934,7 +934,7 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isCheckpointGhost, _scr
|
|||
: mode === 'display' ? `self-end w-fit max-w-full whitespace-pre-wrap` : '' // user words should be pre
|
||||
}
|
||||
|
||||
${isCheckpointGhost ? 'opacity-50 pointer-events-none' : ''}
|
||||
${isCheckpointGhost && !isMsgAfterCheckpoint ? 'opacity-50 pointer-events-none' : ''}
|
||||
`}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
|
|
@ -1850,19 +1850,20 @@ type ChatBubbleProps = {
|
|||
isCommitted: boolean,
|
||||
chatIsRunning: IsRunningType,
|
||||
threadId: string,
|
||||
currCheckpointIdx: number,
|
||||
currCheckpointIdx: number | undefined,
|
||||
_scrollToBottom: (() => void) | null,
|
||||
}
|
||||
|
||||
const ChatBubble = ({ threadId, chatMessage, currCheckpointIdx, isCommitted, messageIdx, chatIsRunning, _scrollToBottom }: ChatBubbleProps) => {
|
||||
const role = chatMessage.role
|
||||
|
||||
const isCheckpointGhost = messageIdx > currCheckpointIdx && !chatIsRunning // whether to show as gray (if chat is running, for good measure just dont show any ghosts)
|
||||
const isCheckpointGhost = messageIdx > (currCheckpointIdx ?? Infinity) && !chatIsRunning // whether to show as gray (if chat is running, for good measure just dont show any ghosts)
|
||||
|
||||
if (role === 'user') {
|
||||
return <UserMessageComponent
|
||||
chatMessage={chatMessage}
|
||||
isCheckpointGhost={isCheckpointGhost}
|
||||
currCheckpointIdx={currCheckpointIdx}
|
||||
messageIdx={messageIdx}
|
||||
_scrollToBottom={_scrollToBottom}
|
||||
/>
|
||||
|
|
@ -2015,7 +2016,8 @@ export const SidebarChat = () => {
|
|||
const toolCallSoFar = currThreadStreamState?.toolCallSoFar
|
||||
const reasoningSoFar = currThreadStreamState?.reasoningSoFar
|
||||
|
||||
const toolIsGenerating = !!toolCallSoFar && toolCallSoFar.name === 'edit_file' // show loading for slow tools (right now just edit)
|
||||
// this is just if it's currently being generated, NOT if it's currently running
|
||||
const toolIsGenerating = toolCallSoFar && !toolCallSoFar.isDone && toolCallSoFar.name === 'edit_file' // show loading for slow tools (right now just edit)
|
||||
|
||||
// ----- SIDEBAR CHAT state (local) -----
|
||||
|
||||
|
|
@ -2067,11 +2069,10 @@ export const SidebarChat = () => {
|
|||
|
||||
|
||||
const threadId = currentThread.id
|
||||
const currCheckpointIdx = chatThreadsState.allThreads[threadId]?.state?.currCheckpointIdx ?? Infinity // if not exist, treat like checkpoint is last message (infinity)
|
||||
const currCheckpointIdx = chatThreadsState.allThreads[threadId]?.state?.currCheckpointIdx ?? undefined // if not exist, treat like checkpoint is last message (infinity)
|
||||
|
||||
const previousMessagesHTML = useMemo(() => {
|
||||
const lastMessageIdx = previousMessages.findLastIndex(v => v.role !== 'checkpoint')
|
||||
|
||||
// tool request shows up as Editing... if in progress
|
||||
return previousMessages.map((message, i) => {
|
||||
return <ChatBubble
|
||||
|
|
@ -2085,13 +2086,13 @@ export const SidebarChat = () => {
|
|||
_scrollToBottom={() => scrollToBottom(scrollContainerRef)}
|
||||
/>
|
||||
})
|
||||
}, [previousMessages, isRunning, threadId])
|
||||
}, [previousMessages, threadId, currCheckpointIdx, isRunning])
|
||||
|
||||
const streamingChatIdx = previousMessagesHTML.length
|
||||
const currStreamingMessageHTML = reasoningSoFar || displayContentSoFar || isRunning ?
|
||||
<ChatBubble
|
||||
key={getChatBubbleId(threadId, streamingChatIdx)}
|
||||
currCheckpointIdx={currCheckpointIdx} // if streaming, can't be the case
|
||||
currCheckpointIdx={currCheckpointIdx}
|
||||
chatMessage={{
|
||||
role: 'assistant',
|
||||
displayContent: displayContentSoFar ?? '',
|
||||
|
|
@ -2108,8 +2109,6 @@ export const SidebarChat = () => {
|
|||
/> : null
|
||||
|
||||
|
||||
const generatingToolTitle = toolCallSoFar && toolNames.includes(toolCallSoFar.name as ToolName) ? titleOfToolName[toolCallSoFar.name as ToolName]?.proposed : toolCallSoFar?.name
|
||||
|
||||
const messagesHTML = <ScrollToBottomContainer
|
||||
key={'messages' + chatThreadsState.currentThreadId} // force rerender on all children if id changes
|
||||
scrollContainerRef={scrollContainerRef}
|
||||
|
|
@ -2124,13 +2123,15 @@ export const SidebarChat = () => {
|
|||
>
|
||||
{/* previous messages */}
|
||||
{previousMessagesHTML}
|
||||
|
||||
|
||||
{currStreamingMessageHTML}
|
||||
|
||||
|
||||
{toolIsGenerating ?
|
||||
<ToolHeaderWrapper key={getChatBubbleId(currentThread.id, streamingChatIdx + 1)} title={generatingToolTitle} desc1={<span className='flex items-center'>Generating<IconLoading /></span>} />
|
||||
<ToolHeaderWrapper key={getChatBubbleId(currentThread.id, streamingChatIdx + 1)}
|
||||
title={toolCallSoFar && toolNames.includes(toolCallSoFar.name as ToolName) ?
|
||||
titleOfToolName[toolCallSoFar.name as ToolName]?.proposed
|
||||
: toolCallSoFar?.name}
|
||||
desc1={<span className='flex items-center'>Generating<IconLoading /></span>}
|
||||
/>
|
||||
: null}
|
||||
|
||||
{isRunning === 'LLM' && !toolIsGenerating ? <ProseWrapper>
|
||||
|
|
|
|||
|
|
@ -339,11 +339,11 @@ ${details.map((d, i) => `${i + 1}. ${d}`).join('\n\n')}`)
|
|||
}
|
||||
|
||||
|
||||
// log all prompts
|
||||
for (const chatMode of ['agent', 'gather', 'normal'] satisfies ChatMode[]) {
|
||||
console.log(`========================================= SYSTEM MESSAGE FOR ${chatMode} ===================================\n`,
|
||||
chat_systemMessage({ chatMode, workspaceFolders: [], openedURIs: [], activeURI: 'pee', runningTerminalIds: [], directoryStr: 'lol', }))
|
||||
}
|
||||
// // log all prompts
|
||||
// for (const chatMode of ['agent', 'gather', 'normal'] satisfies ChatMode[]) {
|
||||
// console.log(`========================================= SYSTEM MESSAGE FOR ${chatMode} ===================================\n`,
|
||||
// chat_systemMessage({ chatMode, workspaceFolders: [], openedURIs: [], activeURI: 'pee', runningTerminalIds: [], directoryStr: 'lol', }))
|
||||
// }
|
||||
|
||||
|
||||
export const chat_userMessageContent = async (instructions: string, currSelns: StagingSelectionItem[] | null,
|
||||
|
|
|
|||
Loading…
Reference in a new issue