@tanstack/ai 0.47.2 → 0.47.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/activities/chat/messages.js +11 -6
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.js +11 -9
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +3 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/middlewares/otel.js +1 -1
- package/dist/esm/stream-to-response.js +5 -1
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/utilities/errors.d.ts +9 -0
- package/dist/esm/utilities/errors.js +13 -1
- package/dist/esm/utilities/errors.js.map +1 -1
- package/package.json +3 -3
- package/src/activities/chat/messages.ts +9 -2
- package/src/activities/chat/stream/processor.ts +24 -21
- package/src/activities/summarize/chat-stream-summarize.ts +4 -0
- package/src/stream-to-response.ts +5 -0
- package/src/utilities/errors.ts +23 -0
|
@@ -22,8 +22,9 @@ import {
|
|
|
22
22
|
generateMessageId,
|
|
23
23
|
uiMessageToModelMessages,
|
|
24
24
|
} from '../messages.js'
|
|
25
|
-
import {
|
|
25
|
+
import { runErrorEventToError } from '../../../utilities/errors'
|
|
26
26
|
import { isProviderExecutedToolCall } from '../../../utilities/provider-executed'
|
|
27
|
+
import { normalizeToolResult } from '../../../utilities/tool-result'
|
|
27
28
|
import { defaultJSONParser } from './json-parser'
|
|
28
29
|
import {
|
|
29
30
|
appendStructuredOutputDelta,
|
|
@@ -819,6 +820,15 @@ export class StreamProcessor {
|
|
|
819
820
|
// Update activeMessageIds
|
|
820
821
|
this.activeMessageIds.delete(pendingId)
|
|
821
822
|
this.activeMessageIds.add(messageId)
|
|
823
|
+
|
|
824
|
+
// TOOL_CALL_ARGS/END route through toolCallToMessage. Keep those
|
|
825
|
+
// entries on the remapped id so later args still accumulate
|
|
826
|
+
// (interleaved text can arrive as a full START/CONTENT/END block).
|
|
827
|
+
for (const [toolCallId, mappedMessageId] of this.toolCallToMessage) {
|
|
828
|
+
if (mappedMessageId === pendingId) {
|
|
829
|
+
this.toolCallToMessage.set(toolCallId, messageId)
|
|
830
|
+
}
|
|
831
|
+
}
|
|
822
832
|
}
|
|
823
833
|
|
|
824
834
|
// Ensure state exists
|
|
@@ -886,9 +896,6 @@ export class StreamProcessor {
|
|
|
886
896
|
if (state.currentSegmentText !== state.lastEmittedText) {
|
|
887
897
|
this.emitTextUpdateForMessage(messageId)
|
|
888
898
|
}
|
|
889
|
-
|
|
890
|
-
// Complete all tool calls for this message
|
|
891
|
-
this.completeAllToolCallsForMessage(messageId)
|
|
892
899
|
}
|
|
893
900
|
|
|
894
901
|
/**
|
|
@@ -1139,9 +1146,6 @@ export class StreamProcessor {
|
|
|
1139
1146
|
): void {
|
|
1140
1147
|
const { messageId, state } = this.ensureAssistantMessage(chunk.messageId)
|
|
1141
1148
|
|
|
1142
|
-
// Content arriving means all current tool calls for this message are complete
|
|
1143
|
-
this.completeAllToolCallsForMessage(messageId)
|
|
1144
|
-
|
|
1145
1149
|
if (this.structuredMessageIds.has(messageId)) {
|
|
1146
1150
|
// `chunk.delta` is incremental; `chunk.content` is sometimes cumulative
|
|
1147
1151
|
// (mirrors what the plain-text branch handles below). Reconcile against
|
|
@@ -1681,15 +1685,7 @@ export class StreamProcessor {
|
|
|
1681
1685
|
// the surfaced Error so consumers can recover the upstream detail that the
|
|
1682
1686
|
// RUN_ERROR's `message` alone discards. Both are optional and added only
|
|
1683
1687
|
// when present, keeping the Error backward compatible.
|
|
1684
|
-
|
|
1685
|
-
const code = chunk.code ?? chunk.error?.code
|
|
1686
|
-
if (code !== undefined) {
|
|
1687
|
-
Object.assign(error, { code })
|
|
1688
|
-
}
|
|
1689
|
-
if (chunk.rawEvent !== undefined) {
|
|
1690
|
-
Object.assign(error, { rawEvent: chunk.rawEvent })
|
|
1691
|
-
}
|
|
1692
|
-
this.events.onError?.(error)
|
|
1688
|
+
this.events.onError?.(runErrorEventToError(chunk))
|
|
1693
1689
|
}
|
|
1694
1690
|
|
|
1695
1691
|
/**
|
|
@@ -2087,8 +2083,17 @@ export class StreamProcessor {
|
|
|
2087
2083
|
// counts as a completed tool call in getCompletedToolCalls()/getState().
|
|
2088
2084
|
toolCall.state = 'input-complete'
|
|
2089
2085
|
|
|
2090
|
-
//
|
|
2091
|
-
|
|
2086
|
+
// Only surface `input` from a strict parse. The streaming partial-JSON
|
|
2087
|
+
// parser closes unterminated strings, so truncated arguments would become
|
|
2088
|
+
// a plausible but wrong object (GitHub issue #1017). If parse fails,
|
|
2089
|
+
// `input` stays unset and consumers use the raw `arguments` string.
|
|
2090
|
+
let strictParseSucceeded = false
|
|
2091
|
+
try {
|
|
2092
|
+
toolCall.parsedArguments = JSON.parse(toolCall.arguments)
|
|
2093
|
+
strictParseSucceeded = true
|
|
2094
|
+
} catch {
|
|
2095
|
+
toolCall.parsedArguments = undefined
|
|
2096
|
+
}
|
|
2092
2097
|
|
|
2093
2098
|
// Don't downgrade the rendered part of a call that already reached the
|
|
2094
2099
|
// terminal 'error' state (e.g. an output-error TOOL_CALL_RESULT arrived
|
|
@@ -2116,9 +2121,7 @@ export class StreamProcessor {
|
|
|
2116
2121
|
name: toolCall.name,
|
|
2117
2122
|
arguments: toolCall.arguments,
|
|
2118
2123
|
state: 'input-complete',
|
|
2119
|
-
...(toolCall.parsedArguments
|
|
2120
|
-
input: toolCall.parsedArguments,
|
|
2121
|
-
}),
|
|
2124
|
+
...(strictParseSucceeded && { input: toolCall.parsedArguments }),
|
|
2122
2125
|
...(toolCall.metadata !== undefined && { metadata: toolCall.metadata }),
|
|
2123
2126
|
})
|
|
2124
2127
|
this.emitMessagesChange()
|
|
@@ -38,6 +38,7 @@ export interface ChatStreamCapable {
|
|
|
38
38
|
* - Groq: `max_completion_tokens`
|
|
39
39
|
* - Gemini: `maxOutputTokens`
|
|
40
40
|
* - OpenRouter: `maxCompletionTokens`
|
|
41
|
+
* - LLM Gateway: `max_tokens`
|
|
41
42
|
* - Ollama: nested `options.num_predict` (no entry — see `applyMaxLength`)
|
|
42
43
|
*/
|
|
43
44
|
const MAX_TOKENS_KEY_BY_ADAPTER: Record<string, string> = {
|
|
@@ -47,6 +48,9 @@ const MAX_TOKENS_KEY_BY_ADAPTER: Record<string, string> = {
|
|
|
47
48
|
groq: 'max_completion_tokens',
|
|
48
49
|
gemini: 'maxOutputTokens',
|
|
49
50
|
openrouter: 'maxCompletionTokens',
|
|
51
|
+
// LLM Gateway exposes an OpenAI-compatible Chat Completions surface whose
|
|
52
|
+
// only output cap is `max_tokens` — it does not read `max_completion_tokens`.
|
|
53
|
+
llmgateway: 'max_tokens',
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
/**
|
|
@@ -9,6 +9,7 @@ import { notifyRunDisconnected } from './delivery-disconnect'
|
|
|
9
9
|
import { resolveResumeRunId } from './stream-durability'
|
|
10
10
|
import { EventType } from './types'
|
|
11
11
|
import { resolveDebugOption } from './logger/resolve'
|
|
12
|
+
import { runErrorEventToError } from './utilities/errors'
|
|
12
13
|
import type { LockStore } from './activities/chat/middleware/locks'
|
|
13
14
|
import type {
|
|
14
15
|
RunRecord,
|
|
@@ -46,6 +47,10 @@ export async function streamToText(
|
|
|
46
47
|
let accumulatedContent = ''
|
|
47
48
|
|
|
48
49
|
for await (const chunk of stream) {
|
|
50
|
+
if (chunk.type === 'RUN_ERROR') {
|
|
51
|
+
throw runErrorEventToError(chunk)
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
if (chunk.type === 'TEXT_MESSAGE_CONTENT' && chunk.delta) {
|
|
50
55
|
accumulatedContent += chunk.delta
|
|
51
56
|
}
|
package/src/utilities/errors.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { StreamChunk } from '../types'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Best-effort extraction of a human-readable message from an unknown thrown
|
|
3
5
|
* value, returning `undefined` when none can be found.
|
|
@@ -27,3 +29,24 @@ export function errorTypeName(err: unknown): string {
|
|
|
27
29
|
}
|
|
28
30
|
return 'Error'
|
|
29
31
|
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Convert an AG-UI RUN_ERROR event to the Error shape exposed to consumers.
|
|
35
|
+
* Preserves the provider code and sanitized raw event when available, while
|
|
36
|
+
* accepting the deprecated nested error payload for backward compatibility.
|
|
37
|
+
*/
|
|
38
|
+
export function runErrorEventToError(
|
|
39
|
+
chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,
|
|
40
|
+
): Error {
|
|
41
|
+
const error = new Error(
|
|
42
|
+
chunk.message || chunk.error?.message || 'An error occurred',
|
|
43
|
+
)
|
|
44
|
+
const code = chunk.code ?? chunk.error?.code
|
|
45
|
+
if (code !== undefined) {
|
|
46
|
+
Object.assign(error, { code })
|
|
47
|
+
}
|
|
48
|
+
if (chunk.rawEvent !== undefined) {
|
|
49
|
+
Object.assign(error, { rawEvent: chunk.rawEvent })
|
|
50
|
+
}
|
|
51
|
+
return error
|
|
52
|
+
}
|