mirror of
https://github.com/voideditor/void
synced 2026-05-23 09:28:23 +00:00
Merge pull request #401 from voideditor/model-selection
Model selection
This commit is contained in:
commit
a0fa104d23
11 changed files with 94 additions and 67 deletions
|
|
@ -34,7 +34,7 @@
|
|||
// // const result = await new Promise((res, rej) => {
|
||||
// // sendLLMMessage({
|
||||
// // messages,
|
||||
// // tools: ['search_files'],
|
||||
// // tools: ['search_for_files'],
|
||||
// // onFinalMessage: ({ result: r, }) => {
|
||||
// // res(r)
|
||||
// // },
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
// // const result = new Promise((res, rej) => {
|
||||
// // sendLLMMessage({
|
||||
// // messages,
|
||||
// // tools: ['search_files'],
|
||||
// // tools: ['search_for_files'],
|
||||
// // onResult: (r) => {
|
||||
// // res(r)
|
||||
// // }
|
||||
|
|
|
|||
|
|
@ -417,7 +417,6 @@ class DirectoryStrService extends Disposable implements IDirectoryStrService {
|
|||
break;
|
||||
}
|
||||
}
|
||||
console.log('cutoff!!!!!!!', str, cutOffMessage)
|
||||
|
||||
if (cutOff) {
|
||||
return `${str}\n${cutOffMessage}`
|
||||
|
|
|
|||
|
|
@ -1235,7 +1235,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
_removeStylesFns: new Set(),
|
||||
}
|
||||
|
||||
console.log('FIRING START STREAMING IN DIFFZONE!!!')
|
||||
const diffZone = this._addDiffArea(adding)
|
||||
this._onDidChangeStreamingInDiffZone.fire({ uri, diffareaid: diffZone.diffareaid })
|
||||
this._onDidAddOrDeleteDiffZones.fire({ uri })
|
||||
|
|
|
|||
|
|
@ -162,7 +162,6 @@ export const useApplyButtonState = ({ applyBoxId, uri }: { applyBoxId: string, u
|
|||
)
|
||||
if (shouldUpdate) {
|
||||
rerender(c => c + 1)
|
||||
console.log('rerendering....')
|
||||
}
|
||||
}, [applyBoxId, uri]))
|
||||
|
||||
|
|
@ -322,12 +321,12 @@ export const ApplyButtonsHTML = ({ codeStr, applyBoxId, reapplyIcon, uri }: { co
|
|||
<IconShell1
|
||||
Icon={X}
|
||||
onClick={onReject}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Reject file' })}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Remove' })}
|
||||
/>
|
||||
<IconShell1
|
||||
Icon={Check}
|
||||
onClick={onAccept}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Accept file' })}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Keep' })}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,14 +20,13 @@ import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js';
|
|||
import { ChatMode, FeatureName, isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js';
|
||||
import { WarningBox } from '../void-settings-tsx/WarningBox.js';
|
||||
import { getModelCapabilities, getIsReasoningEnabledState } from '../../../../common/modelCapabilities.js';
|
||||
import { AlertTriangle, Ban, Check, ChevronRight, Dot, FileIcon, Pencil, Undo, Undo2, X } from 'lucide-react';
|
||||
import { AlertTriangle, Ban, Check, ChevronRight, Dot, FileIcon, Pencil, Undo, Undo2, X, Flag } from 'lucide-react';
|
||||
import { ChatMessage, CheckpointEntry, StagingSelectionItem, ToolMessage } from '../../../../common/chatThreadServiceTypes.js';
|
||||
import { LintErrorItem, ToolCallParams, ToolNameWithApproval } from '../../../../common/toolsServiceTypes.js';
|
||||
import { ApplyButtonsHTML, CopyButton, IconShell1, JumpToFileButton, JumpToTerminalButton, StatusIndicator, StatusIndicatorForApplyButton, useApplyButtonState } from '../markdown/ApplyBlockHoverButtons.js';
|
||||
import { IsRunningType } from '../../../chatThreadService.js';
|
||||
import { acceptAllBg, acceptBorder, buttonFontSize, buttonTextColor, rejectAllBg, rejectBg, rejectBorder } from '../../../../common/helpers/colors.js';
|
||||
import { ToolName, toolNames } from '../../../../common/prompt/prompts.js';
|
||||
import { error } from 'console';
|
||||
import { RawToolCallObj } from '../../../../common/sendLLMMessageTypes.js';
|
||||
import { MAX_FILE_CHARS_PAGE } from '../../../toolsService.js';
|
||||
|
||||
|
|
@ -1173,7 +1172,7 @@ const titleOfToolName = {
|
|||
'ls_dir': { done: 'Inspected folder', proposed: 'Inspect folder', running: loadingTitleWrapper('Inspecting folder') },
|
||||
'get_dir_structure': { done: 'Inspected folder', proposed: 'Inspect folder', running: loadingTitleWrapper('Inspecting folder') },
|
||||
'search_pathnames_only': { done: 'Searched by file name', proposed: 'Search by file name', running: loadingTitleWrapper('Searching by file name') },
|
||||
'search_files': { done: 'Searched', proposed: 'Search', running: loadingTitleWrapper('Searching') },
|
||||
'search_for_files': { done: 'Searched', proposed: 'Search', running: loadingTitleWrapper('Searching') },
|
||||
'create_file_or_folder': { done: `Created`, proposed: `Create`, running: loadingTitleWrapper(`Creating`) },
|
||||
'delete_file_or_folder': { done: `Deleted`, proposed: `Delete`, running: loadingTitleWrapper(`Deleting`) },
|
||||
'edit_file': { done: `Edited file`, proposed: 'Edit file', running: loadingTitleWrapper('Editing file') },
|
||||
|
|
@ -1207,8 +1206,8 @@ const toolNameToDesc = (toolName: ToolName, _toolParams: ToolCallParams[ToolName
|
|||
} else if (toolName === 'search_pathnames_only') {
|
||||
const toolParams = _toolParams as ToolCallParams['search_pathnames_only']
|
||||
return `"${toolParams.queryStr}"`;
|
||||
} else if (toolName === 'search_files') {
|
||||
const toolParams = _toolParams as ToolCallParams['search_files']
|
||||
} else if (toolName === 'search_for_files') {
|
||||
const toolParams = _toolParams as ToolCallParams['search_for_files']
|
||||
return `"${toolParams.queryStr}"`;
|
||||
} else if (toolName === 'create_file_or_folder') {
|
||||
const toolParams = _toolParams as ToolCallParams['create_file_or_folder']
|
||||
|
|
@ -1355,21 +1354,31 @@ const LintErrorChildren = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => {
|
|||
}
|
||||
|
||||
const EditToolLintErrors = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => {
|
||||
|
||||
if (lintErrors.length === 0) return null;
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="w-full px-2">
|
||||
<ToolHeaderWrapper className='!border-t-0' title={'Lint errors'} desc1={''} isOpen={isOpen} onClick={() => { setIsOpen(o => !o) }} >
|
||||
<LintErrorChildren lintErrors={lintErrors} />
|
||||
</ToolHeaderWrapper>
|
||||
|
||||
<div className="w-full px-2 mt-0.5">
|
||||
<div
|
||||
className={`flex items-center cursor-pointer select-none transition-colors duration-150 pl-0 py-0.5 rounded group`}
|
||||
onClick={() => setIsOpen(o => !o)}
|
||||
style={{ background: 'none' }}
|
||||
>
|
||||
<ChevronRight
|
||||
className={`mr-1 h-4 w-4 flex-shrink-0 transition-transform duration-100 text-void-fg-4 group-hover:text-void-fg-3 ${isOpen ? 'rotate-90' : ''}`}
|
||||
/>
|
||||
<span className="font-medium text-void-fg-4 group-hover:text-void-fg-3 text-xs">Lint errors</span>
|
||||
</div>
|
||||
<div
|
||||
className={`overflow-hidden transition-all duration-200 ease-in-out ${isOpen ? 'opacity-100 py-1' : 'max-h-0 opacity-0'} text-xs pl-5`}
|
||||
>
|
||||
<div className="flex flex-col gap-0.5 overflow-x-auto whitespace-nowrap text-void-fg-4 opacity-90 border-l-2 border-void-warning px-2 py-0.5">
|
||||
{lintErrors.map((error, i) => (
|
||||
<div key={i} className="">Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1445,7 +1454,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
|||
const { params, result } = toolMessage
|
||||
componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }
|
||||
if (result.hasNextPage && params.pageNumber === 1) // first page
|
||||
componentParams.desc2 = `(first ${Math.round(MAX_FILE_CHARS_PAGE) / 1000}k)`
|
||||
componentParams.desc2 = `(truncated after ${Math.round(MAX_FILE_CHARS_PAGE) / 1000}k)`
|
||||
else if (params.pageNumber > 1) // subsequent pages
|
||||
componentParams.desc2 = `(part ${params.pageNumber})`
|
||||
}
|
||||
|
|
@ -1596,7 +1605,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
|||
return <ToolHeaderWrapper {...componentParams} />
|
||||
}
|
||||
},
|
||||
'search_files': {
|
||||
'search_for_files': {
|
||||
resultWrapper: ({ toolMessage }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
|
|
@ -1925,28 +1934,30 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
|||
const Checkpoint = ({ message, threadId, messageIdx, isCheckpointGhost, threadIsRunning }: { message: CheckpointEntry, threadId: string; messageIdx: number, isCheckpointGhost: boolean, threadIsRunning: boolean }) => {
|
||||
const accessor = useAccessor()
|
||||
const chatThreadService = accessor.get('IChatThreadService')
|
||||
const [showCheckpointIcon, setShowCheckpointIcon] = React.useState(false); // add icon state
|
||||
|
||||
return <div
|
||||
className={`flex items-center justify-center px-2 `}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
text-xs
|
||||
text-void-fg-3
|
||||
cursor-pointer select-none
|
||||
${isCheckpointGhost ? 'opacity-50' : 'opacity-100'}
|
||||
`}
|
||||
text-xs
|
||||
text-void-fg-3
|
||||
cursor-pointer select-none
|
||||
${isCheckpointGhost ? 'opacity-50' : 'opacity-100'}
|
||||
`}
|
||||
style={{ position: 'relative', display: 'inline-block' }} // allow absolute icon
|
||||
onClick={() => {
|
||||
if (threadIsRunning) return
|
||||
chatThreadService.jumpToCheckpointBeforeMessageIdx({ threadId, messageIdx, jumpToUserModified: true })
|
||||
}}
|
||||
>
|
||||
Checkpoint
|
||||
Checkpoint
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
|
||||
type ChatBubbleMode = 'display' | 'edit'
|
||||
type ChatBubbleProps = {
|
||||
chatMessage: ChatMessage,
|
||||
|
|
|
|||
|
|
@ -245,12 +245,12 @@ const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
|||
// </button>
|
||||
|
||||
const acceptAllButton = <AcceptAllButtonWrapper
|
||||
text={'Accept File'}
|
||||
text={'Keep Changes'}
|
||||
onClick={onAcceptAll}
|
||||
/>
|
||||
|
||||
const rejectAllButton = <RejectAllButtonWrapper
|
||||
text={'Reject File'}
|
||||
text={'Reject All'}
|
||||
onClick={onRejectAll}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import { Disposable } from '../../../../base/common/lifecycle.js';
|
|||
import { localize2 } from '../../../../nls.js';
|
||||
import { StagingSelectionItem } from '../common/chatThreadServiceTypes.js';
|
||||
import { IChatThreadService } from './chatThreadService.js';
|
||||
import { getActiveWindow } from '../../../../base/browser/dom.js';
|
||||
|
||||
// ---------- Register commands and keybindings ----------
|
||||
|
||||
|
|
@ -225,9 +226,13 @@ registerAction2(class extends Action2 {
|
|||
metricsService.capture('Chat Navigation', { type: 'New Chat' })
|
||||
|
||||
stateService.setState({ isHistoryOpen: false, currentTab: 'chat' })
|
||||
stateService.fireFocusChat()
|
||||
const chatThreadService = accessor.get(IChatThreadService)
|
||||
chatThreadService.openNewThread()
|
||||
|
||||
// focus
|
||||
stateService.fireFocusChat()
|
||||
const window = getActiveWindow()
|
||||
window.requestAnimationFrame(() => stateService.fireFocusChat())
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ type ToolResultToString = { [T in ToolName]: (p: ToolCallParams[T], result: Awai
|
|||
|
||||
|
||||
// pagination info
|
||||
export const MAX_FILE_CHARS_PAGE = 50_000
|
||||
export const MAX_FILE_CHARS_PAGE = Infinity
|
||||
export const MAX_CHILDREN_URIs_PAGE = 500
|
||||
export const MAX_TERMINAL_CHARS_PAGE = 20_000
|
||||
export const TERMINAL_TIMEOUT_TIME = 5 // seconds
|
||||
|
|
@ -163,8 +163,11 @@ export class ToolsService implements IToolsService {
|
|||
const uri = validateURI(uriStr)
|
||||
const pageNumber = validatePageNum(pageNumberUnknown)
|
||||
|
||||
const startLine = validateNumber(startLineUnknown, { default: null })
|
||||
const endLine = validateNumber(endLineUnknown, { default: null })
|
||||
let startLine = validateNumber(startLineUnknown, { default: null })
|
||||
let endLine = validateNumber(endLineUnknown, { default: null })
|
||||
|
||||
if (startLine !== null && startLine < 1) startLine = null
|
||||
if (endLine !== null && endLine < 1) endLine = null
|
||||
|
||||
return { uri, startLine, endLine, pageNumber }
|
||||
},
|
||||
|
|
@ -194,7 +197,7 @@ export class ToolsService implements IToolsService {
|
|||
return { queryStr, searchInFolder, pageNumber }
|
||||
|
||||
},
|
||||
search_files: (params: RawToolParamsObj) => {
|
||||
search_for_files: (params: RawToolParamsObj) => {
|
||||
const {
|
||||
query: queryUnknown,
|
||||
search_in_folder: searchInFolderUnknown,
|
||||
|
|
@ -308,7 +311,7 @@ export class ToolsService implements IToolsService {
|
|||
return { result: { uris, hasNextPage } }
|
||||
},
|
||||
|
||||
search_files: async ({ queryStr, isRegex, searchInFolder, pageNumber }) => {
|
||||
search_for_files: async ({ queryStr, isRegex, searchInFolder, pageNumber }) => {
|
||||
const searchFolders = searchInFolder === null ?
|
||||
workspaceContextService.getWorkspace().folders.map(f => f.uri)
|
||||
: [searchInFolder]
|
||||
|
|
@ -400,7 +403,7 @@ export class ToolsService implements IToolsService {
|
|||
// given to the LLM after the call
|
||||
this.stringOfResult = {
|
||||
read_file: (params, result) => {
|
||||
return `${result.fileContents}${nextPageStr(result.hasNextPage)}${result.hasNextPage ? `This file has ${result.totalFileLen} characters, paginated ${MAX_FILE_CHARS_PAGE} at a time.` : ''}`
|
||||
return `${params.uri.fsPath}\n\`\`\`\n${result.fileContents}\n\`\`\`${nextPageStr(result.hasNextPage)}`
|
||||
},
|
||||
ls_dir: (params, result) => {
|
||||
const dirTreeStr = stringifyDirectoryTree1Deep(params, result)
|
||||
|
|
@ -412,7 +415,7 @@ export class ToolsService implements IToolsService {
|
|||
search_pathnames_only: (params, result) => {
|
||||
return result.uris.map(uri => uri.fsPath).join('\n') + nextPageStr(result.hasNextPage)
|
||||
},
|
||||
search_files: (params, result) => {
|
||||
search_for_files: (params, result) => {
|
||||
return result.uris.map(uri => uri.fsPath).join('\n') + nextPageStr(result.hasNextPage)
|
||||
},
|
||||
read_lint_errors: (params, result) => {
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ export const voidTools = {
|
|||
description: `Returns full contents of a given file.`,
|
||||
params: {
|
||||
...uriParam('file'),
|
||||
start_line: { description: 'Optional. Default is 1. Start reading on this line.' },
|
||||
end_line: { description: 'Optional. Default is Infinity. Stop reading after this line.' },
|
||||
start_line: { description: 'Optional. Only fill this in if you already know the line numbers you need to search. Defaults to 1.' },
|
||||
end_line: { description: 'Optional. Only fill this in if you already know the line numbers you need to search. Defaults to Infinity.' },
|
||||
...paginationParam,
|
||||
},
|
||||
},
|
||||
|
|
@ -84,7 +84,7 @@ export const voidTools = {
|
|||
name: 'ls_dir',
|
||||
description: `Lists all files and folders in the given URI.`,
|
||||
params: {
|
||||
...uriParam('folder'),
|
||||
uri: { description: `Optional. The FULL path to the ${'folder'}. Leave this as empty or "" to search all folders.` },
|
||||
...paginationParam,
|
||||
},
|
||||
},
|
||||
|
|
@ -106,19 +106,19 @@ export const voidTools = {
|
|||
description: `Returns all pathnames that match a given query (searches ONLY file names). You should use this when looking for a file with a specific name or path.`,
|
||||
params: {
|
||||
query: { description: `Your query for the search.` },
|
||||
search_in_folder: { description: 'Optional. Only search files in this given folder glob.' },
|
||||
search_in_folder: { description: 'Optional. Only fill this in if you need to limit your search because there were too many results.' },
|
||||
...paginationParam,
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
|
||||
search_files: {
|
||||
name: 'search_files',
|
||||
description: `Returns all pathnames that match a given query (searches ONLY file contents). The query can be any substring or glob. You can follow this with read_file to view result contents.`,
|
||||
search_for_files: {
|
||||
name: 'search_for_files',
|
||||
description: `Returns a list of file names whose content matches the given query. The query can be any substring or regex.`,
|
||||
params: {
|
||||
query: { description: `Your query for the search.` },
|
||||
search_in_folder: { description: 'Optional. Only search files in this given folder glob.' },
|
||||
search_in_folder: { description: 'Optional. Only fill this in if you need to limit your search because there were too many results.' },
|
||||
is_regex: { description: 'Optional. Default is false. Whether query is a regex.' },
|
||||
...paginationParam,
|
||||
},
|
||||
|
|
@ -158,10 +158,9 @@ export const voidTools = {
|
|||
...uriParam('file'),
|
||||
change_description: {
|
||||
description: `\
|
||||
A brief code description of the change you want to make, with comments like "// ... existing code ..." to condense your writing. \
|
||||
NEVER re-write the whole file. Instead, use comments like "// ... existing code ...". Bias towards writing as little as possible. \
|
||||
Your description will be handed to a smaller model to make the change, so it must be clear and concise. \
|
||||
Your description MUST be wrapped in triple backticks. \
|
||||
A code description of the change you want to make, with comments like "// ... existing code ..." to condense your writing. \
|
||||
NEVER re-write the whole file. Bias towards writing as little as possible. \
|
||||
Here's an example of a good description:\n${editToolDescriptionExample}`
|
||||
}
|
||||
},
|
||||
|
|
@ -345,7 +344,6 @@ ${details.map((d, i) => `${i + 1}. ${d}`).join('\n\n')}`)
|
|||
ansStrs.push(fsInfo)
|
||||
if (toolDefinitions) ansStrs.push(toolDefinitions)
|
||||
ansStrs.push(importantDetails)
|
||||
ansStrs.push('Now, please assist the user with their query.')
|
||||
|
||||
const fullSystemMsgStr = ansStrs
|
||||
.join('\n\n\n')
|
||||
|
|
@ -464,13 +462,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 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.
|
||||
2. The "ORIGINAL" code in each SEARCH/REPLACE block must EXACTLY match lines in the original file. The original code must NOT includes any new whitespace, comments, or any other modifications from the original code.
|
||||
3. The "ORIGINAL" code in each SEARCH/REPLACE block must include enough text to uniquely identify the change in the file, but please bias towards writing as little as possible.
|
||||
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 "UPDATED" code is complete and will not result in syntax/lint errors.
|
||||
- Make sure the "UPDATED" code is ready for production as-is, and fix any relevant 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export type ToolCallParams = {
|
|||
'ls_dir': { rootURI: URI, pageNumber: number },
|
||||
'get_dir_structure': { rootURI: URI },
|
||||
'search_pathnames_only': { queryStr: string, searchInFolder: string | null, pageNumber: number },
|
||||
'search_files': { queryStr: string, isRegex: boolean, searchInFolder: URI | null, pageNumber: number },
|
||||
'search_for_files': { queryStr: string, isRegex: boolean, searchInFolder: URI | null, pageNumber: number },
|
||||
'read_lint_errors': { uri: URI },
|
||||
// ---
|
||||
'edit_file': { uri: URI, changeDescription: string },
|
||||
|
|
@ -43,7 +43,7 @@ export type ToolResultType = {
|
|||
'ls_dir': { children: ShallowDirectoryItem[] | null, hasNextPage: boolean, hasPrevPage: boolean, itemsRemaining: number },
|
||||
'get_dir_structure': { str: string, },
|
||||
'search_pathnames_only': { uris: URI[], hasNextPage: boolean },
|
||||
'search_files': { uris: URI[], hasNextPage: boolean },
|
||||
'search_for_files': { uris: URI[], hasNextPage: boolean },
|
||||
'read_lint_errors': { lintErrors: LintErrorItem[] | null },
|
||||
// ---
|
||||
'edit_file': Promise<{ lintErrors: LintErrorItem[] | null }>,
|
||||
|
|
|
|||
|
|
@ -256,7 +256,12 @@ const _sendOpenAICompatibleChat = ({ messages, onText, onFinalMessage, onError,
|
|||
fullReasoningSoFar += newReasoning
|
||||
}
|
||||
|
||||
onText({ fullText: fullTextSoFar, fullReasoning: fullReasoningSoFar })
|
||||
onText({
|
||||
fullText: fullTextSoFar,
|
||||
fullReasoning: fullReasoningSoFar,
|
||||
toolCall: isAToolName(toolName) ? { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId } : undefined,
|
||||
})
|
||||
|
||||
}
|
||||
// on final
|
||||
if (!fullTextSoFar && !fullReasoningSoFar && !toolName) {
|
||||
|
|
@ -402,6 +407,14 @@ const sendAnthropicChat = ({ messages, providerName, onText, onFinalMessage, onE
|
|||
let fullToolName = ''
|
||||
let fullToolParams = ''
|
||||
|
||||
|
||||
const runOnText = () => {
|
||||
onText({
|
||||
fullText,
|
||||
fullReasoning,
|
||||
toolCall: isAToolName(fullToolName) ? { name: fullToolName, rawParams: {}, isDone: false, doneParams: [], id: 'dummy' } : undefined,
|
||||
})
|
||||
}
|
||||
// there are no events for tool_use, it comes in at the end
|
||||
stream.on('streamEvent', e => {
|
||||
// start block
|
||||
|
|
@ -409,22 +422,22 @@ const sendAnthropicChat = ({ messages, providerName, onText, onFinalMessage, onE
|
|||
if (e.content_block.type === 'text') {
|
||||
if (fullText) fullText += '\n\n' // starting a 2nd text block
|
||||
fullText += e.content_block.text
|
||||
onText({ fullText, fullReasoning, })
|
||||
runOnText()
|
||||
}
|
||||
else if (e.content_block.type === 'thinking') {
|
||||
if (fullReasoning) fullReasoning += '\n\n' // starting a 2nd reasoning block
|
||||
fullReasoning += e.content_block.thinking
|
||||
onText({ fullText, fullReasoning, })
|
||||
runOnText()
|
||||
}
|
||||
else if (e.content_block.type === 'redacted_thinking') {
|
||||
console.log('delta', e.content_block.type)
|
||||
if (fullReasoning) fullReasoning += '\n\n' // starting a 2nd reasoning block
|
||||
fullReasoning += '[redacted_thinking]'
|
||||
onText({ fullText, fullReasoning, })
|
||||
runOnText()
|
||||
}
|
||||
else if (e.content_block.type === 'tool_use') {
|
||||
fullToolName += e.content_block.name ?? '' // anthropic gives us the tool name in the start block
|
||||
onText({ fullText, fullReasoning, })
|
||||
runOnText()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -432,15 +445,15 @@ const sendAnthropicChat = ({ messages, providerName, onText, onFinalMessage, onE
|
|||
else if (e.type === 'content_block_delta') {
|
||||
if (e.delta.type === 'text_delta') {
|
||||
fullText += e.delta.text
|
||||
onText({ fullText, fullReasoning, })
|
||||
runOnText()
|
||||
}
|
||||
else if (e.delta.type === 'thinking_delta') {
|
||||
fullReasoning += e.delta.thinking
|
||||
onText({ fullText, fullReasoning, })
|
||||
runOnText()
|
||||
}
|
||||
else if (e.delta.type === 'input_json_delta') { // tool use
|
||||
fullToolParams += e.delta.partial_json ?? '' // anthropic gives us the partial delta (string) here - https://docs.anthropic.com/en/api/messages-streaming
|
||||
onText({ fullText, fullReasoning, })
|
||||
runOnText()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue