draft parser

This commit is contained in:
centdix
2025-08-29 11:32:58 +00:00
parent 5e96efdccc
commit 97dabd3f1d
2 changed files with 132 additions and 21 deletions
@@ -44,6 +44,7 @@ import type { ContextElement } from './context'
import type { Selection } from 'monaco-editor'
import type AIChatInput from './AIChatInput.svelte'
import { prepareApiSystemMessage, prepareApiUserMessage } from './api/core'
import { getAnthropicCompletion, parseAnthropicCompletion } from './anthropic'
// If the estimated token usage is greater than the model context window - the threshold, we delete the oldest message
const MAX_TOKENS_THRESHOLD_PERCENTAGE = 0.05
@@ -409,23 +410,47 @@ class AIChatManager {
this.pendingPrompt = ''
}
const completion = await getCompletion(
[systemMessage, ...messages, ...(pendingUserMessage ? [pendingUserMessage] : [])],
abortController,
tools.map((t) => t.def)
)
const model = getCurrentModel()
if (completion) {
const continueCompletion = await parseOpenAICompletion(
completion,
callbacks,
messages,
addedMessages,
tools,
helpers
if (model.provider === 'anthropic') {
const completion = await getAnthropicCompletion(
[systemMessage, ...messages, ...(pendingUserMessage ? [pendingUserMessage] : [])],
abortController,
tools.map((t) => t.def)
)
if (!continueCompletion) {
break
if (completion) {
const continueCompletion = await parseAnthropicCompletion(
completion,
callbacks,
messages,
addedMessages,
tools,
helpers
)
if (!continueCompletion) {
break
}
}
} else {
const completion = await getCompletion(
[systemMessage, ...messages, ...(pendingUserMessage ? [pendingUserMessage] : [])],
abortController,
tools.map((t) => t.def)
)
if (completion) {
const continueCompletion = await parseOpenAICompletion(
completion,
callbacks,
messages,
addedMessages,
tools,
helpers
)
if (!continueCompletion) {
break
}
}
}
}
@@ -7,15 +7,15 @@ import type {
ToolUseBlockParam,
Tool as AnthropicTool
} from '@anthropic-ai/sdk/resources'
import type { MessageStream } from '@anthropic-ai/sdk/lib/MessageStream'
import { getProviderAndCompletionConfig, workspaceAIClients } from '../lib'
import type { Stream } from 'openai/streaming.mjs'
import type { Tool, ToolCallbacks } from './shared'
import { processToolCall, type Tool, type ToolCallbacks } from './shared'
export async function getAnthropicCompletion(
messages: ChatCompletionMessageParam[],
abortController: AbortController,
tools?: OpenAI.Chat.Completions.ChatCompletionTool[]
) {
): Promise<MessageStream> {
const { provider, config } = getProviderAndCompletionConfig({ messages, stream: true })
const { system, messages: anthropicMessages } = convertOpenAIToAnthropicMessages(messages)
const anthropicTools = convertOpenAIToolsToAnthropic(tools)
@@ -43,7 +43,7 @@ export async function getAnthropicCompletion(
}
export async function parseAnthropicCompletion(
completion: Stream<any>,
completion: MessageStream,
callbacks: ToolCallbacks & {
onNewToken: (token: string) => void
onMessageEnd: () => void
@@ -53,8 +53,94 @@ export async function parseAnthropicCompletion(
tools: Tool<any>[],
helpers: any
): Promise<boolean> {
// TODO: Implement Anthropic completion parsing
return true
let hasToolCalls = false
let toolCallsToProcess: { id: string; name: string; input: any }[] = []
let textContent = ''
// Handle text streaming
completion.on('text', (textDelta: string, _textSnapshot: string) => {
textContent += textDelta
callbacks.onNewToken(textDelta)
})
// Handle completed content blocks (including tool use)
completion.on('contentBlock', (contentBlock: any) => {
if (contentBlock.type === 'tool_use') {
hasToolCalls = true
toolCallsToProcess.push({
id: contentBlock.id,
name: contentBlock.name,
input: contentBlock.input
})
// Preprocess tool if it has a preAction
const tool = tools.find((t) => t.def.function.name === contentBlock.name)
if (tool && tool.preAction) {
tool.preAction({ toolCallbacks: callbacks, toolId: contentBlock.id })
}
}
})
// Handle errors
completion.on('error', (error: any) => {
console.error('Anthropic stream error:', error)
throw error
})
// Wait for completion
await completion.done()
// Add text content to messages if any
if (textContent) {
const assistantMessage = { role: 'assistant' as const, content: textContent }
addedMessages.push(assistantMessage)
messages.push(assistantMessage)
}
callbacks.onMessageEnd()
// Process tool calls if any
if (hasToolCalls && toolCallsToProcess.length > 0) {
// Convert Anthropic tool calls to OpenAI format for compatibility
const toolCalls = toolCallsToProcess.map((toolCall) => ({
id: toolCall.id,
type: 'function' as const,
function: {
name: toolCall.name,
arguments: JSON.stringify(toolCall.input)
}
}))
const assistantWithTools = {
role: 'assistant' as const,
tool_calls: toolCalls
}
messages.push(assistantWithTools)
addedMessages.push(assistantWithTools)
// Process each tool call
for (const toolCall of toolCallsToProcess) {
const openAIToolCall = {
id: toolCall.id,
type: 'function' as const,
function: {
name: toolCall.name,
arguments: JSON.stringify(toolCall.input)
}
}
const messageToAdd = await processToolCall({
tools,
toolCall: openAIToolCall,
helpers,
toolCallbacks: callbacks
})
messages.push(messageToAdd)
addedMessages.push(messageToAdd)
}
return true // Continue the conversation loop
}
return false // End the conversation
}
export function convertOpenAIToAnthropicMessages(messages: ChatCompletionMessageParam[]): {