@tanstack/ai 0.2.2 → 0.3.1
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/README.md +1 -0
- package/dist/esm/activities/chat/index.js +264 -186
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +22 -0
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/stream/index.d.ts +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +22 -43
- package/dist/esm/activities/chat/stream/processor.js +93 -157
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +22 -21
- package/dist/esm/activities/chat/tools/tool-calls.js +40 -27
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/generateImage/index.js +41 -1
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.js +33 -1
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.js +29 -1
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.js +50 -0
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/summarize/index.js +9 -8
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/event-client.d.ts +378 -254
- package/dist/esm/event-client.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/stream-to-response.d.ts +1 -1
- package/dist/esm/stream-to-response.js +5 -3
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/types.d.ts +162 -46
- package/package.json +1 -1
- package/src/activities/chat/index.ts +362 -216
- package/src/activities/chat/messages.ts +40 -1
- package/src/activities/chat/stream/index.ts +1 -5
- package/src/activities/chat/stream/processor.ts +142 -270
- package/src/activities/chat/tools/tool-calls.ts +55 -49
- package/src/activities/generateImage/index.ts +47 -2
- package/src/activities/generateSpeech/index.ts +37 -1
- package/src/activities/generateTranscription/index.ts +33 -1
- package/src/activities/generateVideo/index.ts +54 -0
- package/src/activities/summarize/index.ts +10 -9
- package/src/event-client.ts +460 -256
- package/src/index.ts +2 -3
- package/src/stream-to-response.ts +8 -6
- package/src/types.ts +209 -65
|
@@ -39,6 +39,7 @@ import type {
|
|
|
39
39
|
StreamChunk,
|
|
40
40
|
ToolCall,
|
|
41
41
|
ToolCallPart,
|
|
42
|
+
ToolResultPart,
|
|
42
43
|
UIMessage,
|
|
43
44
|
} from '../../../types'
|
|
44
45
|
|
|
@@ -78,67 +79,13 @@ export interface StreamProcessorEvents {
|
|
|
78
79
|
onThinkingUpdate?: (messageId: string, content: string) => void
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
/**
|
|
82
|
-
* Legacy handlers for backward compatibility
|
|
83
|
-
* These are the old callback-style handlers
|
|
84
|
-
*/
|
|
85
|
-
export interface StreamProcessorHandlers {
|
|
86
|
-
onTextUpdate?: (content: string) => void
|
|
87
|
-
onThinkingUpdate?: (content: string) => void
|
|
88
|
-
|
|
89
|
-
// Tool call lifecycle handlers
|
|
90
|
-
onToolCallStart?: (index: number, id: string, name: string) => void
|
|
91
|
-
onToolCallDelta?: (index: number, args: string) => void
|
|
92
|
-
onToolCallComplete?: (
|
|
93
|
-
index: number,
|
|
94
|
-
id: string,
|
|
95
|
-
name: string,
|
|
96
|
-
args: string,
|
|
97
|
-
) => void
|
|
98
|
-
onToolCallStateChange?: (
|
|
99
|
-
index: number,
|
|
100
|
-
id: string,
|
|
101
|
-
name: string,
|
|
102
|
-
state: ToolCallState,
|
|
103
|
-
args: string,
|
|
104
|
-
parsedArgs?: any,
|
|
105
|
-
) => void
|
|
106
|
-
|
|
107
|
-
// Tool result handlers
|
|
108
|
-
onToolResultStateChange?: (
|
|
109
|
-
toolCallId: string,
|
|
110
|
-
content: string,
|
|
111
|
-
state: ToolResultState,
|
|
112
|
-
error?: string,
|
|
113
|
-
) => void
|
|
114
|
-
|
|
115
|
-
// Approval/client tool handlers
|
|
116
|
-
onApprovalRequested?: (
|
|
117
|
-
toolCallId: string,
|
|
118
|
-
toolName: string,
|
|
119
|
-
input: any,
|
|
120
|
-
approvalId: string,
|
|
121
|
-
) => void
|
|
122
|
-
onToolInputAvailable?: (
|
|
123
|
-
toolCallId: string,
|
|
124
|
-
toolName: string,
|
|
125
|
-
input: any,
|
|
126
|
-
) => void
|
|
127
|
-
|
|
128
|
-
// Stream lifecycle
|
|
129
|
-
onStreamEnd?: (content: string, toolCalls?: Array<ToolCall>) => void
|
|
130
|
-
onError?: (error: { message: string; code?: string }) => void
|
|
131
|
-
}
|
|
132
|
-
|
|
133
82
|
/**
|
|
134
83
|
* Options for StreamProcessor
|
|
135
84
|
*/
|
|
136
85
|
export interface StreamProcessorOptions {
|
|
137
86
|
chunkStrategy?: ChunkStrategy
|
|
138
|
-
/**
|
|
87
|
+
/** Event-driven handlers */
|
|
139
88
|
events?: StreamProcessorEvents
|
|
140
|
-
/** Legacy callback handlers (for backward compatibility) */
|
|
141
|
-
handlers?: StreamProcessorHandlers
|
|
142
89
|
jsonParser?: {
|
|
143
90
|
parse: (jsonString: string) => any
|
|
144
91
|
}
|
|
@@ -168,7 +115,6 @@ export interface StreamProcessorOptions {
|
|
|
168
115
|
export class StreamProcessor {
|
|
169
116
|
private chunkStrategy: ChunkStrategy
|
|
170
117
|
private events: StreamProcessorEvents
|
|
171
|
-
private handlers: StreamProcessorHandlers
|
|
172
118
|
private jsonParser: { parse: (jsonString: string) => any }
|
|
173
119
|
private recordingEnabled: boolean
|
|
174
120
|
|
|
@@ -197,7 +143,6 @@ export class StreamProcessor {
|
|
|
197
143
|
constructor(options: StreamProcessorOptions = {}) {
|
|
198
144
|
this.chunkStrategy = options.chunkStrategy || new ImmediateStrategy()
|
|
199
145
|
this.events = options.events || {}
|
|
200
|
-
this.handlers = options.handlers || {}
|
|
201
146
|
this.jsonParser = options.jsonParser || defaultJSONParser
|
|
202
147
|
this.recordingEnabled = options.recording ?? false
|
|
203
148
|
|
|
@@ -352,11 +297,23 @@ export class StreamProcessor {
|
|
|
352
297
|
|
|
353
298
|
if (toolParts.length === 0) return true
|
|
354
299
|
|
|
300
|
+
// Get tool result parts to check for server tool completion
|
|
301
|
+
const toolResultIds = new Set(
|
|
302
|
+
lastAssistant.parts
|
|
303
|
+
.filter((p): p is ToolResultPart => p.type === 'tool-result')
|
|
304
|
+
.map((p) => p.toolCallId),
|
|
305
|
+
)
|
|
306
|
+
|
|
355
307
|
// All tool calls must be in a terminal state
|
|
308
|
+
// A tool call is complete if:
|
|
309
|
+
// 1. It was approved/denied (approval-responded state)
|
|
310
|
+
// 2. It has an output field set (client tool completed via addToolResult)
|
|
311
|
+
// 3. It has a corresponding tool-result part (server tool completed)
|
|
356
312
|
return toolParts.every(
|
|
357
313
|
(part) =>
|
|
358
314
|
part.state === 'approval-responded' ||
|
|
359
|
-
(part.output !== undefined && !part.approval)
|
|
315
|
+
(part.output !== undefined && !part.approval) ||
|
|
316
|
+
toolResultIds.has(part.id),
|
|
360
317
|
)
|
|
361
318
|
}
|
|
362
319
|
|
|
@@ -423,49 +380,51 @@ export class StreamProcessor {
|
|
|
423
380
|
}
|
|
424
381
|
|
|
425
382
|
switch (chunk.type) {
|
|
426
|
-
|
|
427
|
-
|
|
383
|
+
// AG-UI Events
|
|
384
|
+
case 'TEXT_MESSAGE_CONTENT':
|
|
385
|
+
this.handleTextMessageContentEvent(chunk)
|
|
428
386
|
break
|
|
429
387
|
|
|
430
|
-
case '
|
|
431
|
-
this.
|
|
388
|
+
case 'TOOL_CALL_START':
|
|
389
|
+
this.handleToolCallStartEvent(chunk)
|
|
432
390
|
break
|
|
433
391
|
|
|
434
|
-
case '
|
|
435
|
-
this.
|
|
392
|
+
case 'TOOL_CALL_ARGS':
|
|
393
|
+
this.handleToolCallArgsEvent(chunk)
|
|
436
394
|
break
|
|
437
395
|
|
|
438
|
-
case '
|
|
439
|
-
this.
|
|
396
|
+
case 'TOOL_CALL_END':
|
|
397
|
+
this.handleToolCallEndEvent(chunk)
|
|
440
398
|
break
|
|
441
399
|
|
|
442
|
-
case '
|
|
443
|
-
this.
|
|
400
|
+
case 'RUN_FINISHED':
|
|
401
|
+
this.handleRunFinishedEvent(chunk)
|
|
444
402
|
break
|
|
445
403
|
|
|
446
|
-
case '
|
|
447
|
-
this.
|
|
404
|
+
case 'RUN_ERROR':
|
|
405
|
+
this.handleRunErrorEvent(chunk)
|
|
448
406
|
break
|
|
449
407
|
|
|
450
|
-
case '
|
|
451
|
-
this.
|
|
408
|
+
case 'STEP_FINISHED':
|
|
409
|
+
this.handleStepFinishedEvent(chunk)
|
|
452
410
|
break
|
|
453
411
|
|
|
454
|
-
case '
|
|
455
|
-
this.
|
|
412
|
+
case 'CUSTOM':
|
|
413
|
+
this.handleCustomEvent(chunk)
|
|
456
414
|
break
|
|
457
415
|
|
|
458
416
|
default:
|
|
459
|
-
//
|
|
417
|
+
// RUN_STARTED, TEXT_MESSAGE_START, TEXT_MESSAGE_END, STEP_STARTED,
|
|
418
|
+
// STATE_SNAPSHOT, STATE_DELTA - no special handling needed
|
|
460
419
|
break
|
|
461
420
|
}
|
|
462
421
|
}
|
|
463
422
|
|
|
464
423
|
/**
|
|
465
|
-
* Handle
|
|
424
|
+
* Handle TEXT_MESSAGE_CONTENT event
|
|
466
425
|
*/
|
|
467
|
-
private
|
|
468
|
-
chunk: Extract<StreamChunk, { type: '
|
|
426
|
+
private handleTextMessageContentEvent(
|
|
427
|
+
chunk: Extract<StreamChunk, { type: 'TEXT_MESSAGE_CONTENT' }>,
|
|
469
428
|
): void {
|
|
470
429
|
// Content arriving means all current tool calls are complete
|
|
471
430
|
this.completeAllToolCalls()
|
|
@@ -495,7 +454,7 @@ export class StreamProcessor {
|
|
|
495
454
|
// Prefer delta over content - delta is the incremental change
|
|
496
455
|
if (chunk.delta !== '') {
|
|
497
456
|
nextText = currentText + chunk.delta
|
|
498
|
-
} else if (chunk.content !== '') {
|
|
457
|
+
} else if (chunk.content && chunk.content !== '') {
|
|
499
458
|
// Fallback: use content if delta is not provided
|
|
500
459
|
if (chunk.content.startsWith(currentText)) {
|
|
501
460
|
nextText = chunk.content
|
|
@@ -512,8 +471,7 @@ export class StreamProcessor {
|
|
|
512
471
|
this.totalTextContent += textDelta
|
|
513
472
|
|
|
514
473
|
// Use delta for chunk strategy if available
|
|
515
|
-
|
|
516
|
-
const chunkPortion = chunk.delta ?? chunk.content ?? ''
|
|
474
|
+
const chunkPortion = chunk.delta || chunk.content || ''
|
|
517
475
|
const shouldEmit = this.chunkStrategy.shouldEmit(
|
|
518
476
|
chunkPortion,
|
|
519
477
|
this.currentSegmentText,
|
|
@@ -524,100 +482,75 @@ export class StreamProcessor {
|
|
|
524
482
|
}
|
|
525
483
|
|
|
526
484
|
/**
|
|
527
|
-
* Handle
|
|
485
|
+
* Handle TOOL_CALL_START event
|
|
528
486
|
*/
|
|
529
|
-
private
|
|
530
|
-
chunk: Extract<StreamChunk, { type: '
|
|
487
|
+
private handleToolCallStartEvent(
|
|
488
|
+
chunk: Extract<StreamChunk, { type: 'TOOL_CALL_START' }>,
|
|
531
489
|
): void {
|
|
532
490
|
// Mark that we've seen tool calls since the last text segment
|
|
533
491
|
this.hasToolCallsSinceTextStart = true
|
|
534
492
|
|
|
535
|
-
const toolCallId = chunk.
|
|
493
|
+
const toolCallId = chunk.toolCallId
|
|
536
494
|
const existingToolCall = this.toolCalls.get(toolCallId)
|
|
537
495
|
|
|
538
496
|
if (!existingToolCall) {
|
|
539
497
|
// New tool call starting
|
|
540
|
-
const initialState: ToolCallState =
|
|
541
|
-
? 'input-streaming'
|
|
542
|
-
: 'awaiting-input'
|
|
498
|
+
const initialState: ToolCallState = 'awaiting-input'
|
|
543
499
|
|
|
544
500
|
const newToolCall: InternalToolCallState = {
|
|
545
|
-
id: chunk.
|
|
546
|
-
name: chunk.
|
|
547
|
-
arguments:
|
|
501
|
+
id: chunk.toolCallId,
|
|
502
|
+
name: chunk.toolName,
|
|
503
|
+
arguments: '',
|
|
548
504
|
state: initialState,
|
|
549
505
|
parsedArguments: undefined,
|
|
550
|
-
index: chunk.index,
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// Try to parse the arguments
|
|
554
|
-
if (chunk.toolCall.function.arguments) {
|
|
555
|
-
newToolCall.parsedArguments = this.jsonParser.parse(
|
|
556
|
-
chunk.toolCall.function.arguments,
|
|
557
|
-
)
|
|
506
|
+
index: chunk.index ?? this.toolCalls.size,
|
|
558
507
|
}
|
|
559
508
|
|
|
560
509
|
this.toolCalls.set(toolCallId, newToolCall)
|
|
561
510
|
this.toolCallOrder.push(toolCallId)
|
|
562
511
|
|
|
563
|
-
// Get actual index for this tool call (based on order)
|
|
564
|
-
const actualIndex = this.toolCallOrder.indexOf(toolCallId)
|
|
565
|
-
|
|
566
|
-
// Emit legacy lifecycle event
|
|
567
|
-
this.handlers.onToolCallStart?.(
|
|
568
|
-
actualIndex,
|
|
569
|
-
chunk.toolCall.id,
|
|
570
|
-
chunk.toolCall.function.name,
|
|
571
|
-
)
|
|
572
|
-
|
|
573
|
-
// Emit legacy state change event
|
|
574
|
-
this.handlers.onToolCallStateChange?.(
|
|
575
|
-
actualIndex,
|
|
576
|
-
chunk.toolCall.id,
|
|
577
|
-
chunk.toolCall.function.name,
|
|
578
|
-
initialState,
|
|
579
|
-
chunk.toolCall.function.arguments || '',
|
|
580
|
-
newToolCall.parsedArguments,
|
|
581
|
-
)
|
|
582
|
-
|
|
583
|
-
// Emit initial delta
|
|
584
|
-
if (chunk.toolCall.function.arguments) {
|
|
585
|
-
this.handlers.onToolCallDelta?.(
|
|
586
|
-
actualIndex,
|
|
587
|
-
chunk.toolCall.function.arguments,
|
|
588
|
-
)
|
|
589
|
-
}
|
|
590
|
-
|
|
591
512
|
// Update UIMessage
|
|
592
513
|
if (this.currentAssistantMessageId) {
|
|
593
514
|
this.messages = updateToolCallPart(
|
|
594
515
|
this.messages,
|
|
595
516
|
this.currentAssistantMessageId,
|
|
596
517
|
{
|
|
597
|
-
id: chunk.
|
|
598
|
-
name: chunk.
|
|
599
|
-
arguments:
|
|
518
|
+
id: chunk.toolCallId,
|
|
519
|
+
name: chunk.toolName,
|
|
520
|
+
arguments: '',
|
|
600
521
|
state: initialState,
|
|
601
522
|
},
|
|
602
523
|
)
|
|
603
524
|
this.emitMessagesChange()
|
|
604
525
|
|
|
605
|
-
// Emit
|
|
526
|
+
// Emit granular event
|
|
606
527
|
this.events.onToolCallStateChange?.(
|
|
607
528
|
this.currentAssistantMessageId,
|
|
608
|
-
chunk.
|
|
529
|
+
chunk.toolCallId,
|
|
609
530
|
initialState,
|
|
610
|
-
|
|
531
|
+
'',
|
|
611
532
|
)
|
|
612
533
|
}
|
|
613
|
-
}
|
|
614
|
-
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Handle TOOL_CALL_ARGS event
|
|
539
|
+
*/
|
|
540
|
+
private handleToolCallArgsEvent(
|
|
541
|
+
chunk: Extract<StreamChunk, { type: 'TOOL_CALL_ARGS' }>,
|
|
542
|
+
): void {
|
|
543
|
+
const toolCallId = chunk.toolCallId
|
|
544
|
+
const existingToolCall = this.toolCalls.get(toolCallId)
|
|
545
|
+
|
|
546
|
+
if (existingToolCall) {
|
|
615
547
|
const wasAwaitingInput = existingToolCall.state === 'awaiting-input'
|
|
616
548
|
|
|
617
|
-
|
|
549
|
+
// Accumulate arguments from delta
|
|
550
|
+
existingToolCall.arguments += chunk.delta || ''
|
|
618
551
|
|
|
619
552
|
// Update state
|
|
620
|
-
if (wasAwaitingInput && chunk.
|
|
553
|
+
if (wasAwaitingInput && chunk.delta) {
|
|
621
554
|
existingToolCall.state = 'input-streaming'
|
|
622
555
|
}
|
|
623
556
|
|
|
@@ -626,27 +559,6 @@ export class StreamProcessor {
|
|
|
626
559
|
existingToolCall.arguments,
|
|
627
560
|
)
|
|
628
561
|
|
|
629
|
-
// Get actual index for this tool call
|
|
630
|
-
const actualIndex = this.toolCallOrder.indexOf(toolCallId)
|
|
631
|
-
|
|
632
|
-
// Emit legacy state change event
|
|
633
|
-
this.handlers.onToolCallStateChange?.(
|
|
634
|
-
actualIndex,
|
|
635
|
-
existingToolCall.id,
|
|
636
|
-
existingToolCall.name,
|
|
637
|
-
existingToolCall.state,
|
|
638
|
-
existingToolCall.arguments,
|
|
639
|
-
existingToolCall.parsedArguments,
|
|
640
|
-
)
|
|
641
|
-
|
|
642
|
-
// Emit delta
|
|
643
|
-
if (chunk.toolCall.function.arguments) {
|
|
644
|
-
this.handlers.onToolCallDelta?.(
|
|
645
|
-
actualIndex,
|
|
646
|
-
chunk.toolCall.function.arguments,
|
|
647
|
-
)
|
|
648
|
-
}
|
|
649
|
-
|
|
650
562
|
// Update UIMessage
|
|
651
563
|
if (this.currentAssistantMessageId) {
|
|
652
564
|
this.messages = updateToolCallPart(
|
|
@@ -661,7 +573,7 @@ export class StreamProcessor {
|
|
|
661
573
|
)
|
|
662
574
|
this.emitMessagesChange()
|
|
663
575
|
|
|
664
|
-
// Emit
|
|
576
|
+
// Emit granular event
|
|
665
577
|
this.events.onToolCallStateChange?.(
|
|
666
578
|
this.currentAssistantMessageId,
|
|
667
579
|
existingToolCall.id,
|
|
@@ -673,27 +585,20 @@ export class StreamProcessor {
|
|
|
673
585
|
}
|
|
674
586
|
|
|
675
587
|
/**
|
|
676
|
-
* Handle
|
|
588
|
+
* Handle TOOL_CALL_END event
|
|
677
589
|
*/
|
|
678
|
-
private
|
|
679
|
-
chunk: Extract<StreamChunk, { type: '
|
|
590
|
+
private handleToolCallEndEvent(
|
|
591
|
+
chunk: Extract<StreamChunk, { type: 'TOOL_CALL_END' }>,
|
|
680
592
|
): void {
|
|
681
593
|
const state: ToolResultState = 'complete'
|
|
682
594
|
|
|
683
|
-
// Emit legacy handler
|
|
684
|
-
this.handlers.onToolResultStateChange?.(
|
|
685
|
-
chunk.toolCallId,
|
|
686
|
-
chunk.content,
|
|
687
|
-
state,
|
|
688
|
-
)
|
|
689
|
-
|
|
690
595
|
// Update UIMessage if we have a current assistant message
|
|
691
|
-
if (this.currentAssistantMessageId) {
|
|
596
|
+
if (this.currentAssistantMessageId && chunk.result) {
|
|
692
597
|
this.messages = updateToolResultPart(
|
|
693
598
|
this.messages,
|
|
694
599
|
this.currentAssistantMessageId,
|
|
695
600
|
chunk.toolCallId,
|
|
696
|
-
chunk.
|
|
601
|
+
chunk.result,
|
|
697
602
|
state,
|
|
698
603
|
)
|
|
699
604
|
this.emitMessagesChange()
|
|
@@ -701,32 +606,31 @@ export class StreamProcessor {
|
|
|
701
606
|
}
|
|
702
607
|
|
|
703
608
|
/**
|
|
704
|
-
* Handle
|
|
609
|
+
* Handle RUN_FINISHED event
|
|
705
610
|
*/
|
|
706
|
-
private
|
|
611
|
+
private handleRunFinishedEvent(
|
|
612
|
+
chunk: Extract<StreamChunk, { type: 'RUN_FINISHED' }>,
|
|
613
|
+
): void {
|
|
707
614
|
this.finishReason = chunk.finishReason
|
|
708
615
|
this.isDone = true
|
|
709
616
|
this.completeAllToolCalls()
|
|
710
617
|
}
|
|
711
618
|
|
|
712
619
|
/**
|
|
713
|
-
* Handle
|
|
620
|
+
* Handle RUN_ERROR event
|
|
714
621
|
*/
|
|
715
|
-
private
|
|
716
|
-
chunk: Extract<StreamChunk, { type: '
|
|
622
|
+
private handleRunErrorEvent(
|
|
623
|
+
chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,
|
|
717
624
|
): void {
|
|
718
|
-
// Emit
|
|
719
|
-
this.
|
|
720
|
-
|
|
721
|
-
// Emit new event
|
|
722
|
-
this.events.onError?.(new Error(chunk.error.message))
|
|
625
|
+
// Emit error event
|
|
626
|
+
this.events.onError?.(new Error(chunk.error.message || 'An error occurred'))
|
|
723
627
|
}
|
|
724
628
|
|
|
725
629
|
/**
|
|
726
|
-
* Handle
|
|
630
|
+
* Handle STEP_FINISHED event (for thinking/reasoning content)
|
|
727
631
|
*/
|
|
728
|
-
private
|
|
729
|
-
chunk: Extract<StreamChunk, { type: '
|
|
632
|
+
private handleStepFinishedEvent(
|
|
633
|
+
chunk: Extract<StreamChunk, { type: 'STEP_FINISHED' }>,
|
|
730
634
|
): void {
|
|
731
635
|
const previous = this.thinkingContent
|
|
732
636
|
let nextThinking = previous
|
|
@@ -734,7 +638,7 @@ export class StreamProcessor {
|
|
|
734
638
|
// Prefer delta over content
|
|
735
639
|
if (chunk.delta && chunk.delta !== '') {
|
|
736
640
|
nextThinking = previous + chunk.delta
|
|
737
|
-
} else if (chunk.content !== '') {
|
|
641
|
+
} else if (chunk.content && chunk.content !== '') {
|
|
738
642
|
if (chunk.content.startsWith(previous)) {
|
|
739
643
|
nextThinking = chunk.content
|
|
740
644
|
} else if (previous.startsWith(chunk.content)) {
|
|
@@ -746,9 +650,6 @@ export class StreamProcessor {
|
|
|
746
650
|
|
|
747
651
|
this.thinkingContent = nextThinking
|
|
748
652
|
|
|
749
|
-
// Emit legacy handler
|
|
750
|
-
this.handlers.onThinkingUpdate?.(this.thinkingContent)
|
|
751
|
-
|
|
752
653
|
// Update UIMessage
|
|
753
654
|
if (this.currentAssistantMessageId) {
|
|
754
655
|
this.messages = updateThinkingPart(
|
|
@@ -758,7 +659,7 @@ export class StreamProcessor {
|
|
|
758
659
|
)
|
|
759
660
|
this.emitMessagesChange()
|
|
760
661
|
|
|
761
|
-
// Emit
|
|
662
|
+
// Emit granular event
|
|
762
663
|
this.events.onThinkingUpdate?.(
|
|
763
664
|
this.currentAssistantMessageId,
|
|
764
665
|
this.thinkingContent,
|
|
@@ -767,69 +668,68 @@ export class StreamProcessor {
|
|
|
767
668
|
}
|
|
768
669
|
|
|
769
670
|
/**
|
|
770
|
-
* Handle
|
|
671
|
+
* Handle CUSTOM event
|
|
672
|
+
* Handles special custom events like 'tool-input-available' for client-side tool execution
|
|
673
|
+
* and 'approval-requested' for tool approval flows
|
|
771
674
|
*/
|
|
772
|
-
private
|
|
773
|
-
chunk: Extract<StreamChunk, { type: '
|
|
675
|
+
private handleCustomEvent(
|
|
676
|
+
chunk: Extract<StreamChunk, { type: 'CUSTOM' }>,
|
|
774
677
|
): void {
|
|
775
|
-
//
|
|
776
|
-
|
|
777
|
-
chunk.
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
678
|
+
// Handle client tool input availability - trigger client-side execution
|
|
679
|
+
if (chunk.name === 'tool-input-available' && chunk.data) {
|
|
680
|
+
const { toolCallId, toolName, input } = chunk.data as {
|
|
681
|
+
toolCallId: string
|
|
682
|
+
toolName: string
|
|
683
|
+
input: any
|
|
684
|
+
}
|
|
782
685
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
chunk.approval.id,
|
|
790
|
-
)
|
|
791
|
-
this.emitMessagesChange()
|
|
686
|
+
// Emit onToolCall event for the client to execute the tool
|
|
687
|
+
this.events.onToolCall?.({
|
|
688
|
+
toolCallId,
|
|
689
|
+
toolName,
|
|
690
|
+
input,
|
|
691
|
+
})
|
|
792
692
|
}
|
|
793
693
|
|
|
794
|
-
//
|
|
795
|
-
|
|
796
|
-
toolCallId
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
694
|
+
// Handle approval requests
|
|
695
|
+
if (chunk.name === 'approval-requested' && chunk.data) {
|
|
696
|
+
const { toolCallId, toolName, input, approval } = chunk.data as {
|
|
697
|
+
toolCallId: string
|
|
698
|
+
toolName: string
|
|
699
|
+
input: any
|
|
700
|
+
approval: { id: string; needsApproval: boolean }
|
|
701
|
+
}
|
|
802
702
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
chunk.input,
|
|
814
|
-
)
|
|
703
|
+
// Update the tool call part with approval state
|
|
704
|
+
if (this.currentAssistantMessageId) {
|
|
705
|
+
this.messages = updateToolCallApproval(
|
|
706
|
+
this.messages,
|
|
707
|
+
this.currentAssistantMessageId,
|
|
708
|
+
toolCallId,
|
|
709
|
+
approval.id,
|
|
710
|
+
)
|
|
711
|
+
this.emitMessagesChange()
|
|
712
|
+
}
|
|
815
713
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
714
|
+
// Emit approval request event
|
|
715
|
+
this.events.onApprovalRequest?.({
|
|
716
|
+
toolCallId,
|
|
717
|
+
toolName,
|
|
718
|
+
input,
|
|
719
|
+
approvalId: approval.id,
|
|
720
|
+
})
|
|
721
|
+
}
|
|
822
722
|
}
|
|
823
723
|
|
|
824
724
|
/**
|
|
825
725
|
* Detect if an incoming content chunk represents a NEW text segment
|
|
826
726
|
*/
|
|
827
727
|
private isNewTextSegment(
|
|
828
|
-
chunk: Extract<StreamChunk, { type: '
|
|
728
|
+
chunk: Extract<StreamChunk, { type: 'TEXT_MESSAGE_CONTENT' }>,
|
|
829
729
|
previous: string,
|
|
830
730
|
): boolean {
|
|
831
|
-
//
|
|
832
|
-
if (chunk.
|
|
731
|
+
// Check if content is present (delta is always defined but may be empty string)
|
|
732
|
+
if (chunk.content !== undefined) {
|
|
833
733
|
if (chunk.content.length < previous.length) {
|
|
834
734
|
return true
|
|
835
735
|
}
|
|
@@ -859,7 +759,7 @@ export class StreamProcessor {
|
|
|
859
759
|
* Mark a tool call as complete and emit event
|
|
860
760
|
*/
|
|
861
761
|
private completeToolCall(
|
|
862
|
-
|
|
762
|
+
_index: number,
|
|
863
763
|
toolCall: InternalToolCallState,
|
|
864
764
|
): void {
|
|
865
765
|
toolCall.state = 'input-complete'
|
|
@@ -867,24 +767,6 @@ export class StreamProcessor {
|
|
|
867
767
|
// Try final parse
|
|
868
768
|
toolCall.parsedArguments = this.jsonParser.parse(toolCall.arguments)
|
|
869
769
|
|
|
870
|
-
// Emit legacy state change event
|
|
871
|
-
this.handlers.onToolCallStateChange?.(
|
|
872
|
-
index,
|
|
873
|
-
toolCall.id,
|
|
874
|
-
toolCall.name,
|
|
875
|
-
'input-complete',
|
|
876
|
-
toolCall.arguments,
|
|
877
|
-
toolCall.parsedArguments,
|
|
878
|
-
)
|
|
879
|
-
|
|
880
|
-
// Emit legacy complete event
|
|
881
|
-
this.handlers.onToolCallComplete?.(
|
|
882
|
-
index,
|
|
883
|
-
toolCall.id,
|
|
884
|
-
toolCall.name,
|
|
885
|
-
toolCall.arguments,
|
|
886
|
-
)
|
|
887
|
-
|
|
888
770
|
// Update UIMessage
|
|
889
771
|
if (this.currentAssistantMessageId) {
|
|
890
772
|
this.messages = updateToolCallPart(
|
|
@@ -899,7 +781,7 @@ export class StreamProcessor {
|
|
|
899
781
|
)
|
|
900
782
|
this.emitMessagesChange()
|
|
901
783
|
|
|
902
|
-
// Emit
|
|
784
|
+
// Emit granular event
|
|
903
785
|
this.events.onToolCallStateChange?.(
|
|
904
786
|
this.currentAssistantMessageId,
|
|
905
787
|
toolCall.id,
|
|
@@ -915,9 +797,6 @@ export class StreamProcessor {
|
|
|
915
797
|
private emitTextUpdate(): void {
|
|
916
798
|
this.lastEmittedText = this.currentSegmentText
|
|
917
799
|
|
|
918
|
-
// Emit legacy handler
|
|
919
|
-
this.handlers.onTextUpdate?.(this.currentSegmentText)
|
|
920
|
-
|
|
921
800
|
// Update UIMessage
|
|
922
801
|
if (this.currentAssistantMessageId) {
|
|
923
802
|
this.messages = updateTextPart(
|
|
@@ -927,7 +806,7 @@ export class StreamProcessor {
|
|
|
927
806
|
)
|
|
928
807
|
this.emitMessagesChange()
|
|
929
808
|
|
|
930
|
-
// Emit
|
|
809
|
+
// Emit granular event
|
|
931
810
|
this.events.onTextUpdate?.(
|
|
932
811
|
this.currentAssistantMessageId,
|
|
933
812
|
this.currentSegmentText,
|
|
@@ -954,14 +833,7 @@ export class StreamProcessor {
|
|
|
954
833
|
this.emitTextUpdate()
|
|
955
834
|
}
|
|
956
835
|
|
|
957
|
-
// Emit
|
|
958
|
-
const toolCalls = this.getCompletedToolCalls()
|
|
959
|
-
this.handlers.onStreamEnd?.(
|
|
960
|
-
this.totalTextContent,
|
|
961
|
-
toolCalls.length > 0 ? toolCalls : undefined,
|
|
962
|
-
)
|
|
963
|
-
|
|
964
|
-
// Emit new stream end event
|
|
836
|
+
// Emit stream end event
|
|
965
837
|
if (this.currentAssistantMessageId) {
|
|
966
838
|
const assistantMessage = this.messages.find(
|
|
967
839
|
(m) => m.id === this.currentAssistantMessageId,
|
|
@@ -1002,7 +874,7 @@ export class StreamProcessor {
|
|
|
1002
874
|
}
|
|
1003
875
|
|
|
1004
876
|
/**
|
|
1005
|
-
* Get current processor state
|
|
877
|
+
* Get current processor state
|
|
1006
878
|
*/
|
|
1007
879
|
getState(): ProcessorState {
|
|
1008
880
|
return {
|