diff --git a/apps/component-calling/src/pages/index.vue b/apps/component-calling/src/pages/index.vue index 6ba7e0149..7aba66d77 100644 --- a/apps/component-calling/src/pages/index.vue +++ b/apps/component-calling/src/pages/index.vue @@ -365,23 +365,21 @@ async function handleChatSendMessage() { waiting.value = false - for await (const chunk of response.fullStream) { - if (chunk.type === 'text-delta') { - streamingMessage.value.content += chunk.text + for await (const text of response.textStream) { + streamingMessage.value.content += text - try { - if (chunk.text.length > 1) { - for (const char of chunk.text) { - parser.consume(char) - } - } - else { - parser.consume(chunk.text) + try { + if (text.length > 1) { + for (const char of text) { + parser.consume(char) } } - catch { + else { + parser.consume(text) } } + catch { + } } } catch (err) { diff --git a/apps/component-calling/src/utils/xsai-testing.ts b/apps/component-calling/src/utils/xsai-testing.ts index ea6dcedb6..5f659c10b 100644 --- a/apps/component-calling/src/utils/xsai-testing.ts +++ b/apps/component-calling/src/utils/xsai-testing.ts @@ -1,18 +1,16 @@ -import type { StreamTextEvent } from '@xsai/stream-text' - export function mockStreamText(): { - fullStream: ReadableStream + textStream: ReadableStream } { const source = `weather \`\`\`json {"city":"Shanghai","temperature":"29","condition":"cloudy"} \`\`\`` return { - fullStream: new ReadableStream({ + textStream: new ReadableStream({ start(controller) { const text = source.split('') let index = 0 const interval = setInterval(() => { if (index < text.length) { - controller.enqueue({ type: 'text-delta', text: text[index] }) + controller.enqueue(text[index]!) index++ } else { diff --git a/integrations/satori-bot/src/core/planner/llm-client.ts b/integrations/satori-bot/src/core/planner/llm-client.ts index 32ee0444b..27eed770f 100644 --- a/integrations/satori-bot/src/core/planner/llm-client.ts +++ b/integrations/satori-bot/src/core/planner/llm-client.ts @@ -79,9 +79,9 @@ export async function imagineAnAction( response: res.text, unreadEvents: Object.fromEntries(Object.entries(globalStates.unreadEvents).map(([key, value]) => [key, value.length])), now: new Date().toLocaleString(), - totalTokens: res.usage.total_tokens, - promptTokens: res.usage.prompt_tokens, - completion_tokens: res.usage.completion_tokens, + totalTokens: res.usage.totalTokens, + promptTokens: res.usage.inputTokens, + completion_tokens: res.usage.outputTokens, }).log('Generated action') responseText = res.text diff --git a/integrations/telegram-bot/src/bots/telegram/agent/actions/send-message.ts b/integrations/telegram-bot/src/bots/telegram/agent/actions/send-message.ts index 88e47262f..ef012f9dc 100644 --- a/integrations/telegram-bot/src/bots/telegram/agent/actions/send-message.ts +++ b/integrations/telegram-bot/src/bots/telegram/agent/actions/send-message.ts @@ -27,7 +27,7 @@ export function parseMayStructuredMessage(responseText: string) { const parsedResponse = parse(result[0]) as ({ messages?: unknown, reply_to_message_id?: unknown } | undefined) const hasMessagesArray = Array.isArray(parsedResponse?.messages) - const messages = hasMessagesArray + const messages = Array.isArray(parsedResponse?.messages) ? parsedResponse.messages.filter((message): message is string => typeof message === 'string' && message.trim() !== '') : [] const replyToMessageId = typeof parsedResponse?.reply_to_message_id === 'string' @@ -109,9 +109,9 @@ export async function sendMessage( messages: responseText, response: res.text, now: new Date().toLocaleString(), - totalTokens: res.usage.total_tokens, - promptTokens: res.usage.prompt_tokens, - completion_tokens: res.usage.completion_tokens, + totalTokens: res.usage.totalTokens, + promptTokens: res.usage.inputTokens, + completion_tokens: res.usage.outputTokens, }).log('Message split') const structuredMessage = parseMayStructuredMessage(res.text) diff --git a/integrations/telegram-bot/src/llm/actions.ts b/integrations/telegram-bot/src/llm/actions.ts index d3fe433af..f74e146ad 100644 --- a/integrations/telegram-bot/src/llm/actions.ts +++ b/integrations/telegram-bot/src/llm/actions.ts @@ -99,9 +99,9 @@ export async function imagineAnAction( response: res.text, unreadMessages: Object.fromEntries(Object.entries(globalStates.unreadMessages).map(([key, value]) => [key, value.length])), now: new Date().toLocaleString(), - totalTokens: res.usage.total_tokens, - promptTokens: res.usage.prompt_tokens, - completion_tokens: res.usage.completion_tokens, + totalTokens: res.usage.totalTokens, + promptTokens: res.usage.inputTokens, + completion_tokens: res.usage.outputTokens, }).log('Generated action') const action = tracer.startActiveSpan('telegram.module.generate_agent_action.parse', (s) => { diff --git a/packages/core-agent/src/agents/spark-notify/agent.ts b/packages/core-agent/src/agents/spark-notify/agent.ts index 58127726f..5e496f99c 100644 --- a/packages/core-agent/src/agents/spark-notify/agent.ts +++ b/packages/core-agent/src/agents/spark-notify/agent.ts @@ -245,8 +245,8 @@ export function createSparkNotifyAgent(options: CreateSparkNotifyAgentOptions): return } - if (streamEvent.type === 'tool-result') { - await emit({ type: 'tool-execution', payload: { eventId: request.event.data.eventId, toolCallId: streamEvent.toolCallId, output: streamEvent.result } }) + if (streamEvent.type === 'tool-result' || streamEvent.type === 'tool-error') { + await emit({ type: 'tool-execution', payload: { eventId: request.event.data.eventId, kind: streamEvent.type, toolCallId: streamEvent.toolCallId, output: streamEvent.result } }) return } diff --git a/packages/core-agent/src/runtime/chat-orchestrator-runtime.ts b/packages/core-agent/src/runtime/chat-orchestrator-runtime.ts index 99180d10e..b528ba1a6 100644 --- a/packages/core-agent/src/runtime/chat-orchestrator-runtime.ts +++ b/packages/core-agent/src/runtime/chat-orchestrator-runtime.ts @@ -726,7 +726,6 @@ export function createChatOrchestratorRuntime(deps: ChatOrchestratorRuntimeDeps) }, tools: options.tools, waitForTools: true, - captureToolErrors: true, onUsage: (usage) => { generationUsage = usage deps.onLlmGeneration?.({ diff --git a/packages/core-agent/src/runtime/llm-service.test.ts b/packages/core-agent/src/runtime/llm-service.test.ts index 5c7f2ea39..397de1f9f 100644 --- a/packages/core-agent/src/runtime/llm-service.test.ts +++ b/packages/core-agent/src/runtime/llm-service.test.ts @@ -1,7 +1,7 @@ import type { ChatProvider } from '@xsai-ext/providers/utils' import type { Message, Tool } from '@xsai/shared-chat' -import { describe, expect, it, vi } from 'vitest' +import { beforeEach, describe, expect, it, vi } from 'vitest' import { isContentArrayRelatedError, sanitizeMessages, streamFrom } from './llm-service' @@ -39,7 +39,10 @@ function createMockStreamResult( } } -describe('streamFrom tool error capture', () => { +describe('streamFrom tool errors', () => { + beforeEach(() => { + streamTextMock.mockReset() + }) it('requests final streaming usage and emits the reported token totals once', async () => { const onUsage = vi.fn() streamTextMock.mockReturnValueOnce(createMockStreamResult( @@ -140,11 +143,7 @@ describe('streamFrom tool error capture', () => { })).resolves.toBeUndefined() }) - /** - * @example - * await streamFrom({ model, chatProvider, messages, options: { captureToolErrors: true } }) - */ - it('keeps captureToolErrors internal while forwarding failed tool calls as tool-error events', async () => { + it('maps xsai tool-error results to AIRI tool-error events without wrapping tools', async () => { let resolveSteps: ((steps: unknown[]) => void) | undefined const events: unknown[] = [] const failingTool = { @@ -160,8 +159,8 @@ describe('streamFrom tool error capture', () => { } satisfies Tool streamTextMock.mockImplementationOnce((options: { - captureToolErrors?: boolean onEvent: (event: unknown) => Promise + preToolCall?: unknown tools?: Tool[] }) => { const steps = new Promise((resolve) => { @@ -169,19 +168,15 @@ describe('streamFrom tool error capture', () => { }) queueMicrotask(async () => { - const result = await options.tools?.[0]?.execute({}, { - messages: [], - toolCallId: 'call-1', - }) - await options.onEvent({ - type: 'tool-result', + type: 'tool-result.done', args: {}, - result, + isError: true, + result: 'Tool "play_chess" execution failed: Focus mode does not accept game-state mutation inputs.', toolCallId: 'call-1', toolName: 'play_chess', }) - await options.onEvent({ type: 'finish', finishReason: 'stop' }) + await options.onEvent({ type: 'text.delta', delta: 'ok' }) resolveSteps?.([]) }) @@ -193,7 +188,6 @@ describe('streamFrom tool error capture', () => { chatProvider: provider, messages: [{ role: 'user', content: 'play chess' }] as Message[], options: { - captureToolErrors: true, tools: [failingTool], onStreamEvent: (event) => { events.push(event) @@ -202,16 +196,35 @@ describe('streamFrom tool error capture', () => { }) const streamOptions = streamTextMock.mock.calls[0]?.[0] - expect(streamOptions.captureToolErrors).toBeUndefined() - expect(streamOptions.tools?.[0]).not.toBe(failingTool) - expect(failingTool.execute).toHaveBeenCalledTimes(1) - expect(events).toContainEqual(expect.objectContaining({ + expect(streamOptions.preToolCall).toBeUndefined() + expect(streamOptions.tools?.[0]).toBe(failingTool) + expect(failingTool.execute).not.toHaveBeenCalled() + expect(events).toContainEqual({ type: 'tool-error', + args: {}, isError: true, + result: 'Tool "play_chess" execution failed: Focus mode does not accept game-state mutation inputs.', toolCallId: 'call-1', toolName: 'play_chess', - result: expect.stringContaining('Focus mode does not accept game-state mutation inputs.'), - })) + }) + expect(events).toContainEqual({ type: 'text-delta', text: 'ok' }) + expect(events).toContainEqual({ type: 'finish' }) + }) + + it('rejects when the finish listener throws instead of leaving the stream pending', async () => { + streamTextMock.mockReturnValueOnce(createMockStreamResult()) + + await expect(streamFrom({ + model: 'model-a', + chatProvider: provider, + messages: [{ role: 'user', content: 'hello' }] as Message[], + options: { + onStreamEvent: async (event) => { + if (event.type === 'finish') + throw new Error('finish listener failed') + }, + }, + })).rejects.toThrow('finish listener failed') }) }) diff --git a/packages/core-agent/src/runtime/llm-service.ts b/packages/core-agent/src/runtime/llm-service.ts index 2287d807d..85ae89c9c 100644 --- a/packages/core-agent/src/runtime/llm-service.ts +++ b/packages/core-agent/src/runtime/llm-service.ts @@ -1,13 +1,11 @@ import type { ChatProvider } from '@xsai-ext/providers/utils' -import type { Message, Tool, Usage } from '@xsai/shared-chat' +import type { Event, Message, Usage } from '@xsai/shared-chat' -import type { StreamFromOptions, StreamOptions } from '../types/llm' +import type { StreamEvent, StreamFromOptions, StreamOptions } from '../types/llm' import { stepCountAtLeast } from '@xsai/shared-chat' import { streamText } from '@xsai/stream-text' -import { errorMessageFromValue } from '../utils/error-message' - /** * Normalize chat messages so they match the wire format the active provider * actually accepts, flattening content-part arrays back to plain strings when @@ -103,75 +101,45 @@ async function resolveTools(options?: StreamOptions) { return tools ?? [] } -function isAbortError(error: unknown): boolean { - return typeof error === 'object' - && error !== null - && (error as { name?: unknown }).name === 'AbortError' -} - -function createCapturedToolErrorResult(toolName: string, error: unknown): string { - return `Tool call error for "${toolName}": ${errorMessageFromValue(error)}` -} - -function normalizeUsage(usage: Usage | undefined) { - if (usage?.inputTokens == null || usage.outputTokens == null || usage.totalTokens == null) { - return { source: 'unavailable' as const } - } - - return { - inputTokens: usage.inputTokens, - outputTokens: usage.outputTokens, - totalTokens: usage.totalTokens, - source: 'reported' as const, - } -} - -function withCapturedToolErrors( - tools: Tool[], - capturedToolErrorByCallId: Map, -): Tool[] { - return tools.map(tool => ({ - ...tool, - execute: async (input, executeOptions) => { - try { - return await tool.execute(input, executeOptions) +/** + * Maps xsAI stream events onto the AIRI {@link StreamEvent} contract. + * + * xsAI 0.5.0-beta.8 marks failed tool executions with `isError: true` on + * `tool-result.done` instead of aborting the stream, so AIRI can distinguish + * `tool-error` from `tool-result` directly from the event payload. + */ +function toAiriStreamEvent(event: Event): StreamEvent | null { + switch (event.type) { + case 'text.delta': + return { type: 'text-delta', text: event.delta } + case 'reasoning.delta': + return { type: 'reasoning-delta', text: event.delta } + case 'tool-call.done': + return { ...event, type: 'tool-call' } + case 'tool-result.done': + if (event.isError === true) + return { ...event, type: 'tool-error', isError: true } + return { + type: 'tool-result', + toolCallId: event.toolCallId, + result: typeof event.result === 'string' || Array.isArray(event.result) + ? event.result + : JSON.stringify(event.result), } - catch (error) { - if (isAbortError(error)) - throw error - - const result = createCapturedToolErrorResult(tool.function.name, error) - capturedToolErrorByCallId.set(executeOptions.toolCallId, result) - return result + case 'error': + return { + type: 'error', + error: event.cause ?? new Error(event.message), } - }, - })) -} - -function resolveCapturedToolErrorEvent( - event: unknown, - capturedToolErrorByCallId: Map, -) { - if ( - typeof event !== 'object' - || event === null - || (event as { type?: unknown }).type !== 'tool-result' - || typeof (event as { toolCallId?: unknown }).toolCallId !== 'string' - ) { - return event - } - - const toolCallId = (event as { toolCallId: string }).toolCallId - const result = capturedToolErrorByCallId.get(toolCallId) - if (result == null) - return event - - capturedToolErrorByCallId.delete(toolCallId) - return { - ...event, - type: 'tool-error', - isError: true, - result, + case 'text.start': + case 'text.done': + case 'reasoning.start': + case 'reasoning.done': + case 'step.start': + case 'step.done': + case 'tool-call.start': + case 'tool-call.delta': + return null } } @@ -193,10 +161,6 @@ export async function streamFrom({ const customTools = supportedTools ? await resolveTools(options) : [] const mergedTools = supportedTools ? [...builtinTools, ...customTools] : [] const tools = mergedTools.length > 0 ? mergedTools : undefined - const capturedToolErrorByCallId = new Map() - const streamTools = options?.captureToolErrors && tools != null - ? withCapturedToolErrors(tools, capturedToolErrorByCallId) - : tools return new Promise((resolve, reject) => { let settled = false @@ -214,13 +178,13 @@ export async function streamFrom({ reject(error) } - const onEvent = async (event: unknown) => { + const onEvent = async (event: Event) => { try { - const streamEvent = resolveCapturedToolErrorEvent(event, capturedToolErrorByCallId) - await options?.onStreamEvent?.(streamEvent as any) - if (event && (event as any).type === 'error') { - rejectOnce((event as any).error ?? new Error('Stream error')) - } + const streamEvent = toAiriStreamEvent(event) + if (streamEvent != null) + await options?.onStreamEvent?.(streamEvent) + if (streamEvent?.type === 'error') + rejectOnce(streamEvent.error) } catch (error) { rejectOnce(error) @@ -235,12 +199,7 @@ export async function streamFrom({ headers: options?.headers, streamOptions: { includeUsage: true }, stopWhen: stepCountAtLeast(10), - // NOTICE: - // Do not pass xsAI's `captureToolErrors` option here. In the installed - // @xsai/stream-text version, stream options are spread into the provider - // chat body, so unknown runtime-only fields can be rejected upstream. - // AIRI captures tool failures by wrapping local tool executors instead. - tools: streamTools, + tools, toolChoice: options?.toolChoice, onEvent, }) @@ -263,6 +222,19 @@ export async function streamFrom({ // Ignore any late provider error event emitted after xsAI has already // resolved the authoritative full-step lifecycle. stepsSettled = true + try { + await options?.onStreamEvent?.({ type: 'finish' } as const) + } + catch (error) { + // The finish listener runs after steps settled, so rejectOnce would + // ignore this error as a "late provider event". A listener failure + // is still a real failure and must reject the outer promise. + if (!settled) { + settled = true + reject(error) + } + return + } let usage: Usage | undefined try { usage = await streamResult.totalUsage @@ -271,7 +243,11 @@ export async function streamFrom({ console.error('Stream totalUsage error:', error) } try { - await options?.onUsage?.(normalizeUsage(usage)) + const normalizedUsage = !usage + || (usage.inputTokens == null && usage.outputTokens == null && usage.totalTokens == null) + ? { source: 'unavailable' as const } + : { ...usage, source: 'reported' as const } + await options?.onUsage?.(normalizedUsage) } catch (error) { // Usage observers are telemetry-only and must not turn a completed diff --git a/packages/core-agent/src/types/llm.ts b/packages/core-agent/src/types/llm.ts index 01e7be02c..7bc452fd2 100644 --- a/packages/core-agent/src/types/llm.ts +++ b/packages/core-agent/src/types/llm.ts @@ -17,7 +17,7 @@ export type StreamEvent | { type: 'reasoning-delta', text: string } | ({ type: 'finish' } & any) | ({ type: 'tool-call' } & CompletionToolCall) - | (CompletionToolResult & { type: 'tool-error' }) + | (CompletionToolResult & { type: 'tool-error', isError: true }) | { type: 'tool-result', toolCallId: string, result?: string | CommonContentPart[] } | { type: 'error', error: any } @@ -35,7 +35,6 @@ export interface StreamOptions { toolsCompatibility?: Map supportsTools?: boolean waitForTools?: boolean - captureToolErrors?: boolean /** Provider tool-selection directive for one request. */ toolChoice?: ToolChoice tools?: Tool[] | (() => Promise) diff --git a/packages/stage-ui/src/stores/ai/chat-llm/llm.test.ts b/packages/stage-ui/src/stores/ai/chat-llm/llm.test.ts index badd88cc3..0d76f4124 100644 --- a/packages/stage-ui/src/stores/ai/chat-llm/llm.test.ts +++ b/packages/stage-ui/src/stores/ai/chat-llm/llm.test.ts @@ -129,40 +129,32 @@ describe('isToolRelatedError', () => { }) } - it('resolves from steps while still forwarding tool_calls finish events', async () => { - let onEvent: ((event: unknown) => Promise) | undefined - streamTextMock.mockImplementation((options: { onEvent: (event: unknown) => Promise }) => { - onEvent = options.onEvent - return createMockStreamResult() - }) + it('resolves from steps and emits a single finish event', async () => { + streamTextMock.mockImplementation(() => createMockStreamResult()) const store = useLLM() const onStreamEvent = vi.fn() - let resolved = false - const pending = store.stream('model-a', provider, [{ role: 'user', content: 'hello' }] as Message[], { + await store.stream('model-a', provider, [{ role: 'user', content: 'hello' }] as Message[], { waitForTools: true, onStreamEvent, - }).then(() => { - resolved = true }) - await vi.waitFor(() => expect(onEvent).toBeTypeOf('function')) - await onEvent!({ type: 'finish', finishReason: 'tool_calls' }) - await Promise.resolve() - expect(resolved).toBe(true) - - await onEvent!({ type: 'finish', finishReason: 'stop' }) - await pending - - expect(onStreamEvent).toHaveBeenCalledTimes(2) + expect(onStreamEvent).toHaveBeenCalledTimes(1) + expect(onStreamEvent).toHaveBeenCalledWith({ type: 'finish' }) }) it('ignores later error events after steps have resolved', async () => { let onEvent: ((event: unknown) => Promise) | undefined + let resolveSteps: ((steps: unknown[]) => void) | undefined streamTextMock.mockImplementation((options: { onEvent: (event: unknown) => Promise }) => { onEvent = options.onEvent - return createMockStreamResult() + return { + ...createMockStreamResult(), + steps: new Promise((resolve) => { + resolveSteps = resolve + }), + } }) const store = useLLM() @@ -171,18 +163,27 @@ describe('isToolRelatedError', () => { }) await vi.waitFor(() => expect(onEvent).toBeTypeOf('function')) - await onEvent!({ type: 'finish', finishReason: 'tool_calls' }) - await onEvent!({ type: 'error', error: new Error('stream failed') }) + resolveSteps?.([]) + await Promise.resolve() + await onEvent!({ type: 'error', message: 'stream failed', cause: new Error('stream failed') }) await expect(pending).resolves.toBeUndefined() }) it('keeps builtin tools when stream steps resolve before a tool-related error event', async () => { const store = useLLM() const llmToolsStore = useLlmToolsStore() - const customTool = { name: 'custom-tool' } as any + const customTool = { + type: 'function', + function: { + name: 'custom-tool', + description: 'Custom tool.', + parameters: { type: 'object', properties: {} }, + }, + execute: vi.fn(async () => 'ok'), + } satisfies Tool const runtimeTool = { id: 'plugin:chess:runtime_play_chess_match', - type: 'function', + type: 'function' as const, function: { name: 'runtime_play_chess_match', description: 'Start a runtime chess match.', @@ -195,7 +196,7 @@ describe('isToolRelatedError', () => { streamTextMock.mockImplementationOnce((options: { onEvent: (event: unknown) => Promise, tools?: unknown[] }) => { queueMicrotask(async () => { - await options.onEvent({ type: 'error', error: new Error('model does not support tools') }) + await options.onEvent({ type: 'error', message: 'model does not support tools', cause: new Error('model does not support tools') }) }) return createMockStreamResult() }) @@ -208,15 +209,10 @@ describe('isToolRelatedError', () => { expect(Array.isArray(firstCallTools)).toBe(true) expect(mcpMock).toHaveBeenCalledTimes(1) expect(debugMock).toHaveBeenCalledTimes(1) - expect(firstCallTools).toContain(customTool) + expect(firstCallTools?.map(toolNameFrom)).toContain('custom-tool') expect(firstCallTools?.map(toolNameFrom)).toContain('runtime_play_chess_match') - streamTextMock.mockImplementationOnce((options: { onEvent: (event: unknown) => Promise, tools?: unknown[] }) => { - queueMicrotask(async () => { - await options.onEvent({ type: 'finish', finishReason: 'stop' }) - }) - return createMockStreamResult() - }) + streamTextMock.mockImplementationOnce(() => createMockStreamResult()) await store.stream('model-a', provider, [{ role: 'user', content: 'hello again' }] as Message[], { tools: [customTool], @@ -232,7 +228,7 @@ describe('isToolRelatedError', () => { const llmToolsStore = useLlmToolsStore() const playChessTool = { id: 'plugin:chess:runtime_open_chess_board', - type: 'function', + type: 'function' as const, function: { name: 'runtime_open_chess_board', description: 'Open the runtime chess board.', @@ -242,7 +238,7 @@ describe('isToolRelatedError', () => { } satisfies ExecutableTool const runtimeMcpStatusTool = { id: 'mcp:runtime_sync_mcp_status', - type: 'function', + type: 'function' as const, function: { name: 'runtime_sync_mcp_status', description: 'Sync runtime MCP status.', @@ -253,12 +249,7 @@ describe('isToolRelatedError', () => { llmToolsStore.addTools(runtimeMcpStatusTool, playChessTool) - streamTextMock.mockImplementationOnce((options: { onEvent: (event: unknown) => Promise, tools?: unknown[] }) => { - queueMicrotask(async () => { - await options.onEvent({ type: 'finish', finishReason: 'stop' }) - }) - return createMockStreamResult() - }) + streamTextMock.mockImplementationOnce(() => createMockStreamResult()) await store.stream('model-a', provider, [{ role: 'user', content: 'play chess' }] as Message[]) @@ -273,6 +264,7 @@ describe('isToolRelatedError', () => { const store = useLLM() const llmToolsStore = useLlmToolsStore() const builtinTool = { + type: 'function', function: { name: 'duplicate_runtime_tool', description: 'Builtin version.', @@ -282,7 +274,7 @@ describe('isToolRelatedError', () => { } as unknown as Tool const runtimeTool = { id: 'plugin:runtime:duplicate_runtime_tool', - type: 'function', + type: 'function' as const, function: { name: 'duplicate_runtime_tool', description: 'Runtime version.', @@ -294,16 +286,11 @@ describe('isToolRelatedError', () => { mcpMock.mockResolvedValueOnce([builtinTool] as Tool[]) llmToolsStore.addTools(runtimeTool) - streamTextMock.mockImplementationOnce((options: { onEvent: (event: unknown) => Promise, tools?: unknown[] }) => { - queueMicrotask(async () => { - await options.onEvent({ type: 'finish', finishReason: 'stop' }) - }) - return createMockStreamResult() - }) + streamTextMock.mockImplementationOnce(() => createMockStreamResult()) await store.stream('model-a', provider, [{ role: 'user', content: 'play chess' }] as Message[]) - const mergedTools = streamTextMock.mock.calls[0]?.[0]?.tools as Array<{ function?: { name?: string } }> + const mergedTools = streamTextMock.mock.calls[0]?.[0]?.tools as Array<{ function?: { name?: string, description?: string } }> const duplicateNameTools = mergedTools.filter(tool => tool.function?.name === 'duplicate_runtime_tool') expect(duplicateNameTools).toHaveLength(1) diff --git a/packages/stage-ui/src/stores/chat.contract.test.ts b/packages/stage-ui/src/stores/chat.contract.test.ts index 5b65b48f2..baddfa8b3 100644 --- a/packages/stage-ui/src/stores/chat.contract.test.ts +++ b/packages/stage-ui/src/stores/chat.contract.test.ts @@ -518,7 +518,7 @@ describe('chat store contract', () => { llmStreamMock.mockImplementation(async (_model: string, _chatProvider: ChatProvider, messages: Message[], options: any) => { composedMessages = messages expect(options.waitForTools).toBe(true) - expect(options.captureToolErrors).toBe(true) + expect(options.captureToolErrors).toBeUndefined() await options.onStreamEvent({ type: 'text-delta', text: 'hello' }) await options.onStreamEvent({ type: 'finish', finishReason: 'stop' }) diff --git a/packages/stage-ui/src/stores/execute-tool.test.ts b/packages/stage-ui/src/stores/execute-tool.test.ts deleted file mode 100644 index 0a879b4fc..000000000 --- a/packages/stage-ui/src/stores/execute-tool.test.ts +++ /dev/null @@ -1,294 +0,0 @@ -import type { Message, Tool, ToolCall } from '@xsai/shared-chat' - -import { InvalidToolCallError, InvalidToolInputError, ToolExecutionError } from '@xsai/shared' -import { executeTool } from '@xsai/shared-chat' -import { describe, expect, it, vi } from 'vitest' - -function createToolCall(overrides: Partial & { - function?: Partial & { name?: string, arguments?: string } -} = {}): ToolCall { - const fn = overrides.function ?? {} - return { - id: 'call_1', - type: 'function', - function: { - name: 'myTool', - arguments: '{}', - ...fn, - }, - ...overrides, - } as ToolCall -} - -function createTool(name: string, execute: Tool['execute']): Tool { - return { - type: 'function', - function: { name, description: '', parameters: {} }, - execute, - } -} - -const emptyMessages: Message[] = [] - -describe('executeTool (patched @xsai/shared-chat)', () => { - it('returns success tool message when tool executes', async () => { - const tools = [createTool('myTool', async () => 'ok')] - const toolCall = createToolCall() - - const out = await executeTool({ - messages: emptyMessages, - toolCall, - tools, - }) - - expect(out.completionToolResult.isError).toBeUndefined() - expect(out.completionToolResult.result).toBe('ok') - expect(out.message.role).toBe('tool') - expect(out.message.content).toBe('ok') - expect(out.message.tool_call_id).toBe('call_1') - }) - - it('captures unknown tool as error result instead of throwing', async () => { - const tools = [createTool('other', async () => 'x')] - const toolCall = createToolCall({ function: { name: 'missingTool', arguments: '{}' } }) - - const out = await executeTool({ - captureToolErrors: true, - messages: emptyMessages, - toolCall, - tools, - }) - - expect(out.completionToolResult.isError).toBe(true) - expect(out.completionToolResult.error).toBeDefined() - expect(InvalidToolCallError.isInstance(out.completionToolResult.error)).toBe(true) - expect(String(out.message.content)).toContain('missingTool') - expect(out.message.role).toBe('tool') - }) - - it('captures invalid JSON arguments as error result', async () => { - const tools = [createTool('myTool', async () => 'x')] - const toolCall = createToolCall({ function: { name: 'myTool', arguments: '{broken' } }) - - const out = await executeTool({ - captureToolErrors: true, - messages: emptyMessages, - toolCall, - tools, - }) - - expect(out.completionToolResult.isError).toBe(true) - expect(InvalidToolInputError.isInstance(out.completionToolResult.error)).toBe(true) - expect(String(out.message.content)).toContain('myTool') - }) - - it('captures tool execute rejection as error result', async () => { - const tools = [ - createTool('myTool', async () => { - throw new Error('execute failed') - }), - ] - const toolCall = createToolCall() - - const out = await executeTool({ - captureToolErrors: true, - messages: emptyMessages, - toolCall, - tools, - }) - - expect(out.completionToolResult.isError).toBe(true) - expect(ToolExecutionError.isInstance(out.completionToolResult.error)).toBe(true) - expect(String(out.message.content)).toContain('myTool') - expect(String(out.message.content)).toContain('execution failed') - }) - - it('rethrows AbortError from tool execute', async () => { - const controller = new AbortController() - const tools = [ - createTool('myTool', async () => { - const err = new Error('aborted') - err.name = 'AbortError' - throw err - }), - ] - const toolCall = createToolCall() - - await expect( - executeTool({ - abortSignal: controller.signal, - messages: emptyMessages, - toolCall, - tools, - }), - ).rejects.toMatchObject({ name: 'AbortError' }) - }) - - it('repairs invalid tool call when repairToolCall returns a valid call', async () => { - const tools = [ - createTool('goodTool', async () => 'repaired'), - ] - const toolCall = createToolCall({ function: { name: 'badTool', arguments: '{}' } }) - - const out = await executeTool({ - messages: emptyMessages, - repairToolCall: async () => ({ - id: 'call_1', - type: 'function', - function: { name: 'goodTool', arguments: '{}' }, - }), - toolCall, - tools, - }) - - expect(out.completionToolResult.isError).toBeUndefined() - expect(out.completionToolResult.result).toBe('repaired') - }) - - it('returns error when repairToolCall returns null', async () => { - const tools = [createTool('goodTool', async () => 'x')] - const toolCall = createToolCall({ function: { name: 'badTool', arguments: '{}' } }) - - const out = await executeTool({ - captureToolErrors: true, - messages: emptyMessages, - repairToolCall: async () => null, - toolCall, - tools, - }) - - expect(out.completionToolResult.isError).toBe(true) - expect(InvalidToolCallError.isInstance(out.completionToolResult.error)).toBe(true) - }) - - it('invokes lifecycle callbacks on success and on error', async () => { - const onToolCallStart = vi.fn() - const onToolCallFinish = vi.fn() - const tools = [createTool('myTool', async () => 'done')] - - const successCall = createToolCall() - const successOut = await executeTool({ - messages: emptyMessages, - onToolCallFinish, - onToolCallStart, - toolCall: successCall, - tools, - }) - - expect(onToolCallStart).toHaveBeenCalledOnce() - expect(onToolCallStart).toHaveBeenCalledWith({ - input: {}, - toolCallId: 'call_1', - toolName: 'myTool', - }) - expect(onToolCallFinish).toHaveBeenCalled() - const successFinish = onToolCallFinish.mock.calls[0][0] - expect(successFinish.toolName).toBe('myTool') - expect(successFinish.toolCallId).toBe('call_1') - expect(successFinish.output).toBe(successOut.completionToolResult.result) - expect(successFinish.error).toBeUndefined() - expect(typeof successFinish.durationMs).toBe('number') - - onToolCallStart.mockClear() - onToolCallFinish.mockClear() - - const badCall = createToolCall({ function: { name: 'nope', arguments: '{}' } }) - await executeTool({ - captureToolErrors: true, - messages: emptyMessages, - onToolCallFinish, - onToolCallStart, - toolCall: badCall, - tools, - }) - - expect(onToolCallStart).not.toHaveBeenCalled() - expect(onToolCallFinish).toHaveBeenCalledOnce() - const errFinish = onToolCallFinish.mock.calls[0][0] - expect(errFinish.output).toBeUndefined() - expect(InvalidToolCallError.isInstance(errFinish.error)).toBe(true) - }) - - describe('without captureToolErrors (default upstream behavior)', () => { - it('throws InvalidToolCallError for unknown tool', async () => { - const tools = [createTool('other', async () => 'x')] - const toolCall = createToolCall({ function: { name: 'missingTool', arguments: '{}' } }) - - let thrown: unknown - try { - await executeTool({ - messages: emptyMessages, - toolCall, - tools, - }) - expect.fail('expected executeTool to throw') - } - catch (error) { - thrown = error - } - expect(InvalidToolCallError.isInstance(thrown)).toBe(true) - }) - - it('throws InvalidToolInputError for invalid JSON arguments', async () => { - const tools = [createTool('myTool', async () => 'x')] - const toolCall = createToolCall({ function: { name: 'myTool', arguments: '{broken' } }) - - let thrown: unknown - try { - await executeTool({ - messages: emptyMessages, - toolCall, - tools, - }) - expect.fail('expected executeTool to throw') - } - catch (error) { - thrown = error - } - expect(InvalidToolInputError.isInstance(thrown)).toBe(true) - }) - - it('throws ToolExecutionError when tool execute rejects', async () => { - const tools = [ - createTool('myTool', async () => { - throw new Error('execute failed') - }), - ] - const toolCall = createToolCall() - - let thrown: unknown - try { - await executeTool({ - messages: emptyMessages, - toolCall, - tools, - }) - expect.fail('expected executeTool to throw') - } - catch (error) { - thrown = error - } - expect(ToolExecutionError.isInstance(thrown)).toBe(true) - }) - - it('throws InvalidToolCallError when repairToolCall returns null', async () => { - const tools = [createTool('goodTool', async () => 'x')] - const toolCall = createToolCall({ function: { name: 'badTool', arguments: '{}' } }) - - let thrown: unknown - try { - await executeTool({ - messages: emptyMessages, - repairToolCall: async () => null, - toolCall, - tools, - }) - expect.fail('expected executeTool to throw') - } - catch (error) { - thrown = error - } - expect(InvalidToolCallError.isInstance(thrown)).toBe(true) - }) - }) -}) diff --git a/patches/@xsai__generate-text@0.5.0-beta.8.patch b/patches/@xsai__generate-text@0.5.0-beta.8.patch deleted file mode 100644 index 1fed4619a..000000000 --- a/patches/@xsai__generate-text@0.5.0-beta.8.patch +++ /dev/null @@ -1,39 +0,0 @@ -diff --git a/dist/index.d.ts b/dist/index.d.ts -index c9cc51d8b715b6f220d67574b8c674247d032926..9b4c2e6b096a0a139792d3fd4ce89c2f15bfc51e 100644 ---- a/dist/index.d.ts -+++ b/dist/index.d.ts -@@ -1,11 +1,15 @@ - import { WithUnknown } from '@xsai/shared'; --import { ChatOptions, CompletionStep, PostToolCall, PrepareStep, PreToolCall, StopCondition, Message, Usage, FinishReason, AssistantMessage, ChatCompletionUsage, CompletionToolCall, CompletionToolResult } from '@xsai/shared-chat'; -+import { ChatOptions, CompletionStep, OnToolCallFinishCallback, OnToolCallStartCallback, PostToolCall, PrepareStep, PreToolCall, RepairToolCallFunction, StopCondition, Message, Usage, FinishReason, AssistantMessage, ChatCompletionUsage, CompletionToolCall, CompletionToolResult } from '@xsai/shared-chat'; - - interface GenerateTextOptions extends ChatOptions { - onStepFinish?: (step: CompletionStep) => Promise | unknown; -+ captureToolErrors?: boolean; -+ onToolCallFinish?: OnToolCallFinishCallback; -+ onToolCallStart?: OnToolCallStartCallback; - postToolCall?: PostToolCall; - prepareStep?: PrepareStep; - preToolCall?: PreToolCall; -+ repairToolCall?: RepairToolCallFunction; - /** @internal */ - steps?: CompletionStep[]; - /** @default `stepCountAtLeast(1)` */ -diff --git a/dist/index.js b/dist/index.js -index d4fdab8b13f83fc2eb55eca0d15919133347393e..334428415f242fc6b11c874e988bc98187c3eadc 100644 ---- a/dist/index.js -+++ b/dist/index.js -@@ -55,9 +55,13 @@ const rawGenerateText = async (options) => { - const results = await Promise.all( - msgToolCalls.map(async (toolCall) => executeTool({ - abortSignal: options.abortSignal, -+ captureToolErrors: options.captureToolErrors, - messages, -+ onToolCallFinish: options.onToolCallFinish, -+ onToolCallStart: options.onToolCallStart, - postToolCall: options.postToolCall, - preToolCall: options.preToolCall, -+ repairToolCall: options.repairToolCall, - toolCall, - tools: options.tools - })) diff --git a/patches/@xsai__shared-chat@0.5.0-beta.8.patch b/patches/@xsai__shared-chat@0.5.0-beta.8.patch deleted file mode 100644 index 261775347..000000000 --- a/patches/@xsai__shared-chat@0.5.0-beta.8.patch +++ /dev/null @@ -1,211 +0,0 @@ -diff --git a/dist/index.d.ts b/dist/index.d.ts -index b327ec1c9dda3a8c4ddf39beb6a7b4ffc6afb860..8f740ffc45080cf6b80155e62fa87b090aa8efff 100644 ---- a/dist/index.d.ts -+++ b/dist/index.d.ts -@@ -94,6 +94,7 @@ interface CompletionToolCall { - } - interface CompletionToolResult { - args: unknown; -+ error?: unknown; - isError?: boolean; - result: ToolExecuteResult; - toolCallId: string; -@@ -101,6 +102,24 @@ interface CompletionToolResult { - } - type PostToolCall = (toolResult: CompletionToolResult, options: ToolExecuteOptions) => CompletionToolResult | Promise | void; - type PreToolCall = (toolCall: CompletionToolCall, options: ToolExecuteOptions) => CompletionToolCall | CompletionToolResult | Promise | void; -+type OnToolCallFinishCallback = (context: { -+ durationMs: number; -+ error?: unknown; -+ output?: unknown; -+ toolCallId: string; -+ toolName: string; -+}) => Promise | void; -+type OnToolCallStartCallback = (context: { -+ input: unknown; -+ toolCallId: string; -+ toolName: string; -+}) => Promise | void; -+type RepairToolCallFunction = (context: { -+ error: Error; -+ messages: Message[]; -+ toolCall: ToolCall; -+ tools?: Tool[]; -+}) => Promise | ToolCall | null; - interface Tool { - execute: (input: unknown, options: ToolExecuteOptions) => Promise | ToolExecuteResult; - function: { -@@ -275,9 +294,13 @@ declare const chat: >(options: T) => Promise< - - interface ExecuteToolOptions { - abortSignal?: AbortSignal; -+ captureToolErrors?: boolean; - messages: Message[]; -+ onToolCallFinish?: OnToolCallFinishCallback; -+ onToolCallStart?: OnToolCallStartCallback; - postToolCall?: PostToolCall; - preToolCall?: PreToolCall; -+ repairToolCall?: RepairToolCallFunction; - toolCall: ToolCall; - tools?: Tool[]; - wrapResult?: (result: ToolExecuteResult) => T; -@@ -285,10 +308,11 @@ interface ExecuteToolOptions { - interface ExecuteToolResult { - completionToolCall: CompletionToolCall; - completionToolResult: CompletionToolResult; -+ message: ToolMessage; - result: T; - } - declare const toCompletionToolCall: (toolCall: ToolCall) => CompletionToolCall; --declare const executeTool: ({ abortSignal, messages, postToolCall, preToolCall, toolCall, tools, wrapResult }: ExecuteToolOptions) => Promise>; -+declare const executeTool: (options: ExecuteToolOptions) => Promise>; - - interface ResolvePrepareStepOptions { - input: TInput; -@@ -317,4 +341,4 @@ declare const computeTotalUsage: (totalUsage: undefined | Usage, usage: Usage) = - declare const normalizeChatCompletionUsage: (usage: ChatCompletionUsage) => Usage; - - export { and, chat, computeTotalUsage, executeTool, hasToolCall, normalizeChatCompletionUsage, not, or, resolvePrepareStep, shouldStop, stepCountAtLeast, toCompletionToolCall }; --export type { AssistantMessage, AudioContentPart, ChatCompletionUsage, ChatOptions, CommonContentPart, CompletionStep, CompletionToolCall, CompletionToolResult, DeveloperMessage, ErrorEvent, Event, EventType, ExecuteToolOptions, ExecuteToolResult, FileContentPart, FinishReason, ImageContentPart, Message, PostToolCall, PreToolCall, PrepareStep, PrepareStepOptions, PrepareStepResult, ReasoningDeltaEvent, ReasoningDoneEvent, ReasoningStartEvent, RefusalContentPart, ResolvePrepareStepOptions, ResolvePrepareStepResult, StepDoneEvent, StepStartEvent, StopCondition, StopContext, SystemMessage, TextContentPart, TextDeltaEvent, TextDoneEvent, TextStartEvent, Tool, ToolCall, ToolCallDeltaEvent, ToolCallDoneEvent, ToolCallStartEvent, ToolChoice, ToolExecuteOptions, ToolExecuteResult, ToolMessage, ToolResultDoneEvent, ToolValidateFailure, ToolValidateResult, ToolValidateSuccess, Usage, UserMessage }; -+export type { AssistantMessage, AudioContentPart, ChatCompletionUsage, ChatOptions, CommonContentPart, CompletionStep, CompletionToolCall, CompletionToolResult, DeveloperMessage, ErrorEvent, Event, EventType, ExecuteToolOptions, ExecuteToolResult, FileContentPart, FinishReason, ImageContentPart, Message, OnToolCallFinishCallback, OnToolCallStartCallback, PostToolCall, PreToolCall, PrepareStep, PrepareStepOptions, PrepareStepResult, RepairToolCallFunction, ReasoningDeltaEvent, ReasoningDoneEvent, ReasoningStartEvent, RefusalContentPart, ResolvePrepareStepOptions, ResolvePrepareStepResult, StepDoneEvent, StepStartEvent, StopCondition, StopContext, SystemMessage, TextContentPart, TextDeltaEvent, TextDoneEvent, TextStartEvent, Tool, ToolCall, ToolCallDeltaEvent, ToolCallDoneEvent, ToolCallStartEvent, ToolChoice, ToolExecuteOptions, ToolExecuteResult, ToolMessage, ToolResultDoneEvent, ToolValidateFailure, ToolValidateResult, ToolValidateSuccess, Usage, UserMessage }; -diff --git a/dist/index.js b/dist/index.js -index d8fa061c8fe9ba9b76aafea74f40e27a957b0a17..45373a31d56a3127377789177e28e609c99fca64 100644 ---- a/dist/index.js -+++ b/dist/index.js -@@ -1,4 +1,4 @@ --import { postJSON, InvalidToolCallError, InvalidToolInputError } from '@xsai/shared'; -+import { postJSON, InvalidToolCallError, InvalidToolInputError, ToolExecutionError } from '@xsai/shared'; - - const chat = async (options) => postJSON("chat/completions", { - ...options, -@@ -56,15 +56,19 @@ const parseToolInput = async (tool, input) => { - }; - const createErrorToolResult = (toolCall, args, cause, abortSignal) => ({ - args, -+ error: cause, - isError: true, - result: `Tool "${toolCall.toolName}" execution failed: ${abortSignal?.aborted === true ? "This operation was aborted" : cause instanceof Error ? cause.message : String(cause)}`, - toolCallId: toolCall.toolCallId, - toolName: toolCall.toolName - }); --const catchToolError = async (toolCall, abortSignal, callback) => { -+const isAbortError = (error, abortSignal) => abortSignal?.aborted === true || error instanceof Error && error.name === "AbortError"; -+const catchToolError = async (toolCall, abortSignal, captureToolErrors, callback) => { - try { - return await callback(toolCall); - } catch (cause) { -+ if (isAbortError(cause, abortSignal) || !captureToolErrors) -+ throw cause; - return createErrorToolResult(toolCall, InvalidToolInputError.isInstance(cause) ? cause.toolInput : toolCall.args, cause, abortSignal); - } - }; -@@ -90,7 +94,7 @@ const findTool = (tools, toolName, toolCall) => { - } - return tool; - }; --const executeTool = async ({ abortSignal, messages, postToolCall, preToolCall, toolCall, tools, wrapResult }) => { -+const executeToolBase = async ({ abortSignal, captureToolErrors, messages, postToolCall, preToolCall, toolCall, tools, wrapResult }) => { - const wrap = wrapResult ?? toToolMessageContent; - const toolName = toolCall.function.name; - const toolArguments = toolCall.function.arguments; -@@ -120,7 +124,7 @@ const executeTool = async ({ abortSignal, messages, postToolCall, preToolCall, t - let completionToolResult; - let parsedArgs; - let shouldPostToolCall = false; -- const preToolCallResult = await catchToolError(completionToolCall, abortSignal, async (toolCall2) => preToolCall?.(toolCall2, toolExecuteOptions)); -+ const preToolCallResult = await catchToolError(completionToolCall, abortSignal, captureToolErrors, async (toolCall2) => preToolCall?.(toolCall2, toolExecuteOptions)); - if (preToolCallResult) { - assertSameToolCallId(completionToolCall.toolCallId, preToolCallResult, "preToolCallResult"); - if ("result" in preToolCallResult) -@@ -128,13 +132,25 @@ const executeTool = async ({ abortSignal, messages, postToolCall, preToolCall, t - else - completionToolCall = preToolCallResult; - } -- completionToolResult ??= await catchToolError(completionToolCall, abortSignal, async () => { -+ completionToolResult ??= await catchToolError(completionToolCall, abortSignal, captureToolErrors, async () => { - const tool = findTool(tools, completionToolCall.toolName, completionToolCall); - parsedArgs = await parseToolInput(tool, completionToolCall.args); - if (abortSignal?.aborted === true) - return createErrorToolResult(completionToolCall, parsedArgs, abortSignal.reason, abortSignal); - shouldPostToolCall = true; -- const result = await tool.execute(parsedArgs, toolExecuteOptions); -+ let result; -+ try { -+ result = await tool.execute(parsedArgs, toolExecuteOptions); -+ } catch (cause) { -+ if (isAbortError(cause, abortSignal)) -+ throw cause; -+ throw new ToolExecutionError(`Tool "${completionToolCall.toolName}" execution failed.`, { -+ cause, -+ toolCallId: completionToolCall.toolCallId, -+ toolInput: parsedArgs, -+ toolName: completionToolCall.toolName -+ }); -+ } - return { - args: parsedArgs, - result, -@@ -144,7 +160,7 @@ const executeTool = async ({ abortSignal, messages, postToolCall, preToolCall, t - }); - if (shouldPostToolCall) { - completionToolResult.args = parsedArgs; -- const postToolCallResult = await catchToolError(completionToolResult, abortSignal, async (toolResult) => postToolCall?.(toolResult, toolExecuteOptions)); -+ const postToolCallResult = await catchToolError(completionToolResult, abortSignal, captureToolErrors, async (toolResult) => postToolCall?.(toolResult, toolExecuteOptions)); - if (postToolCallResult) { - assertSameToolCallId(completionToolResult.toolCallId, postToolCallResult, "postToolCallResult"); - completionToolResult = postToolCallResult; -@@ -156,6 +172,54 @@ const executeTool = async ({ abortSignal, messages, postToolCall, preToolCall, t - result: wrap(completionToolResult.result) - }; - }; -+const callToolLifecycle = async (callback, context) => { -+ try { -+ await callback?.(context); -+ } catch { -+ } -+}; -+const executeTool = async (options) => { -+ const { abortSignal, captureToolErrors, messages, onToolCallFinish, onToolCallStart, repairToolCall, toolCall, tools } = options; -+ const toolCallId = toolCall.id; -+ const toolName = toolCall.function?.name ?? "unknown"; -+ const startTime = Date.now(); -+ try { -+ const execution = await executeToolBase(options); -+ const failure = execution.completionToolResult.error; -+ if (failure instanceof Error && repairToolCall && (InvalidToolCallError.isInstance(failure) || InvalidToolInputError.isInstance(failure))) { -+ const repaired = await repairToolCall({ error: failure, messages, toolCall, tools }); -+ if (repaired != null) -+ return executeTool({ ...options, repairToolCall: void 0, toolCall: repaired }); -+ } -+ if (!execution.completionToolResult.isError) -+ await callToolLifecycle(onToolCallStart, { input: execution.completionToolResult.args, toolCallId, toolName }); -+ await callToolLifecycle(onToolCallFinish, { -+ durationMs: Date.now() - startTime, -+ error: execution.completionToolResult.error, -+ output: execution.completionToolResult.isError ? void 0 : execution.completionToolResult.result, -+ toolCallId, -+ toolName -+ }); -+ return { -+ ...execution, -+ message: { -+ content: execution.result, -+ role: "tool", -+ tool_call_id: execution.completionToolCall.toolCallId -+ } -+ }; -+ } catch (error) { -+ if (isAbortError(error, abortSignal)) -+ throw error; -+ if (error instanceof Error && repairToolCall && (InvalidToolCallError.isInstance(error) || InvalidToolInputError.isInstance(error))) { -+ const repaired = await repairToolCall({ error, messages, toolCall, tools }); -+ if (repaired != null) -+ return executeTool({ ...options, repairToolCall: void 0, toolCall: repaired }); -+ } -+ await callToolLifecycle(onToolCallFinish, { durationMs: Date.now() - startTime, error, toolCallId, toolName }); -+ throw error; -+ } -+}; - - const resolvePrepareStep = async ({ input, model, prepareStep, stepNumber, steps, toolChoice }) => { - const prepared = prepareStep == null ? void 0 : await prepareStep({ diff --git a/patches/@xsai__stream-text@0.5.0-beta.8.patch b/patches/@xsai__stream-text@0.5.0-beta.8.patch deleted file mode 100644 index 456ce7883..000000000 --- a/patches/@xsai__stream-text@0.5.0-beta.8.patch +++ /dev/null @@ -1,200 +0,0 @@ -diff --git a/dist/index.d.ts b/dist/index.d.ts -index a596849dec1316dd84e4261650aa6b439327a4f5..b238d42480e4654cdb89d0619540275b7796e80d 100644 ---- a/dist/index.d.ts -+++ b/dist/index.d.ts -@@ -1,5 +1,35 @@ - import { WithUnknown } from '@xsai/shared'; --import { ToolCall, FinishReason, ChatCompletionUsage, ChatOptions, Event, CompletionStep, PostToolCall, PrepareStep, PreToolCall, StopCondition, Message, Usage } from '@xsai/shared-chat'; -+import { CompletionToolCall, CompletionToolResult, ToolCall, FinishReason, ChatCompletionUsage, ChatOptions, CompletionStep, OnToolCallFinishCallback, OnToolCallStartCallback, PostToolCall, PrepareStep, PreToolCall, RepairToolCallFunction, StopCondition, Message, Usage } from '@xsai/shared-chat'; -+ -+type StreamTextEvent = (CompletionToolCall & { -+ type: 'tool-call'; -+}) | (CompletionToolResult & { -+ type: 'tool-error'; -+}) | (CompletionToolResult & { -+ type: 'tool-result'; -+}) | { -+ argsTextDelta: string; -+ toolCallId: string; -+ toolName: string; -+ type: 'tool-call-delta'; -+} | { -+ error: unknown; -+ type: 'error'; -+} | { -+ finishReason: FinishReason; -+ type: 'finish'; -+ usage?: Usage; -+} | { -+ text: string; -+ type: 'reasoning-delta'; -+} | { -+ text: string; -+ type: 'text-delta'; -+} | { -+ toolCallId: string; -+ toolName: string; -+ type: 'tool-call-streaming-start'; -+}; - - interface StreamTextChunkResult { - choices: { -@@ -27,12 +57,16 @@ interface StreamTextChunkResult { - } - - interface StreamTextOptions extends ChatOptions { -- onEvent?: (event: Event) => Promise | unknown; -+ onEvent?: (event: StreamTextEvent) => Promise | unknown; - onFinish?: (step?: CompletionStep) => Promise | unknown; - onStepFinish?: (step: CompletionStep) => Promise | unknown; -+ captureToolErrors?: boolean; -+ onToolCallFinish?: OnToolCallFinishCallback; -+ onToolCallStart?: OnToolCallStartCallback; - postToolCall?: PostToolCall; - prepareStep?: PrepareStep; - preToolCall?: PreToolCall; -+ repairToolCall?: RepairToolCallFunction; - /** @default `stepCountAtLeast(1)` */ - stopWhen?: StopCondition; - /** -@@ -48,8 +82,8 @@ interface StreamTextOptions extends ChatOptions { - }; - } - interface StreamTextResult { -- eventStream: ReadableStream; -- fullStream: ReadableStream; -+ eventStream: ReadableStream; -+ fullStream: ReadableStream; - messages: Promise; - reasoningTextStream: ReadableStream; - steps: Promise; -@@ -60,4 +94,4 @@ interface StreamTextResult { - declare const streamText: (options: WithUnknown) => StreamTextResult; - - export { streamText }; --export type { StreamTextChunkResult, StreamTextOptions, StreamTextResult }; -+export type { StreamTextChunkResult, StreamTextEvent, StreamTextOptions, StreamTextResult }; -diff --git a/dist/index.js b/dist/index.js -index ec6e01b0defd49370f75ed50d975aa6da8c03faf..4a956cc07f62049de4fe2ea10ba8c3fba2875ecb 100644 ---- a/dist/index.js -+++ b/dist/index.js -@@ -63,7 +63,6 @@ const streamText = (options) => { - let finishReason = "other"; - let reasoningStarted = false; - let textStarted = false; -- pushEvent({ type: "step.start" }); - await stream.pipeThrough(new TextDecoderStream()).pipeThrough(new EventSourceParserStream()).pipeThrough(new JsonMessageTransformStream()).pipeTo(new WritableStream({ - abort: (reason) => { - errorControllers(reason, eventCtrl, fullCtrl, textCtrl, reasoningTextCtrl); -@@ -82,18 +81,16 @@ const streamText = (options) => { - reasoningField = "reasoning"; - if (!reasoningStarted) { - reasoningStarted = true; -- pushEvent({ type: "reasoning.start" }); - } -- pushEvent({ delta: choice.delta.reasoning, type: "reasoning.delta" }); -+ pushEvent({ text: choice.delta.reasoning, type: "reasoning-delta" }); - pushReasoningText(choice.delta.reasoning); - } else if (choice.delta.reasoning_content != null) { - if (reasoningField !== "reasoning_content") - reasoningField = "reasoning_content"; - if (!reasoningStarted) { - reasoningStarted = true; -- pushEvent({ type: "reasoning.start" }); - } -- pushEvent({ delta: choice.delta.reasoning_content, type: "reasoning.delta" }); -+ pushEvent({ text: choice.delta.reasoning_content, type: "reasoning-delta" }); - pushReasoningText(choice.delta.reasoning_content); - } - if (choice.finish_reason != null) -@@ -102,16 +99,14 @@ const streamText = (options) => { - if (choice.delta.content != null) { - if (!textStarted) { - textStarted = true; -- pushEvent({ type: "text.start" }); - } -- pushEvent({ delta: choice.delta.content, type: "text.delta" }); -+ pushEvent({ text: choice.delta.content, type: "text-delta" }); - pushText(choice.delta.content); - } else if (choice.delta.refusal != null) { - if (!textStarted) { - textStarted = true; -- pushEvent({ type: "text.start" }); - } -- pushEvent({ delta: choice.delta.refusal, type: "text.delta" }); -+ pushEvent({ text: choice.delta.refusal, type: "text-delta" }); - pushText(choice.delta.refusal); - } - } else { -@@ -125,21 +120,17 @@ const streamText = (options) => { - arguments: toolCall.function.arguments ?? "" - } - }; -- pushEvent({ toolCallId: toolCall.id, toolName: toolCall.function.name, type: "tool-call.start" }); -+ pushEvent({ toolCallId: toolCall.id, toolName: toolCall.function.name, type: "tool-call-streaming-start" }); - if (toolCall.function.arguments != null && toolCall.function.arguments.length > 0) -- pushEvent({ delta: toolCall.function.arguments, type: "tool-call.delta" }); -+ pushEvent({ argsTextDelta: toolCall.function.arguments, toolCallId: toolCall.id, toolName: toolCall.function.name, type: "tool-call-delta" }); - } else { - tool_calls[index].function.arguments += toolCall.function.arguments; -- pushEvent({ delta: toolCall.function.arguments, type: "tool-call.delta" }); -+ pushEvent({ argsTextDelta: toolCall.function.arguments, toolCallId: toolCall.id, toolName: toolCall.function.name ?? tool_calls[index].function.name, type: "tool-call-delta" }); - } - } - } - } - })); -- if (reasoningStarted) -- pushEvent({ content: reasoningText ?? "", type: "reasoning.done" }); -- if (textStarted) -- pushEvent({ content: text, type: "text.done" }); - messages.push({ - ...reasoningField != null ? { [reasoningField]: reasoningText } : {}, - content: text, -@@ -152,7 +143,7 @@ const streamText = (options) => { - if (options.abortSignal?.aborted === true) - throw options.abortSignal.reason ?? new Error("This operation was aborted"); - for (const toolCall of toolCalls) -- pushEvent({ ...toolCall, type: "tool-call.done" }); -+ pushEvent({ ...toolCall, type: "tool-call" }); - const step = { - finishReason, - text, -@@ -169,9 +160,13 @@ const streamText = (options) => { - const results = await Promise.all( - validToolCalls.map(async (toolCall) => executeTool({ - abortSignal: options.abortSignal, -+ captureToolErrors: options.captureToolErrors, - messages, -+ onToolCallFinish: options.onToolCallFinish, -+ onToolCallStart: options.onToolCallStart, - postToolCall: options.postToolCall, - preToolCall: options.preToolCall, -+ repairToolCall: options.repairToolCall, - toolCall, - tools: options.tools - })) -@@ -185,12 +180,12 @@ const streamText = (options) => { - role: "tool", - tool_call_id: completionToolCall.toolCallId - }); -- pushEvent({ ...completionToolResult, type: "tool-result.done" }); -+ pushEvent({ ...completionToolResult, type: completionToolResult.isError ? "tool-error" : "tool-result" }); - } - } - const willContinue = validToolCalls.length > 0 && !stop && !options.abortSignal?.aborted; - pushStep(step); -- pushEvent({ type: "step.done", usage }); -+ pushEvent({ finishReason, type: "finish", usage }); - if (willContinue) - return async () => doStream(); - }; -@@ -222,7 +217,7 @@ const streamText = (options) => { - })(); - return { - eventStream, -- fullStream, -+ fullStream: eventStream, - messages: resultMessages.promise, - reasoningTextStream, - steps: resultSteps.promise, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a3efaf8be..85357d964 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1162,8 +1162,8 @@ catalogs: specifier: ^4.0.0 version: 4.0.0 xsschema: - specifier: 0.5.0-beta.2 - version: 0.5.0-beta.2 + specifier: 0.5.0-beta.8 + version: 0.5.0-beta.8 yaml: specifier: ^2.8.3 version: 2.8.3 @@ -1205,15 +1205,6 @@ overrides: packageExtensionsChecksum: sha256-lLxz4me6BFXSsuohDen54RU9DmiItse34Xa+IWGtSzU= patchedDependencies: - '@xsai/generate-text@0.5.0-beta.8': - hash: 9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6 - path: patches/@xsai__generate-text@0.5.0-beta.8.patch - '@xsai/shared-chat@0.5.0-beta.8': - hash: dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50 - path: patches/@xsai__shared-chat@0.5.0-beta.8.patch - '@xsai/stream-text@0.5.0-beta.8': - hash: 55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229 - path: patches/@xsai__stream-text@0.5.0-beta.8.patch mineflayer-pathfinder: hash: 4bbfdca823ab48b74086e6e7d4b2f9baf5ee7c0ba9aee0c279b3c91c50bfd797 path: patches/mineflayer-pathfinder.patch @@ -1383,13 +1374,13 @@ importers: version: 14.2.1(vue@3.5.32(typescript@5.9.3)) '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 '@xsai/stream-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229) + version: 0.5.0-beta.8 valibot: specifier: 'catalog:' version: 1.4.2(typescript@5.9.3) @@ -1398,7 +1389,7 @@ importers: version: 3.5.32(typescript@5.9.3) xsschema: specifier: 'catalog:' - version: 0.5.0-beta.2(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + version: 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) devDependencies: '@iconify-json/solar': specifier: 'catalog:' @@ -1537,7 +1528,7 @@ importers: version: 0.5.0-beta.8 '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/model': specifier: 'catalog:' version: 0.5.0-beta.8 @@ -1546,10 +1537,10 @@ importers: version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 '@xsai/stream-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229) + version: 0.5.0-beta.8 '@xsai/stream-transcription': specifier: 'catalog:' version: 0.5.0-beta.8 @@ -1678,7 +1669,7 @@ importers: version: 7.4.0 xsschema: specifier: 'catalog:' - version: 0.5.0-beta.2(@valibot/to-json-schema@1.0.0-rc.0(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + version: 0.5.0-beta.8(@valibot/to-json-schema@1.0.0-rc.0(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) yauzl: specifier: 'catalog:' version: 3.3.0 @@ -1958,7 +1949,7 @@ importers: version: 0.5.0-beta.8 '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/model': specifier: 'catalog:' version: 0.5.0-beta.8 @@ -1967,10 +1958,10 @@ importers: version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 '@xsai/stream-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229) + version: 0.5.0-beta.8 '@xsai/stream-transcription': specifier: 'catalog:' version: 0.5.0-beta.8 @@ -2123,7 +2114,7 @@ importers: version: 5.0.0 xsschema: specifier: 'catalog:' - version: 0.5.0-beta.2(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + version: 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) zod: specifier: 'catalog:' version: 4.4.3 @@ -2433,7 +2424,7 @@ importers: version: 0.5.0-beta.8 '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/model': specifier: 'catalog:' version: 0.5.0-beta.8 @@ -2442,10 +2433,10 @@ importers: version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 '@xsai/stream-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229) + version: 0.5.0-beta.8 '@xsai/stream-transcription': specifier: 'catalog:' version: 0.5.0-beta.8 @@ -2577,7 +2568,7 @@ importers: version: 7.4.0 xsschema: specifier: 'catalog:' - version: 0.5.0-beta.2(@valibot/to-json-schema@1.0.0-rc.0(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + version: 0.5.0-beta.8(@valibot/to-json-schema@1.0.0-rc.0(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) yauzl: specifier: 'catalog:' version: 3.3.0 @@ -3080,13 +3071,13 @@ importers: version: 0.5.0-beta.8 '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/generate-transcription': specifier: 'catalog:' version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 discord.js: specifier: 'catalog:' version: 14.26.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -3119,10 +3110,10 @@ importers: version: link:../../packages/server-sdk '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 alien-signals: specifier: 'catalog:' version: 3.1.2 @@ -3228,10 +3219,10 @@ importers: version: 0.4.0(typescript@5.9.3) '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 '@xsai/utils-chat': specifier: 'catalog:' version: 0.5.0-beta.8 @@ -3319,10 +3310,10 @@ importers: version: 0.5.0-beta.8 '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 '@xsai/tool': specifier: 'catalog:' version: 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) @@ -3550,7 +3541,7 @@ importers: version: 0.5.0-beta.8 '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/model': specifier: 'catalog:' version: 0.5.0-beta.8 @@ -3559,10 +3550,10 @@ importers: version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 '@xsai/stream-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229) + version: 0.5.0-beta.8 '@xsai/tool': specifier: 'catalog:' version: 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) @@ -3586,7 +3577,7 @@ importers: version: 0.0.5(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(@vitest/browser-playwright@4.1.4)(@vitest/coverage-v8@4.1.4)(chokidar@5.0.0)(dotenv@17.4.2)(esbuild@0.27.2)(giget@2.0.0)(jiti@2.7.0)(jsdom@29.1.1(@noble/hashes@2.0.1)(canvas@3.2.3))(less@4.6.4)(magicast@0.5.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) xsschema: specifier: 'catalog:' - version: 0.5.0-beta.2(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + version: 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) zod: specifier: 'catalog:' version: 4.4.3 @@ -3788,7 +3779,7 @@ importers: version: 1.0.0-beta.14(electron@41.2.1)(h3@2.0.1-rc.25)(hono@4.11.3)(web-worker@1.5.0) '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 packages/plugin-sdk: dependencies: @@ -3828,7 +3819,7 @@ importers: version: 1.4.2(typescript@5.9.3) xsschema: specifier: 'catalog:' - version: 0.5.0-beta.2(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + version: 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) packages/scenarios-stage-tamagotchi-browser: dependencies: @@ -4172,7 +4163,7 @@ importers: version: 2.0.9(vue@3.5.32(typescript@5.9.3)) xsschema: specifier: 'catalog:' - version: 0.5.0-beta.2(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + version: 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) zod: specifier: 'catalog:' version: 4.4.3 @@ -4369,7 +4360,7 @@ importers: version: 0.5.0-beta.8 '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/generate-transcription': specifier: 'catalog:' version: 0.5.0-beta.8 @@ -4381,10 +4372,10 @@ importers: version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 '@xsai/stream-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229) + version: 0.5.0-beta.8 '@xsai/stream-transcription': specifier: 'catalog:' version: 0.5.0-beta.8 @@ -4537,7 +4528,7 @@ importers: version: 4.0.0 xsschema: specifier: 'catalog:' - version: 0.5.0-beta.2(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + version: 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) zod: specifier: 'catalog:' version: 4.4.3 @@ -5243,13 +5234,13 @@ importers: version: 0.5.0-beta.8 '@xsai/generate-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) + version: 0.5.0-beta.8 '@xsai/shared-chat': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + version: 0.5.0-beta.8 '@xsai/stream-text': specifier: 'catalog:' - version: 0.5.0-beta.8(patch_hash=55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229) + version: 0.5.0-beta.8 pinia: specifier: 'catalog:' version: 3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)) @@ -5261,7 +5252,7 @@ importers: version: 0.0.5(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(@vitest/browser-playwright@4.1.4)(@vitest/coverage-v8@4.1.4)(chokidar@5.0.0)(dotenv@17.4.2)(esbuild@0.27.2)(giget@2.0.0)(jiti@2.7.0)(jsdom@29.1.1(@noble/hashes@2.0.1)(canvas@3.2.3))(less@4.6.4)(magicast@0.5.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) xsschema: specifier: 'catalog:' - version: 0.5.0-beta.2(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + version: 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) plugins/airi-plugin-homeassistant: dependencies: @@ -19841,29 +19832,6 @@ packages: resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} engines: {node: '>=0.4.0'} - xsschema@0.5.0-beta.2: - resolution: {integrity: sha512-zUtk3Ro6Gn39zn7fJRImwmLJmSOpao09dUxdLVyofr+RzfxEEdtm7UVJPe4lUGTJxUCzNptpRp549yD9uKWiug==} - peerDependencies: - '@valibot/to-json-schema': ^1.0.0 - arktype: ^2.1.20 - effect: ^3.16.0 - sury: ^10.0.0 || ^11.0.0-alpha - zod: ^3.25.0 || ^4.0.0 - zod-to-json-schema: ^3.25.0 - peerDependenciesMeta: - '@valibot/to-json-schema': - optional: true - arktype: - optional: true - effect: - optional: true - sury: - optional: true - zod: - optional: true - zod-to-json-schema: - optional: true - xsschema@0.5.0-beta.8: resolution: {integrity: sha512-y74st5jRCpO/EMhxi4MC2/GKpWVL2LL12utk5QK2ICCEXVOV6BvOLYp6dXkJBIb4BUCfaVCEei5/cY+JTv3CRw==} peerDependencies: @@ -20082,9 +20050,9 @@ snapshots: dependencies: '@moeru/std': 0.1.0-beta.20 '@valibot/to-json-schema': 1.7.1(valibot@1.4.2(typescript@5.9.3)) - '@xsai/generate-text': 0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6) - '@xsai/shared-chat': 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) - '@xsai/stream-text': 0.5.0-beta.8(patch_hash=55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229) + '@xsai/generate-text': 0.5.0-beta.8 + '@xsai/shared-chat': 0.5.0-beta.8 + '@xsai/stream-text': 0.5.0-beta.8 '@xsai/tool': 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) es-toolkit: 1.50.0 minimatch: 10.2.6 @@ -20224,8 +20192,8 @@ snapshots: dependencies: '@xsai-ext/responses': 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) '@xsai/shared': 0.5.0-beta.8 - '@xsai/shared-chat': 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) - '@xsai/stream-text': 0.5.0-beta.8(patch_hash=55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229) + '@xsai/shared-chat': 0.5.0-beta.8 + '@xsai/stream-text': 0.5.0-beta.8 '@xsai/tool': 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) yocto-queue: 1.2.2 transitivePeerDependencies: @@ -27191,7 +27159,7 @@ snapshots: '@xsai-ext/responses@0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3)': dependencies: '@xsai/shared': 0.5.0-beta.8 - '@xsai/shared-chat': 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + '@xsai/shared-chat': 0.5.0-beta.8 '@xsai/shared-stream': 0.5.0-beta.8 '@xsai/tool': 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) transitivePeerDependencies: @@ -27267,12 +27235,12 @@ snapshots: '@xsai/generate-text@0.5.0-beta.2': dependencies: '@xsai/shared': 0.5.0-beta.8 - '@xsai/shared-chat': 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + '@xsai/shared-chat': 0.5.0-beta.8 - '@xsai/generate-text@0.5.0-beta.8(patch_hash=9ff1a2c30f41026ddc970aec81e65e1d8073abd1ec2387bc4f017f4ccef65fc6)': + '@xsai/generate-text@0.5.0-beta.8': dependencies: '@xsai/shared': 0.5.0-beta.8 - '@xsai/shared-chat': 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + '@xsai/shared-chat': 0.5.0-beta.8 '@xsai/generate-transcription@0.4.4': dependencies: @@ -27286,7 +27254,7 @@ snapshots: dependencies: '@xsai/shared': 0.5.0-beta.8 - '@xsai/shared-chat@0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50)': + '@xsai/shared-chat@0.5.0-beta.8': dependencies: '@xsai/shared': 0.5.0-beta.8 @@ -27298,10 +27266,10 @@ snapshots: '@xsai/shared@0.5.0-beta.8': {} - '@xsai/stream-text@0.5.0-beta.8(patch_hash=55bbd5398e6758eb67e496fa21571cc2623ef2e91d7adc7fdee3ec1a13bd6229)': + '@xsai/stream-text@0.5.0-beta.8': dependencies: '@xsai/shared': 0.5.0-beta.8 - '@xsai/shared-chat': 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + '@xsai/shared-chat': 0.5.0-beta.8 '@xsai/shared-stream': 0.5.0-beta.8 '@xsai/stream-transcription@0.5.0-beta.8': @@ -27312,7 +27280,7 @@ snapshots: '@xsai/tool@0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3)': dependencies: '@xsai/shared': 0.5.0-beta.8 - '@xsai/shared-chat': 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + '@xsai/shared-chat': 0.5.0-beta.8 xsschema: 0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) transitivePeerDependencies: - '@valibot/to-json-schema' @@ -27324,7 +27292,7 @@ snapshots: '@xsai/utils-chat@0.5.0-beta.8': dependencies: - '@xsai/shared-chat': 0.5.0-beta.8(patch_hash=dc3d30181c5826cac0535c0885fcea4b4ea70651da3b28ff593574034718cf50) + '@xsai/shared-chat': 0.5.0-beta.8 abbrev@2.0.0: {} @@ -36558,18 +36526,12 @@ snapshots: xmlhttprequest-ssl@2.1.2: {} - xsschema@0.5.0-beta.2(@valibot/to-json-schema@1.0.0-rc.0(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3): + xsschema@0.5.0-beta.8(@valibot/to-json-schema@1.0.0-rc.0(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3): optionalDependencies: '@valibot/to-json-schema': 1.0.0-rc.0(valibot@1.4.2(typescript@5.9.3)) zod: 4.4.3 zod-to-json-schema: 3.25.2(zod@4.4.3) - xsschema@0.5.0-beta.2(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3): - optionalDependencies: - '@valibot/to-json-schema': 1.7.1(valibot@1.4.2(typescript@5.9.3)) - zod: 4.4.3 - zod-to-json-schema: 3.25.2(zod@4.4.3) - xsschema@0.5.0-beta.8(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@5.9.3)))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3): optionalDependencies: '@valibot/to-json-schema': 1.7.1(valibot@1.4.2(typescript@5.9.3)) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c67143471..e0f03afd2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -31,9 +31,6 @@ overrides: side-channel: npm:@nolyfill/side-channel@^1.0.44 string.prototype.matchall: npm:@nolyfill/string.prototype.matchall@^1.0.44 patchedDependencies: - '@xsai/generate-text@0.5.0-beta.8': patches/@xsai__generate-text@0.5.0-beta.8.patch - '@xsai/shared-chat@0.5.0-beta.8': patches/@xsai__shared-chat@0.5.0-beta.8.patch - '@xsai/stream-text@0.5.0-beta.8': patches/@xsai__stream-text@0.5.0-beta.8.patch mineflayer-pathfinder: patches/mineflayer-pathfinder.patch pixi-live2d-display: patches/pixi-live2d-display.patch sponsorkit@17.1.0: patches/sponsorkit@17.1.0.patch @@ -425,7 +422,7 @@ catalog: wxt: ^0.20.24 xast-util-to-xml: ^4.0.0 xastscript: ^4.0.0 - xsschema: 0.5.0-beta.2 + xsschema: 0.5.0-beta.8 yaml: ^2.8.3 yauzl: ^3.3.0 zod: ^4.3.6