@tanstack/ai 0.44.1 → 0.45.0
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/adapter.d.ts +13 -1
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/index.js +108 -68
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +24 -27
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.js +14 -13
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/approval-schema.js +11 -8
- package/dist/esm/activities/chat/tools/approval-schema.js.map +1 -1
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.js +40 -31
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.js +3 -2
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +2 -0
- package/dist/esm/adapter-internals.js +3 -1
- package/dist/esm/interrupt-resume.js +24 -20
- package/dist/esm/interrupt-resume.js.map +1 -1
- package/dist/esm/interrupts.js +2 -1
- package/dist/esm/interrupts.js.map +1 -1
- package/dist/esm/logger/console-logger.js +1 -3
- package/dist/esm/logger/console-logger.js.map +1 -1
- package/dist/esm/logger/resolve.js +2 -1
- package/dist/esm/logger/resolve.js.map +1 -1
- package/dist/esm/stream-durability.js +1 -1
- package/dist/esm/stream-durability.js.map +1 -1
- package/dist/esm/types.d.ts +19 -16
- package/dist/esm/utilities/chat-params.js +1 -3
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/dist/esm/utilities/media-prompt.js +1 -3
- package/dist/esm/utilities/media-prompt.js.map +1 -1
- package/dist/esm/utilities/structured-output-events.d.ts +17 -0
- package/dist/esm/utilities/structured-output-events.js +32 -0
- package/dist/esm/utilities/structured-output-events.js.map +1 -0
- package/dist/esm/utilities/structured-output-text.d.ts +7 -0
- package/dist/esm/utilities/structured-output-text.js +50 -0
- package/dist/esm/utilities/structured-output-text.js.map +1 -0
- package/package.json +2 -2
- package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +6 -2
- package/skills/ai-core/media-generation/SKILL.md +33 -19
- package/skills/ai-core/structured-outputs/SKILL.md +73 -1
- package/skills/ai-core/tool-calling/SKILL.md +1 -1
- package/src/activities/chat/adapter.ts +16 -1
- package/src/activities/chat/index.ts +128 -47
- package/src/activities/chat/stream/processor.ts +6 -3
- package/src/activities/chat/tools/tool-calls.ts +12 -4
- package/src/adapter-internals.ts +8 -0
- package/src/types.ts +27 -19
- package/src/utilities/structured-output-events.ts +44 -0
- package/src/utilities/structured-output-text.ts +63 -0
|
@@ -1854,9 +1854,10 @@ export class StreamProcessor {
|
|
|
1854
1854
|
|
|
1855
1855
|
if (chunk.name === 'structured-output.start' && chunk.value) {
|
|
1856
1856
|
const v = chunk.value as { messageId?: string }
|
|
1857
|
-
const targetId =
|
|
1857
|
+
const { messageId: targetId } = this.ensureAssistantMessage(
|
|
1858
|
+
v.messageId ?? messageId ?? undefined,
|
|
1859
|
+
)
|
|
1858
1860
|
if (targetId) {
|
|
1859
|
-
this.ensureAssistantMessage(targetId)
|
|
1860
1861
|
this.structuredMessageIds.add(targetId)
|
|
1861
1862
|
this.structuredOutputUpdateBatches.delete(targetId)
|
|
1862
1863
|
this.events.onStructuredOutputChange?.({
|
|
@@ -1876,7 +1877,9 @@ export class StreamProcessor {
|
|
|
1876
1877
|
reasoning?: string
|
|
1877
1878
|
messageId?: string
|
|
1878
1879
|
}
|
|
1879
|
-
const targetId =
|
|
1880
|
+
const { messageId: targetId } = this.ensureAssistantMessage(
|
|
1881
|
+
v.messageId ?? messageId ?? undefined,
|
|
1882
|
+
)
|
|
1880
1883
|
if (targetId) {
|
|
1881
1884
|
this.flushStructuredOutputUpdate(targetId)
|
|
1882
1885
|
this.messages = completeStructuredOutputPart(
|
|
@@ -813,7 +813,7 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
813
813
|
continue
|
|
814
814
|
}
|
|
815
815
|
|
|
816
|
-
// Parse arguments
|
|
816
|
+
// Parse arguments
|
|
817
817
|
let input: unknown = {}
|
|
818
818
|
const argsStr = toolCall.function.arguments.trim() || '{}'
|
|
819
819
|
if (argsStr) {
|
|
@@ -821,9 +821,17 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
821
821
|
const parsed = JSON.parse(argsStr)
|
|
822
822
|
// Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)
|
|
823
823
|
input = parsed && typeof parsed === 'object' ? parsed : {}
|
|
824
|
-
} catch
|
|
825
|
-
|
|
826
|
-
|
|
824
|
+
} catch {
|
|
825
|
+
results.push({
|
|
826
|
+
toolCallId: toolCall.id,
|
|
827
|
+
toolName,
|
|
828
|
+
result: {
|
|
829
|
+
error: `Failed to parse tool arguments as JSON: ${argsStr}`,
|
|
830
|
+
},
|
|
831
|
+
input,
|
|
832
|
+
state: 'output-error',
|
|
833
|
+
})
|
|
834
|
+
continue
|
|
827
835
|
}
|
|
828
836
|
}
|
|
829
837
|
|
package/src/adapter-internals.ts
CHANGED
|
@@ -25,3 +25,11 @@ export {
|
|
|
25
25
|
PendingTurnCapability,
|
|
26
26
|
providePendingTurn,
|
|
27
27
|
} from './activities/chat/middleware/pending-turn'
|
|
28
|
+
export {
|
|
29
|
+
appendOutputSchemaInstruction,
|
|
30
|
+
parseJsonFromAssistantText,
|
|
31
|
+
} from './utilities/structured-output-text'
|
|
32
|
+
export {
|
|
33
|
+
structuredOutputCompleteChunk,
|
|
34
|
+
structuredOutputStartChunk,
|
|
35
|
+
} from './utilities/structured-output-events'
|
package/src/types.ts
CHANGED
|
@@ -958,10 +958,13 @@ export interface TextOptions<
|
|
|
958
958
|
* `supportsCombinedToolsAndSchema(modelOptions) === true`. The adapter
|
|
959
959
|
* should then wire the schema into the upstream request (e.g.
|
|
960
960
|
* `response_format: { type: 'json_schema', ... }`, `text.format`,
|
|
961
|
-
* `output_format`) alongside any `tools`.
|
|
962
|
-
*
|
|
963
|
-
*
|
|
964
|
-
*
|
|
961
|
+
* `output_format`, `--json-schema`) alongside any `tools`.
|
|
962
|
+
*
|
|
963
|
+
* How the engine then takes the object depends on
|
|
964
|
+
* `combinedStructuredOutputSource()`:
|
|
965
|
+
* - `'text'` (default): the final-turn assistant text is the JSON.
|
|
966
|
+
* - `'event'`: the adapter emits `structured-output.complete` during
|
|
967
|
+
* `chatStream`. Accumulated prose is not parsed.
|
|
965
968
|
*
|
|
966
969
|
* Adapters that did NOT declare the capability never see this field
|
|
967
970
|
* populated — the engine instead invokes `structuredOutput` /
|
|
@@ -1555,20 +1558,25 @@ export interface CodeModeExternalErrorEvent extends CustomEvent {
|
|
|
1555
1558
|
name: 'code_mode:external_error'
|
|
1556
1559
|
value: { function: string; error: string; duration: number }
|
|
1557
1560
|
}
|
|
1558
|
-
export interface
|
|
1559
|
-
name: 'code_mode:
|
|
1560
|
-
value: {
|
|
1561
|
+
export interface CodeModeSnippetCallEvent extends CustomEvent {
|
|
1562
|
+
name: 'code_mode:snippet_call'
|
|
1563
|
+
value: { snippet: string; input: unknown; timestamp: number }
|
|
1561
1564
|
}
|
|
1562
|
-
export interface
|
|
1563
|
-
name: 'code_mode:
|
|
1564
|
-
value: {
|
|
1565
|
+
export interface CodeModeSnippetResultEvent extends CustomEvent {
|
|
1566
|
+
name: 'code_mode:snippet_result'
|
|
1567
|
+
value: {
|
|
1568
|
+
snippet: string
|
|
1569
|
+
result: unknown
|
|
1570
|
+
duration: number
|
|
1571
|
+
timestamp: number
|
|
1572
|
+
}
|
|
1565
1573
|
}
|
|
1566
|
-
export interface
|
|
1567
|
-
name: 'code_mode:
|
|
1568
|
-
value: {
|
|
1574
|
+
export interface CodeModeSnippetErrorEvent extends CustomEvent {
|
|
1575
|
+
name: 'code_mode:snippet_error'
|
|
1576
|
+
value: { snippet: string; error: string; duration: number; timestamp: number }
|
|
1569
1577
|
}
|
|
1570
|
-
export interface
|
|
1571
|
-
name: '
|
|
1578
|
+
export interface SnippetRegisteredEvent extends CustomEvent {
|
|
1579
|
+
name: 'snippet:registered'
|
|
1572
1580
|
value: { id: string; name: string; description: string; timestamp: number }
|
|
1573
1581
|
}
|
|
1574
1582
|
|
|
@@ -1587,10 +1595,10 @@ export type KnownCustomEvent =
|
|
|
1587
1595
|
| CodeModeExternalCallEvent
|
|
1588
1596
|
| CodeModeExternalResultEvent
|
|
1589
1597
|
| CodeModeExternalErrorEvent
|
|
1590
|
-
|
|
|
1591
|
-
|
|
|
1592
|
-
|
|
|
1593
|
-
|
|
|
1598
|
+
| CodeModeSnippetCallEvent
|
|
1599
|
+
| CodeModeSnippetResultEvent
|
|
1600
|
+
| CodeModeSnippetErrorEvent
|
|
1601
|
+
| SnippetRegisteredEvent
|
|
1594
1602
|
| StructuredOutputStartEvent
|
|
1595
1603
|
| StructuredOutputCompleteEvent
|
|
1596
1604
|
| ApprovalRequestedEvent
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { EventType } from '../types'
|
|
2
|
+
import type { StreamChunk } from '../types'
|
|
3
|
+
|
|
4
|
+
export function structuredOutputStartChunk(args: {
|
|
5
|
+
messageId: string
|
|
6
|
+
model: string
|
|
7
|
+
threadId: string
|
|
8
|
+
runId: string
|
|
9
|
+
timestamp?: number
|
|
10
|
+
}): StreamChunk {
|
|
11
|
+
return {
|
|
12
|
+
type: EventType.CUSTOM,
|
|
13
|
+
name: 'structured-output.start',
|
|
14
|
+
value: { messageId: args.messageId },
|
|
15
|
+
model: args.model,
|
|
16
|
+
timestamp: args.timestamp ?? Date.now(),
|
|
17
|
+
threadId: args.threadId,
|
|
18
|
+
runId: args.runId,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function structuredOutputCompleteChunk(args: {
|
|
23
|
+
messageId: string
|
|
24
|
+
model: string
|
|
25
|
+
threadId: string
|
|
26
|
+
runId: string
|
|
27
|
+
object: unknown
|
|
28
|
+
raw: string
|
|
29
|
+
timestamp?: number
|
|
30
|
+
}): StreamChunk {
|
|
31
|
+
return {
|
|
32
|
+
type: EventType.CUSTOM,
|
|
33
|
+
name: 'structured-output.complete',
|
|
34
|
+
value: {
|
|
35
|
+
object: args.object,
|
|
36
|
+
raw: args.raw,
|
|
37
|
+
messageId: args.messageId,
|
|
38
|
+
},
|
|
39
|
+
model: args.model,
|
|
40
|
+
timestamp: args.timestamp ?? Date.now(),
|
|
41
|
+
threadId: args.threadId,
|
|
42
|
+
runId: args.runId,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse JSON from a model/harness assistant string.
|
|
3
|
+
* Strips a wrapping markdown fence when the whole payload is fenced.
|
|
4
|
+
* If the model wrote prose first, take the last JSON object or array.
|
|
5
|
+
*/
|
|
6
|
+
export function parseJsonFromAssistantText(raw: string): unknown {
|
|
7
|
+
const trimmed = raw.trim()
|
|
8
|
+
if (trimmed === '') {
|
|
9
|
+
throw new SyntaxError('Assistant text is empty')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const candidates: Array<string> = []
|
|
13
|
+
const wholeFence = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/)
|
|
14
|
+
if (wholeFence?.[1]) candidates.push(wholeFence[1].trim())
|
|
15
|
+
candidates.push(trimmed)
|
|
16
|
+
const lastFence = [
|
|
17
|
+
...trimmed.matchAll(/```(?:json)?\s*([\s\S]*?)\s*```/g),
|
|
18
|
+
].at(-1)
|
|
19
|
+
if (lastFence?.[1]) candidates.push(lastFence[1].trim())
|
|
20
|
+
const extracted = extractLastJsonSlice(trimmed)
|
|
21
|
+
if (extracted !== undefined) candidates.push(extracted)
|
|
22
|
+
|
|
23
|
+
let lastError: unknown
|
|
24
|
+
for (const candidate of candidates) {
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(candidate)
|
|
27
|
+
} catch (error) {
|
|
28
|
+
lastError = error
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
throw lastError instanceof Error
|
|
32
|
+
? lastError
|
|
33
|
+
: new SyntaxError('No JSON object found in assistant text')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function extractLastJsonSlice(text: string): string | undefined {
|
|
37
|
+
for (let end = text.length - 1; end >= 0; end--) {
|
|
38
|
+
if (text[end] !== '}' && text[end] !== ']') continue
|
|
39
|
+
for (let start = end; start >= 0; start--) {
|
|
40
|
+
const opener = text[start]
|
|
41
|
+
if (opener !== '{' && opener !== '[') continue
|
|
42
|
+
const slice = text.slice(start, end + 1)
|
|
43
|
+
try {
|
|
44
|
+
JSON.parse(slice)
|
|
45
|
+
return slice
|
|
46
|
+
} catch {
|
|
47
|
+
// Try an earlier opener, then an earlier closer.
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return undefined
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function appendOutputSchemaInstruction(
|
|
55
|
+
prompt: string,
|
|
56
|
+
schema: unknown,
|
|
57
|
+
): string {
|
|
58
|
+
return `${prompt}
|
|
59
|
+
|
|
60
|
+
Respond with a single JSON object that matches this JSON Schema. Do not wrap the object in markdown unless you must.
|
|
61
|
+
|
|
62
|
+
${JSON.stringify(schema)}`
|
|
63
|
+
}
|