mirror of
https://github.com/moeru-ai/airi.git
synced 2026-08-14 00:48:06 +00:00
feat(core-agent): bump xsai / remove patches (#2164)
## Summary - Bump catalog `@xsai/*`, `@xsai-ext/providers`, and `xsschema` to **0.5.0-beta.8**, and delete the three `@xsai/*` pnpm patches — the beta.2 ones from #1602 and the beta.8 regenerations that landed on main in `38a008500`. - Always capture tool failures on the core-agent chat path: xsAI beta.8 marks failed tool executions with `isError: true` on `tool-result.done`, and `llm-service.ts` maps that to AIRI's `tool-error` event via `toAiriStreamEvent`, so the agent loop continues instead of aborting. - Remove the `captureToolErrors` request flag (and stop forwarding it into `streamText`). - Rebased onto latest `main` (`b230e16b2`). Includes one follow-up fix: steps are marked settled before the finish listener runs, and finish listener failures still reject the stream. ### Related - Supersedes / follows up on [#1602](https://github.com/moeru-ai/airi/pull/1602) (`captureToolErrors` + xsai patches). ### Scope of capture | Case | Covered | | --- | --- | | A — unknown tool | yes | | B — invalid / unparseable arguments JSON | yes | | C — `validate` failure | yes | | D — `execute` throw | yes | | `missing_name` / `missing_arguments` | no (xsai still aborts) | | `repairToolCall` | no | Error copy on beta.8: `Tool "<toolName>" execution failed: …` (produced by xsAI). ## Test plan ### Automated (Vitest) - [x] core-agent `llm-service.test.ts` — 16/16 - [x] core-agent full suite — 82/82 - [x] stage-ui `llm.test.ts` + `chat.contract.test.ts` — 44/44 (the previous `stepsSettled` timing failure is fixed in this branch) - [x] stage-ui full suite — 594 passed / 0 failed (one browser-test-runner teardown error, not a test failure) - [x] typecheck — core-agent, stage-ui, component-calling, satori-bot pass; telegram-bot fails only at `src/utils/velin.ts`, which is pre-existing on main and untouched by this PR ### Real-environment E2E (rebase branch, DeepSeek V4 Flash via DeepSeek API) Harness: `/Users/lulu/GitHub/airi-e2e/pr2164/tool-error-e2e-rebase.mjs` — drives the built `core-agent` `streamFrom` with a deliberately failing tool. | Case | Result | Evidence | | --- | --- | --- | | D — execute throw | **pass** | `tool-error` carried `Tool "always_fail" execution failed: boom: deterministic tool failure`; the model answered: "The always_fail tool threw a deterministic error as expected." | | A — unknown tool | **pass** | The model called the unavailable `search_the_moon_database` after being told truthfully that this tests AIRI's error capture; runtime returned `tool-error` and the conversation continued. | | B — bad arguments JSON | not observed on real model | providers rarely emit invalid `arguments`; covered by unit test | | C — `validate` failure | pass (earlier manual run with a temporary validate-gated tool) | — | Evidence artifacts: `/Users/lulu/GitHub/airi-e2e/artifacts/pr2164/tool-error-e2e-2026-08-10T16-49-15-384Z.{json,log}` ### Notes / non-goals - Fallout-only updates for the xsai beta.8 API rename: `textStream`, `inputTokens` / `outputTokens` / `totalTokens` in component-calling / telegram / satori. --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Cursor
autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
parent
b230e16b2e
commit
fd8b7a0ae3
@@ -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) {
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import type { StreamTextEvent } from '@xsai/stream-text'
|
||||
|
||||
export function mockStreamText(): {
|
||||
fullStream: ReadableStream<StreamTextEvent>
|
||||
textStream: ReadableStream<string>
|
||||
} {
|
||||
const source = `<component_call><component_name>weather</component_name> \`\`\`json <component_props>{"city":"Shanghai","temperature":"29","condition":"cloudy"}</component_props> \`\`\`</component_call>`
|
||||
return {
|
||||
fullStream: new ReadableStream<StreamTextEvent>({
|
||||
textStream: new ReadableStream<string>({
|
||||
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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -726,7 +726,6 @@ export function createChatOrchestratorRuntime(deps: ChatOrchestratorRuntimeDeps)
|
||||
},
|
||||
tools: options.tools,
|
||||
waitForTools: true,
|
||||
captureToolErrors: true,
|
||||
onUsage: (usage) => {
|
||||
generationUsage = usage
|
||||
deps.onLlmGeneration?.({
|
||||
|
||||
@@ -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<void>
|
||||
preToolCall?: unknown
|
||||
tools?: Tool[]
|
||||
}) => {
|
||||
const steps = new Promise<unknown[]>((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')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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<string, string>,
|
||||
): 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<string, string>,
|
||||
) {
|
||||
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<string, string>()
|
||||
const streamTools = options?.captureToolErrors && tools != null
|
||||
? withCapturedToolErrors(tools, capturedToolErrorByCallId)
|
||||
: tools
|
||||
|
||||
return new Promise<void>((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
|
||||
|
||||
@@ -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<string, boolean>
|
||||
supportsTools?: boolean
|
||||
waitForTools?: boolean
|
||||
captureToolErrors?: boolean
|
||||
/** Provider tool-selection directive for one request. */
|
||||
toolChoice?: ToolChoice
|
||||
tools?: Tool[] | (() => Promise<Tool[] | undefined>)
|
||||
|
||||
@@ -129,40 +129,32 @@ describe('isToolRelatedError', () => {
|
||||
})
|
||||
}
|
||||
|
||||
it('resolves from steps while still forwarding tool_calls finish events', async () => {
|
||||
let onEvent: ((event: unknown) => Promise<void>) | undefined
|
||||
streamTextMock.mockImplementation((options: { onEvent: (event: unknown) => Promise<void> }) => {
|
||||
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<void>) | undefined
|
||||
let resolveSteps: ((steps: unknown[]) => void) | undefined
|
||||
streamTextMock.mockImplementation((options: { onEvent: (event: unknown) => Promise<void> }) => {
|
||||
onEvent = options.onEvent
|
||||
return createMockStreamResult()
|
||||
return {
|
||||
...createMockStreamResult(),
|
||||
steps: new Promise<unknown[]>((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<void>, 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<void>, 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<void>, 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<void>, 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)
|
||||
|
||||
@@ -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' })
|
||||
|
||||
@@ -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<ToolCall> & {
|
||||
function?: Partial<ToolCall['function']> & { 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)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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<true>) => Promise<unknown> | unknown;
|
||||
+ captureToolErrors?: boolean;
|
||||
+ onToolCallFinish?: OnToolCallFinishCallback;
|
||||
+ onToolCallStart?: OnToolCallStartCallback;
|
||||
postToolCall?: PostToolCall;
|
||||
prepareStep?: PrepareStep;
|
||||
preToolCall?: PreToolCall;
|
||||
+ repairToolCall?: RepairToolCallFunction;
|
||||
/** @internal */
|
||||
steps?: CompletionStep<true>[];
|
||||
/** @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
|
||||
}))
|
||||
@@ -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<CompletionToolResult | void> | void;
|
||||
type PreToolCall = (toolCall: CompletionToolCall, options: ToolExecuteOptions) => CompletionToolCall | CompletionToolResult | Promise<CompletionToolCall | CompletionToolResult | void> | void;
|
||||
+type OnToolCallFinishCallback = (context: {
|
||||
+ durationMs: number;
|
||||
+ error?: unknown;
|
||||
+ output?: unknown;
|
||||
+ toolCallId: string;
|
||||
+ toolName: string;
|
||||
+}) => Promise<void> | void;
|
||||
+type OnToolCallStartCallback = (context: {
|
||||
+ input: unknown;
|
||||
+ toolCallId: string;
|
||||
+ toolName: string;
|
||||
+}) => Promise<void> | void;
|
||||
+type RepairToolCallFunction = (context: {
|
||||
+ error: Error;
|
||||
+ messages: Message[];
|
||||
+ toolCall: ToolCall;
|
||||
+ tools?: Tool[];
|
||||
+}) => Promise<ToolCall | null> | ToolCall | null;
|
||||
interface Tool {
|
||||
execute: (input: unknown, options: ToolExecuteOptions) => Promise<ToolExecuteResult> | ToolExecuteResult;
|
||||
function: {
|
||||
@@ -275,9 +294,13 @@ declare const chat: <T extends WithUnknown<ChatOptions>>(options: T) => Promise<
|
||||
|
||||
interface ExecuteToolOptions<T = ToolMessage['content']> {
|
||||
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<T = ToolMessage['content']> {
|
||||
interface ExecuteToolResult<T = ToolMessage['content']> {
|
||||
completionToolCall: CompletionToolCall;
|
||||
completionToolResult: CompletionToolResult;
|
||||
+ message: ToolMessage;
|
||||
result: T;
|
||||
}
|
||||
declare const toCompletionToolCall: (toolCall: ToolCall) => CompletionToolCall;
|
||||
-declare const executeTool: <T = ToolMessage["content"]>({ abortSignal, messages, postToolCall, preToolCall, toolCall, tools, wrapResult }: ExecuteToolOptions<T>) => Promise<ExecuteToolResult<T>>;
|
||||
+declare const executeTool: <T = ToolMessage["content"]>(options: ExecuteToolOptions<T>) => Promise<ExecuteToolResult<T>>;
|
||||
|
||||
interface ResolvePrepareStepOptions<TInput = Message[], TToolChoice = ToolChoice> {
|
||||
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({
|
||||
@@ -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> | unknown;
|
||||
+ onEvent?: (event: StreamTextEvent) => Promise<unknown> | unknown;
|
||||
onFinish?: (step?: CompletionStep) => Promise<unknown> | unknown;
|
||||
onStepFinish?: (step: CompletionStep) => Promise<unknown> | unknown;
|
||||
+ captureToolErrors?: boolean;
|
||||
+ onToolCallFinish?: OnToolCallFinishCallback;
|
||||
+ onToolCallStart?: OnToolCallStartCallback;
|
||||
postToolCall?: PostToolCall;
|
||||
prepareStep?: PrepareStep;
|
||||
preToolCall?: PreToolCall;
|
||||
+ repairToolCall?: RepairToolCallFunction;
|
||||
/** @default `stepCountAtLeast(1)` */
|
||||
stopWhen?: StopCondition<Message>;
|
||||
/**
|
||||
@@ -48,8 +82,8 @@ interface StreamTextOptions extends ChatOptions {
|
||||
};
|
||||
}
|
||||
interface StreamTextResult {
|
||||
- eventStream: ReadableStream<Event>;
|
||||
- fullStream: ReadableStream<StreamTextChunkResult>;
|
||||
+ eventStream: ReadableStream<StreamTextEvent>;
|
||||
+ fullStream: ReadableStream<StreamTextEvent>;
|
||||
messages: Promise<Message[]>;
|
||||
reasoningTextStream: ReadableStream<string>;
|
||||
steps: Promise<CompletionStep[]>;
|
||||
@@ -60,4 +94,4 @@ interface StreamTextResult {
|
||||
declare const streamText: (options: WithUnknown<StreamTextOptions>) => 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,
|
||||
Generated
+56
-94
@@ -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))
|
||||
|
||||
+1
-4
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user