@tanstack/ai-devtools-core 0.0.2 → 0.1.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/README.md +26 -0
- package/dist/esm/components/ConversationDetails.js +0 -15
- package/dist/esm/components/ConversationDetails.js.map +1 -1
- package/dist/esm/components/conversation/ChunksCollapsible.js.map +1 -1
- package/dist/esm/components/conversation/ConversationTabs.d.ts +1 -1
- package/dist/esm/components/conversation/ConversationTabs.js +6 -22
- package/dist/esm/components/conversation/ConversationTabs.js.map +1 -1
- package/dist/esm/components/conversation/index.d.ts +0 -1
- package/dist/esm/store/ai-context.d.ts +0 -11
- package/dist/esm/store/ai-context.js +3 -123
- package/dist/esm/store/ai-context.js.map +1 -1
- package/package.json +3 -3
- package/src/components/ConversationDetails.tsx +0 -9
- package/src/components/conversation/ChunksCollapsible.tsx +1 -3
- package/src/components/conversation/ConversationTabs.tsx +1 -17
- package/src/components/conversation/index.ts +0 -1
- package/src/store/ai-context.tsx +753 -980
- package/dist/esm/components/conversation/EmbeddingsTab.d.ts +0 -7
- package/dist/esm/components/conversation/EmbeddingsTab.js +0 -90
- package/dist/esm/components/conversation/EmbeddingsTab.js.map +0 -1
- package/src/components/conversation/EmbeddingsTab.tsx +0 -80
package/src/store/ai-context.tsx
CHANGED
|
@@ -86,15 +86,6 @@ export interface Chunk {
|
|
|
86
86
|
chunkCount: number
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export interface EmbeddingOperation {
|
|
90
|
-
id: string
|
|
91
|
-
model: string
|
|
92
|
-
inputCount: number
|
|
93
|
-
duration: number
|
|
94
|
-
timestamp: number
|
|
95
|
-
status: 'started' | 'completed'
|
|
96
|
-
}
|
|
97
|
-
|
|
98
89
|
export interface SummarizeOperation {
|
|
99
90
|
id: string
|
|
100
91
|
model: string
|
|
@@ -123,10 +114,7 @@ export interface Conversation {
|
|
|
123
114
|
providerOptions?: Record<string, unknown>
|
|
124
115
|
/** Flags for which operation types this conversation has */
|
|
125
116
|
hasChat?: boolean
|
|
126
|
-
hasEmbedding?: boolean
|
|
127
117
|
hasSummarize?: boolean
|
|
128
|
-
/** Embedding operations in this conversation */
|
|
129
|
-
embeddings?: Array<EmbeddingOperation>
|
|
130
118
|
/** Summarize operations in this conversation */
|
|
131
119
|
summaries?: Array<SummarizeOperation>
|
|
132
120
|
}
|
|
@@ -599,1165 +587,950 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
599
587
|
// ============= Client Events =============
|
|
600
588
|
|
|
601
589
|
cleanupFns.push(
|
|
602
|
-
aiEventClient.on(
|
|
603
|
-
|
|
604
|
-
(
|
|
605
|
-
|
|
590
|
+
aiEventClient.on('client:created', (e) => {
|
|
591
|
+
const clientId = e.payload.clientId
|
|
592
|
+
getOrCreateConversation(
|
|
593
|
+
clientId,
|
|
594
|
+
'client',
|
|
595
|
+
`Client Chat (${clientId.substring(0, 8)})`,
|
|
596
|
+
)
|
|
597
|
+
updateConversation(clientId, { model: undefined, provider: 'Client' })
|
|
598
|
+
}),
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
cleanupFns.push(
|
|
602
|
+
aiEventClient.on('client:message-sent', (e) => {
|
|
603
|
+
const clientId = e.payload.clientId
|
|
604
|
+
if (!state.conversations[clientId]) {
|
|
606
605
|
getOrCreateConversation(
|
|
607
606
|
clientId,
|
|
608
607
|
'client',
|
|
609
608
|
`Client Chat (${clientId.substring(0, 8)})`,
|
|
610
609
|
)
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
610
|
+
}
|
|
611
|
+
addMessage(clientId, {
|
|
612
|
+
id: e.payload.messageId,
|
|
613
|
+
role: 'user',
|
|
614
|
+
content: e.payload.content,
|
|
615
|
+
timestamp: e.payload.timestamp,
|
|
616
|
+
source: 'client',
|
|
617
|
+
})
|
|
618
|
+
updateConversation(clientId, { status: 'active' })
|
|
619
|
+
}),
|
|
615
620
|
)
|
|
616
621
|
|
|
617
622
|
cleanupFns.push(
|
|
618
|
-
aiEventClient.on(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
`Client Chat (${clientId.substring(0, 8)})`,
|
|
627
|
-
)
|
|
628
|
-
}
|
|
623
|
+
aiEventClient.on('client:message-appended', (e) => {
|
|
624
|
+
const clientId = e.payload.clientId
|
|
625
|
+
const role = e.payload.role
|
|
626
|
+
|
|
627
|
+
if (role === 'user') return
|
|
628
|
+
if (!state.conversations[clientId]) return
|
|
629
|
+
|
|
630
|
+
if (role === 'assistant') {
|
|
629
631
|
addMessage(clientId, {
|
|
630
632
|
id: e.payload.messageId,
|
|
631
|
-
role: '
|
|
632
|
-
content: e.payload.
|
|
633
|
+
role: 'assistant',
|
|
634
|
+
content: e.payload.contentPreview,
|
|
633
635
|
timestamp: e.payload.timestamp,
|
|
634
636
|
source: 'client',
|
|
635
637
|
})
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const role = e.payload.role
|
|
648
|
-
|
|
649
|
-
if (role === 'user') return
|
|
650
|
-
if (!state.conversations[clientId]) return
|
|
651
|
-
|
|
652
|
-
if (role === 'assistant') {
|
|
653
|
-
addMessage(clientId, {
|
|
654
|
-
id: e.payload.messageId,
|
|
655
|
-
role: 'assistant',
|
|
656
|
-
content: e.payload.contentPreview,
|
|
657
|
-
timestamp: e.payload.timestamp,
|
|
658
|
-
source: 'client',
|
|
659
|
-
})
|
|
660
|
-
} else if (role === 'tool') {
|
|
661
|
-
// Tool result message from the LLM
|
|
662
|
-
addMessage(clientId, {
|
|
663
|
-
id: e.payload.messageId,
|
|
664
|
-
role: 'tool',
|
|
665
|
-
content: e.payload.contentPreview,
|
|
666
|
-
timestamp: e.payload.timestamp,
|
|
667
|
-
source: 'client',
|
|
668
|
-
})
|
|
669
|
-
}
|
|
670
|
-
},
|
|
671
|
-
{ withEventTarget: false },
|
|
672
|
-
),
|
|
638
|
+
} else if (role === 'tool') {
|
|
639
|
+
// Tool result message from the LLM
|
|
640
|
+
addMessage(clientId, {
|
|
641
|
+
id: e.payload.messageId,
|
|
642
|
+
role: 'tool',
|
|
643
|
+
content: e.payload.contentPreview,
|
|
644
|
+
timestamp: e.payload.timestamp,
|
|
645
|
+
source: 'client',
|
|
646
|
+
})
|
|
647
|
+
}
|
|
648
|
+
}),
|
|
673
649
|
)
|
|
674
650
|
|
|
675
651
|
cleanupFns.push(
|
|
676
|
-
aiEventClient.on(
|
|
677
|
-
|
|
678
|
-
(
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
}
|
|
685
|
-
},
|
|
686
|
-
{ withEventTarget: false },
|
|
687
|
-
),
|
|
652
|
+
aiEventClient.on('client:loading-changed', (e) => {
|
|
653
|
+
const clientId = e.payload.clientId
|
|
654
|
+
if (state.conversations[clientId]) {
|
|
655
|
+
updateConversation(clientId, {
|
|
656
|
+
status: e.payload.isLoading ? 'active' : 'completed',
|
|
657
|
+
})
|
|
658
|
+
}
|
|
659
|
+
}),
|
|
688
660
|
)
|
|
689
661
|
|
|
690
662
|
cleanupFns.push(
|
|
691
|
-
aiEventClient.on(
|
|
692
|
-
|
|
693
|
-
(
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
}
|
|
701
|
-
},
|
|
702
|
-
{ withEventTarget: false },
|
|
703
|
-
),
|
|
663
|
+
aiEventClient.on('client:stopped', (e) => {
|
|
664
|
+
const clientId = e.payload.clientId
|
|
665
|
+
if (state.conversations[clientId]) {
|
|
666
|
+
updateConversation(clientId, {
|
|
667
|
+
status: 'completed',
|
|
668
|
+
completedAt: e.payload.timestamp,
|
|
669
|
+
})
|
|
670
|
+
}
|
|
671
|
+
}),
|
|
704
672
|
)
|
|
705
673
|
|
|
706
674
|
cleanupFns.push(
|
|
707
|
-
aiEventClient.on(
|
|
708
|
-
|
|
709
|
-
(
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}
|
|
718
|
-
},
|
|
719
|
-
{ withEventTarget: false },
|
|
720
|
-
),
|
|
675
|
+
aiEventClient.on('client:messages-cleared', (e) => {
|
|
676
|
+
const clientId = e.payload.clientId
|
|
677
|
+
if (state.conversations[clientId]) {
|
|
678
|
+
updateConversation(clientId, {
|
|
679
|
+
messages: [],
|
|
680
|
+
chunks: [],
|
|
681
|
+
usage: undefined,
|
|
682
|
+
})
|
|
683
|
+
}
|
|
684
|
+
}),
|
|
721
685
|
)
|
|
722
686
|
|
|
723
687
|
cleanupFns.push(
|
|
724
|
-
aiEventClient.on(
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
}
|
|
735
|
-
},
|
|
736
|
-
{ withEventTarget: false },
|
|
737
|
-
),
|
|
688
|
+
aiEventClient.on('client:reloaded', (e) => {
|
|
689
|
+
const clientId = e.payload.clientId
|
|
690
|
+
const conv = state.conversations[clientId]
|
|
691
|
+
if (conv) {
|
|
692
|
+
updateConversation(clientId, {
|
|
693
|
+
messages: conv.messages.slice(0, e.payload.fromMessageIndex),
|
|
694
|
+
status: 'active',
|
|
695
|
+
})
|
|
696
|
+
}
|
|
697
|
+
}),
|
|
738
698
|
)
|
|
739
699
|
|
|
740
700
|
cleanupFns.push(
|
|
741
|
-
aiEventClient.on(
|
|
742
|
-
|
|
743
|
-
(e)
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
}
|
|
748
|
-
},
|
|
749
|
-
{ withEventTarget: false },
|
|
750
|
-
),
|
|
701
|
+
aiEventClient.on('client:error-changed', (e) => {
|
|
702
|
+
const clientId = e.payload.clientId
|
|
703
|
+
if (state.conversations[clientId] && e.payload.error) {
|
|
704
|
+
updateConversation(clientId, { status: 'error' })
|
|
705
|
+
}
|
|
706
|
+
}),
|
|
751
707
|
)
|
|
752
708
|
|
|
753
709
|
cleanupFns.push(
|
|
754
|
-
aiEventClient.on(
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
const messageId = e.payload.messageId
|
|
759
|
-
const content = e.payload.content
|
|
710
|
+
aiEventClient.on('client:assistant-message-updated', (e) => {
|
|
711
|
+
const clientId = e.payload.clientId
|
|
712
|
+
const messageId = e.payload.messageId
|
|
713
|
+
const content = e.payload.content
|
|
760
714
|
|
|
761
|
-
|
|
715
|
+
if (!state.conversations[clientId]) return
|
|
762
716
|
|
|
763
|
-
|
|
717
|
+
const conv = state.conversations[clientId]
|
|
764
718
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
719
|
+
// Find message by ID anywhere in the list, not just the last one
|
|
720
|
+
const messageIndex = conv.messages.findIndex(
|
|
721
|
+
(m: Message) => m.id === messageId,
|
|
722
|
+
)
|
|
769
723
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
{ withEventTarget: false },
|
|
780
|
-
),
|
|
724
|
+
// Only update existing messages, don't create new ones
|
|
725
|
+
// (client:message-appended is responsible for creating messages)
|
|
726
|
+
if (messageIndex >= 0) {
|
|
727
|
+
updateMessage(clientId, messageIndex, {
|
|
728
|
+
content,
|
|
729
|
+
model: conv.model,
|
|
730
|
+
})
|
|
731
|
+
}
|
|
732
|
+
}),
|
|
781
733
|
)
|
|
782
734
|
|
|
783
735
|
cleanupFns.push(
|
|
784
|
-
aiEventClient.on(
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
timestamp: number
|
|
802
|
-
}
|
|
736
|
+
aiEventClient.on('client:tool-call-updated', (e) => {
|
|
737
|
+
const {
|
|
738
|
+
clientId,
|
|
739
|
+
messageId,
|
|
740
|
+
toolCallId,
|
|
741
|
+
toolName,
|
|
742
|
+
state: toolCallState,
|
|
743
|
+
arguments: args,
|
|
744
|
+
} = e.payload as {
|
|
745
|
+
clientId: string
|
|
746
|
+
messageId: string
|
|
747
|
+
toolCallId: string
|
|
748
|
+
toolName: string
|
|
749
|
+
state: string
|
|
750
|
+
arguments: unknown
|
|
751
|
+
timestamp: number
|
|
752
|
+
}
|
|
803
753
|
|
|
804
|
-
|
|
754
|
+
if (!state.conversations[clientId]) return
|
|
805
755
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
756
|
+
const conv = state.conversations[clientId]
|
|
757
|
+
const messageIndex = conv.messages.findIndex(
|
|
758
|
+
(m: Message) => m.id === messageId,
|
|
759
|
+
)
|
|
760
|
+
if (messageIndex === -1) return
|
|
811
761
|
|
|
812
|
-
|
|
813
|
-
|
|
762
|
+
const message = conv.messages[messageIndex]
|
|
763
|
+
if (!message) return
|
|
814
764
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
765
|
+
const toolCalls = message.toolCalls || []
|
|
766
|
+
const existingToolIndex = toolCalls.findIndex(
|
|
767
|
+
(t: ToolCall) => t.id === toolCallId,
|
|
768
|
+
)
|
|
819
769
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
770
|
+
const toolCall: ToolCall = {
|
|
771
|
+
id: toolCallId,
|
|
772
|
+
name: toolName,
|
|
773
|
+
arguments: JSON.stringify(args, null, 2),
|
|
774
|
+
state: toolCallState,
|
|
775
|
+
}
|
|
826
776
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
{ withEventTarget: false },
|
|
834
|
-
),
|
|
777
|
+
if (existingToolIndex >= 0) {
|
|
778
|
+
updateToolCall(clientId, messageIndex, existingToolIndex, toolCall)
|
|
779
|
+
} else {
|
|
780
|
+
setToolCalls(clientId, messageIndex, [...toolCalls, toolCall])
|
|
781
|
+
}
|
|
782
|
+
}),
|
|
835
783
|
)
|
|
836
784
|
|
|
837
785
|
cleanupFns.push(
|
|
838
|
-
aiEventClient.on(
|
|
839
|
-
|
|
840
|
-
(e) => {
|
|
841
|
-
const { clientId, messageId, toolCallId, approvalId } = e.payload
|
|
786
|
+
aiEventClient.on('client:approval-requested', (e) => {
|
|
787
|
+
const { clientId, messageId, toolCallId, approvalId } = e.payload
|
|
842
788
|
|
|
843
|
-
|
|
789
|
+
if (!state.conversations[clientId]) return
|
|
844
790
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
)
|
|
849
|
-
if (messageIndex === -1) return
|
|
850
|
-
|
|
851
|
-
const message = conv.messages[messageIndex]
|
|
852
|
-
if (!message?.toolCalls) return
|
|
791
|
+
const conv = state.conversations[clientId]
|
|
792
|
+
const messageIndex = conv.messages.findIndex((m) => m.id === messageId)
|
|
793
|
+
if (messageIndex === -1) return
|
|
853
794
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
)
|
|
857
|
-
if (toolCallIndex === -1) return
|
|
795
|
+
const message = conv.messages[messageIndex]
|
|
796
|
+
if (!message?.toolCalls) return
|
|
858
797
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
798
|
+
const toolCallIndex = message.toolCalls.findIndex(
|
|
799
|
+
(t) => t.id === toolCallId,
|
|
800
|
+
)
|
|
801
|
+
if (toolCallIndex === -1) return
|
|
802
|
+
|
|
803
|
+
updateToolCall(clientId, messageIndex, toolCallIndex, {
|
|
804
|
+
approvalRequired: true,
|
|
805
|
+
approvalId,
|
|
806
|
+
state: 'approval-requested',
|
|
807
|
+
})
|
|
808
|
+
}),
|
|
867
809
|
)
|
|
868
810
|
|
|
869
811
|
// ============= Tool Events =============
|
|
870
812
|
|
|
871
813
|
cleanupFns.push(
|
|
872
|
-
aiEventClient.on(
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
)
|
|
894
|
-
const message = conv.messages[messageIndex]
|
|
895
|
-
if (!message?.toolCalls) continue
|
|
814
|
+
aiEventClient.on('tool:result-added', (e) => {
|
|
815
|
+
const {
|
|
816
|
+
clientId,
|
|
817
|
+
toolCallId,
|
|
818
|
+
toolName,
|
|
819
|
+
output,
|
|
820
|
+
state: resultState,
|
|
821
|
+
timestamp,
|
|
822
|
+
} = e.payload
|
|
823
|
+
|
|
824
|
+
if (!state.conversations[clientId]) return
|
|
825
|
+
|
|
826
|
+
const conv = state.conversations[clientId]
|
|
827
|
+
|
|
828
|
+
// Find the message with the tool call and update it
|
|
829
|
+
for (
|
|
830
|
+
let messageIndex = conv.messages.length - 1;
|
|
831
|
+
messageIndex >= 0;
|
|
832
|
+
messageIndex--
|
|
833
|
+
) {
|
|
834
|
+
const message = conv.messages[messageIndex]
|
|
835
|
+
if (!message?.toolCalls) continue
|
|
896
836
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
837
|
+
const toolCallIndex = message.toolCalls.findIndex(
|
|
838
|
+
(t: ToolCall) => t.id === toolCallId,
|
|
839
|
+
)
|
|
840
|
+
if (toolCallIndex >= 0) {
|
|
841
|
+
// Update the tool call state
|
|
842
|
+
updateToolCall(clientId, messageIndex, toolCallIndex, {
|
|
843
|
+
result: output,
|
|
844
|
+
state: resultState === 'output-error' ? 'error' : 'complete',
|
|
845
|
+
})
|
|
906
846
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
}
|
|
918
|
-
addChunkToMessage(clientId, chunk)
|
|
919
|
-
return
|
|
847
|
+
// Also add a chunk to show the tool result in the chunks view
|
|
848
|
+
const chunk: Chunk = {
|
|
849
|
+
id: `chunk-tool-result-${toolCallId}-${Date.now()}`,
|
|
850
|
+
type: 'tool_result',
|
|
851
|
+
messageId: message.id,
|
|
852
|
+
toolCallId,
|
|
853
|
+
toolName,
|
|
854
|
+
result: output,
|
|
855
|
+
timestamp,
|
|
856
|
+
chunkCount: 1,
|
|
920
857
|
}
|
|
858
|
+
addChunkToMessage(clientId, chunk)
|
|
859
|
+
return
|
|
921
860
|
}
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
),
|
|
861
|
+
}
|
|
862
|
+
}),
|
|
925
863
|
)
|
|
926
864
|
|
|
927
865
|
cleanupFns.push(
|
|
928
|
-
aiEventClient.on(
|
|
929
|
-
|
|
930
|
-
(e) => {
|
|
931
|
-
const { clientId, toolCallId, approved } = e.payload
|
|
866
|
+
aiEventClient.on('tool:approval-responded', (e) => {
|
|
867
|
+
const { clientId, toolCallId, approved } = e.payload
|
|
932
868
|
|
|
933
|
-
|
|
869
|
+
if (!state.conversations[clientId]) return
|
|
934
870
|
|
|
935
|
-
|
|
871
|
+
const conv = state.conversations[clientId]
|
|
936
872
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
873
|
+
for (
|
|
874
|
+
let messageIndex = conv.messages.length - 1;
|
|
875
|
+
messageIndex >= 0;
|
|
876
|
+
messageIndex--
|
|
877
|
+
) {
|
|
878
|
+
const message = conv.messages[messageIndex]
|
|
879
|
+
if (!message?.toolCalls) continue
|
|
944
880
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
}
|
|
881
|
+
const toolCallIndex = message.toolCalls.findIndex(
|
|
882
|
+
(t: ToolCall) => t.id === toolCallId,
|
|
883
|
+
)
|
|
884
|
+
if (toolCallIndex >= 0) {
|
|
885
|
+
updateToolCall(clientId, messageIndex, toolCallIndex, {
|
|
886
|
+
state: approved ? 'approved' : 'denied',
|
|
887
|
+
approvalRequired: false,
|
|
888
|
+
})
|
|
889
|
+
return
|
|
955
890
|
}
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
),
|
|
891
|
+
}
|
|
892
|
+
}),
|
|
959
893
|
)
|
|
960
894
|
|
|
961
895
|
// ============= Stream Events =============
|
|
962
896
|
|
|
963
897
|
cleanupFns.push(
|
|
964
|
-
aiEventClient.on(
|
|
965
|
-
|
|
966
|
-
(
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
chunkCount: 1,
|
|
979
|
-
}
|
|
898
|
+
aiEventClient.on('stream:chunk:content', (e) => {
|
|
899
|
+
const streamId = e.payload.streamId
|
|
900
|
+
const conversationId = streamToConversation.get(streamId)
|
|
901
|
+
if (!conversationId) return
|
|
902
|
+
|
|
903
|
+
const chunk: Chunk = {
|
|
904
|
+
id: `chunk-${Date.now()}-${Math.random()}`,
|
|
905
|
+
type: 'content',
|
|
906
|
+
messageId: e.payload.messageId,
|
|
907
|
+
content: e.payload.content,
|
|
908
|
+
delta: e.payload.delta,
|
|
909
|
+
timestamp: e.payload.timestamp,
|
|
910
|
+
chunkCount: 1,
|
|
911
|
+
}
|
|
980
912
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
{ withEventTarget: false },
|
|
994
|
-
),
|
|
913
|
+
const conv = state.conversations[conversationId]
|
|
914
|
+
if (conv?.type === 'client') {
|
|
915
|
+
addChunkToMessage(conversationId, chunk)
|
|
916
|
+
} else {
|
|
917
|
+
ensureMessageForChunk(
|
|
918
|
+
conversationId,
|
|
919
|
+
e.payload.messageId,
|
|
920
|
+
e.payload.timestamp,
|
|
921
|
+
)
|
|
922
|
+
addChunk(conversationId, chunk)
|
|
923
|
+
}
|
|
924
|
+
}),
|
|
995
925
|
)
|
|
996
926
|
|
|
997
927
|
cleanupFns.push(
|
|
998
|
-
aiEventClient.on(
|
|
999
|
-
|
|
1000
|
-
(
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
chunkCount: 1,
|
|
1014
|
-
}
|
|
928
|
+
aiEventClient.on('stream:chunk:tool-call', (e) => {
|
|
929
|
+
const streamId = e.payload.streamId
|
|
930
|
+
const conversationId = streamToConversation.get(streamId)
|
|
931
|
+
if (!conversationId) return
|
|
932
|
+
|
|
933
|
+
const chunk: Chunk = {
|
|
934
|
+
id: `chunk-${Date.now()}-${Math.random()}`,
|
|
935
|
+
type: 'tool_call',
|
|
936
|
+
messageId: e.payload.messageId,
|
|
937
|
+
toolCallId: e.payload.toolCallId,
|
|
938
|
+
toolName: e.payload.toolName,
|
|
939
|
+
arguments: e.payload.arguments,
|
|
940
|
+
timestamp: e.payload.timestamp,
|
|
941
|
+
chunkCount: 1,
|
|
942
|
+
}
|
|
1015
943
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
{ withEventTarget: false },
|
|
1029
|
-
),
|
|
944
|
+
const conv = state.conversations[conversationId]
|
|
945
|
+
if (conv?.type === 'client') {
|
|
946
|
+
addChunkToMessage(conversationId, chunk)
|
|
947
|
+
} else {
|
|
948
|
+
ensureMessageForChunk(
|
|
949
|
+
conversationId,
|
|
950
|
+
e.payload.messageId,
|
|
951
|
+
e.payload.timestamp,
|
|
952
|
+
)
|
|
953
|
+
addChunk(conversationId, chunk)
|
|
954
|
+
}
|
|
955
|
+
}),
|
|
1030
956
|
)
|
|
1031
957
|
|
|
1032
958
|
cleanupFns.push(
|
|
1033
|
-
aiEventClient.on(
|
|
1034
|
-
|
|
1035
|
-
(
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
chunkCount: 1,
|
|
1048
|
-
}
|
|
959
|
+
aiEventClient.on('stream:chunk:tool-result', (e) => {
|
|
960
|
+
const streamId = e.payload.streamId
|
|
961
|
+
const conversationId = streamToConversation.get(streamId)
|
|
962
|
+
if (!conversationId) return
|
|
963
|
+
|
|
964
|
+
const chunk: Chunk = {
|
|
965
|
+
id: `chunk-${Date.now()}-${Math.random()}`,
|
|
966
|
+
type: 'tool_result',
|
|
967
|
+
messageId: e.payload.messageId,
|
|
968
|
+
toolCallId: e.payload.toolCallId,
|
|
969
|
+
content: e.payload.result,
|
|
970
|
+
timestamp: e.payload.timestamp,
|
|
971
|
+
chunkCount: 1,
|
|
972
|
+
}
|
|
1049
973
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
974
|
+
const conv = state.conversations[conversationId]
|
|
975
|
+
if (conv?.type === 'client') {
|
|
976
|
+
addChunkToMessage(conversationId, chunk)
|
|
977
|
+
} else {
|
|
978
|
+
ensureMessageForChunk(
|
|
979
|
+
conversationId,
|
|
980
|
+
e.payload.messageId,
|
|
981
|
+
e.payload.timestamp,
|
|
982
|
+
)
|
|
983
|
+
addChunk(conversationId, chunk)
|
|
984
|
+
}
|
|
1061
985
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
}
|
|
986
|
+
// Also update the toolCalls array with the result
|
|
987
|
+
if (conv && e.payload.toolCallId) {
|
|
988
|
+
for (let i = conv.messages.length - 1; i >= 0; i--) {
|
|
989
|
+
const message = conv.messages[i]
|
|
990
|
+
if (!message?.toolCalls) continue
|
|
991
|
+
|
|
992
|
+
const toolCallIndex = message.toolCalls.findIndex(
|
|
993
|
+
(t) => t.id === e.payload.toolCallId,
|
|
994
|
+
)
|
|
995
|
+
if (toolCallIndex >= 0) {
|
|
996
|
+
updateToolCall(conversationId, i, toolCallIndex, {
|
|
997
|
+
result: e.payload.result,
|
|
998
|
+
state: 'complete',
|
|
999
|
+
})
|
|
1000
|
+
break
|
|
1078
1001
|
}
|
|
1079
1002
|
}
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
),
|
|
1003
|
+
}
|
|
1004
|
+
}),
|
|
1083
1005
|
)
|
|
1084
1006
|
|
|
1085
1007
|
cleanupFns.push(
|
|
1086
|
-
aiEventClient.on(
|
|
1087
|
-
|
|
1088
|
-
(
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
chunkCount: 1,
|
|
1101
|
-
}
|
|
1008
|
+
aiEventClient.on('stream:chunk:thinking', (e) => {
|
|
1009
|
+
const streamId = e.payload.streamId
|
|
1010
|
+
const conversationId = streamToConversation.get(streamId)
|
|
1011
|
+
if (!conversationId) return
|
|
1012
|
+
|
|
1013
|
+
const chunk: Chunk = {
|
|
1014
|
+
id: `chunk-${Date.now()}-${Math.random()}`,
|
|
1015
|
+
type: 'thinking',
|
|
1016
|
+
messageId: e.payload.messageId,
|
|
1017
|
+
content: e.payload.content,
|
|
1018
|
+
delta: e.payload.delta,
|
|
1019
|
+
timestamp: e.payload.timestamp,
|
|
1020
|
+
chunkCount: 1,
|
|
1021
|
+
}
|
|
1102
1022
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
)
|
|
1111
|
-
if (messageIndex !== -1) {
|
|
1112
|
-
updateMessage(conversationId, messageIndex, {
|
|
1113
|
-
thinkingContent: e.payload.content,
|
|
1114
|
-
})
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
} else {
|
|
1118
|
-
ensureMessageForChunk(
|
|
1119
|
-
conversationId,
|
|
1120
|
-
e.payload.messageId,
|
|
1121
|
-
e.payload.timestamp,
|
|
1023
|
+
const conv = state.conversations[conversationId]
|
|
1024
|
+
if (conv?.type === 'client') {
|
|
1025
|
+
addChunkToMessage(conversationId, chunk)
|
|
1026
|
+
|
|
1027
|
+
if (e.payload.messageId) {
|
|
1028
|
+
const messageIndex = conv.messages.findIndex(
|
|
1029
|
+
(msg) => msg.id === e.payload.messageId,
|
|
1122
1030
|
)
|
|
1123
|
-
|
|
1031
|
+
if (messageIndex !== -1) {
|
|
1032
|
+
updateMessage(conversationId, messageIndex, {
|
|
1033
|
+
thinkingContent: e.payload.content,
|
|
1034
|
+
})
|
|
1035
|
+
}
|
|
1124
1036
|
}
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1037
|
+
} else {
|
|
1038
|
+
ensureMessageForChunk(
|
|
1039
|
+
conversationId,
|
|
1040
|
+
e.payload.messageId,
|
|
1041
|
+
e.payload.timestamp,
|
|
1042
|
+
)
|
|
1043
|
+
addChunk(conversationId, chunk)
|
|
1044
|
+
}
|
|
1045
|
+
}),
|
|
1128
1046
|
)
|
|
1129
1047
|
|
|
1130
1048
|
cleanupFns.push(
|
|
1131
|
-
aiEventClient.on(
|
|
1132
|
-
|
|
1133
|
-
(
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
chunkCount: 1,
|
|
1145
|
-
}
|
|
1049
|
+
aiEventClient.on('stream:chunk:done', (e) => {
|
|
1050
|
+
const streamId = e.payload.streamId
|
|
1051
|
+
const conversationId = streamToConversation.get(streamId)
|
|
1052
|
+
if (!conversationId) return
|
|
1053
|
+
|
|
1054
|
+
const chunk: Chunk = {
|
|
1055
|
+
id: `chunk-${Date.now()}-${Math.random()}`,
|
|
1056
|
+
type: 'done',
|
|
1057
|
+
messageId: e.payload.messageId,
|
|
1058
|
+
finishReason: e.payload.finishReason || undefined,
|
|
1059
|
+
timestamp: e.payload.timestamp,
|
|
1060
|
+
chunkCount: 1,
|
|
1061
|
+
}
|
|
1146
1062
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1063
|
+
if (e.payload.usage) {
|
|
1064
|
+
updateConversation(conversationId, { usage: e.payload.usage })
|
|
1065
|
+
updateMessageUsage(
|
|
1066
|
+
conversationId,
|
|
1067
|
+
e.payload.messageId,
|
|
1068
|
+
e.payload.usage,
|
|
1069
|
+
)
|
|
1070
|
+
}
|
|
1155
1071
|
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1072
|
+
const conv = state.conversations[conversationId]
|
|
1073
|
+
if (conv?.type === 'client') {
|
|
1074
|
+
addChunkToMessage(conversationId, chunk)
|
|
1075
|
+
} else {
|
|
1076
|
+
ensureMessageForChunk(
|
|
1077
|
+
conversationId,
|
|
1078
|
+
e.payload.messageId,
|
|
1079
|
+
e.payload.timestamp,
|
|
1080
|
+
)
|
|
1081
|
+
addChunk(conversationId, chunk)
|
|
1082
|
+
}
|
|
1167
1083
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
{ withEventTarget: false },
|
|
1174
|
-
),
|
|
1084
|
+
updateConversation(conversationId, {
|
|
1085
|
+
status: 'completed',
|
|
1086
|
+
completedAt: e.payload.timestamp,
|
|
1087
|
+
})
|
|
1088
|
+
}),
|
|
1175
1089
|
)
|
|
1176
1090
|
|
|
1177
1091
|
cleanupFns.push(
|
|
1178
|
-
aiEventClient.on(
|
|
1179
|
-
|
|
1180
|
-
(
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
chunkCount: 1,
|
|
1192
|
-
}
|
|
1092
|
+
aiEventClient.on('stream:chunk:error', (e) => {
|
|
1093
|
+
const streamId = e.payload.streamId
|
|
1094
|
+
const conversationId = streamToConversation.get(streamId)
|
|
1095
|
+
if (!conversationId) return
|
|
1096
|
+
|
|
1097
|
+
const chunk: Chunk = {
|
|
1098
|
+
id: `chunk-${Date.now()}-${Math.random()}`,
|
|
1099
|
+
type: 'error',
|
|
1100
|
+
messageId: e.payload.messageId,
|
|
1101
|
+
error: e.payload.error,
|
|
1102
|
+
timestamp: e.payload.timestamp,
|
|
1103
|
+
chunkCount: 1,
|
|
1104
|
+
}
|
|
1193
1105
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1106
|
+
const conv = state.conversations[conversationId]
|
|
1107
|
+
if (conv?.type === 'client') {
|
|
1108
|
+
addChunkToMessage(conversationId, chunk)
|
|
1109
|
+
} else {
|
|
1110
|
+
ensureMessageForChunk(
|
|
1111
|
+
conversationId,
|
|
1112
|
+
e.payload.messageId,
|
|
1113
|
+
e.payload.timestamp,
|
|
1114
|
+
)
|
|
1115
|
+
addChunk(conversationId, chunk)
|
|
1116
|
+
}
|
|
1205
1117
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
{ withEventTarget: false },
|
|
1212
|
-
),
|
|
1118
|
+
updateConversation(conversationId, {
|
|
1119
|
+
status: 'error',
|
|
1120
|
+
completedAt: e.payload.timestamp,
|
|
1121
|
+
})
|
|
1122
|
+
}),
|
|
1213
1123
|
)
|
|
1214
1124
|
|
|
1215
1125
|
cleanupFns.push(
|
|
1216
|
-
aiEventClient.on(
|
|
1217
|
-
|
|
1218
|
-
(
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
})
|
|
1227
|
-
},
|
|
1228
|
-
{ withEventTarget: false },
|
|
1229
|
-
),
|
|
1126
|
+
aiEventClient.on('stream:ended', (e) => {
|
|
1127
|
+
const streamId = e.payload.streamId
|
|
1128
|
+
const conversationId = streamToConversation.get(streamId)
|
|
1129
|
+
if (!conversationId) return
|
|
1130
|
+
|
|
1131
|
+
updateConversation(conversationId, {
|
|
1132
|
+
status: 'completed',
|
|
1133
|
+
completedAt: e.payload.timestamp,
|
|
1134
|
+
})
|
|
1135
|
+
}),
|
|
1230
1136
|
)
|
|
1231
1137
|
|
|
1232
1138
|
cleanupFns.push(
|
|
1233
|
-
aiEventClient.on(
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
const conversationId = streamToConversation.get(streamId)
|
|
1247
|
-
if (!conversationId) return
|
|
1248
|
-
|
|
1249
|
-
const conv = state.conversations[conversationId]
|
|
1250
|
-
if (!conv) return
|
|
1251
|
-
|
|
1252
|
-
const chunk: Chunk = {
|
|
1253
|
-
id: `chunk-${Date.now()}-${Math.random()}`,
|
|
1254
|
-
type: 'approval',
|
|
1255
|
-
messageId: messageId,
|
|
1256
|
-
toolCallId,
|
|
1257
|
-
toolName,
|
|
1258
|
-
approvalId,
|
|
1259
|
-
input,
|
|
1260
|
-
timestamp,
|
|
1261
|
-
chunkCount: 1,
|
|
1262
|
-
}
|
|
1139
|
+
aiEventClient.on('stream:approval-requested', (e) => {
|
|
1140
|
+
const {
|
|
1141
|
+
streamId,
|
|
1142
|
+
messageId,
|
|
1143
|
+
toolCallId,
|
|
1144
|
+
toolName,
|
|
1145
|
+
input,
|
|
1146
|
+
approvalId,
|
|
1147
|
+
timestamp,
|
|
1148
|
+
} = e.payload
|
|
1149
|
+
|
|
1150
|
+
const conversationId = streamToConversation.get(streamId)
|
|
1151
|
+
if (!conversationId) return
|
|
1263
1152
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
}
|
|
1153
|
+
const conv = state.conversations[conversationId]
|
|
1154
|
+
if (!conv) return
|
|
1155
|
+
|
|
1156
|
+
const chunk: Chunk = {
|
|
1157
|
+
id: `chunk-${Date.now()}-${Math.random()}`,
|
|
1158
|
+
type: 'approval',
|
|
1159
|
+
messageId: messageId,
|
|
1160
|
+
toolCallId,
|
|
1161
|
+
toolName,
|
|
1162
|
+
approvalId,
|
|
1163
|
+
input,
|
|
1164
|
+
timestamp,
|
|
1165
|
+
chunkCount: 1,
|
|
1166
|
+
}
|
|
1269
1167
|
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1168
|
+
if (conv.type === 'client') {
|
|
1169
|
+
addChunkToMessage(conversationId, chunk)
|
|
1170
|
+
} else {
|
|
1171
|
+
addChunk(conversationId, chunk)
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
for (let i = conv.messages.length - 1; i >= 0; i--) {
|
|
1175
|
+
const message = conv.messages[i]
|
|
1176
|
+
if (!message) continue
|
|
1177
|
+
|
|
1178
|
+
if (message.role === 'assistant' && message.toolCalls) {
|
|
1179
|
+
const toolCallIndex = message.toolCalls.findIndex(
|
|
1180
|
+
(t: ToolCall) => t.id === toolCallId,
|
|
1181
|
+
)
|
|
1182
|
+
if (toolCallIndex >= 0) {
|
|
1183
|
+
updateToolCall(conversationId, i, toolCallIndex, {
|
|
1184
|
+
approvalRequired: true,
|
|
1185
|
+
approvalId,
|
|
1186
|
+
state: 'approval-requested',
|
|
1187
|
+
})
|
|
1188
|
+
return
|
|
1286
1189
|
}
|
|
1287
1190
|
}
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
),
|
|
1191
|
+
}
|
|
1192
|
+
}),
|
|
1291
1193
|
)
|
|
1292
1194
|
|
|
1293
1195
|
// ============= Processor Events =============
|
|
1294
1196
|
|
|
1295
1197
|
cleanupFns.push(
|
|
1296
|
-
aiEventClient.on(
|
|
1297
|
-
|
|
1298
|
-
(e) => {
|
|
1299
|
-
const streamId = e.payload.streamId
|
|
1198
|
+
aiEventClient.on('processor:text-updated', (e) => {
|
|
1199
|
+
const streamId = e.payload.streamId
|
|
1300
1200
|
|
|
1301
|
-
|
|
1201
|
+
let conversationId = streamToConversation.get(streamId)
|
|
1302
1202
|
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1203
|
+
if (!conversationId) {
|
|
1204
|
+
const activeClients = Object.values(state.conversations)
|
|
1205
|
+
.filter((c) => c.type === 'client' && c.status === 'active')
|
|
1206
|
+
.sort((a, b) => b.startedAt - a.startedAt)
|
|
1307
1207
|
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
}
|
|
1208
|
+
if (activeClients.length > 0 && activeClients[0]) {
|
|
1209
|
+
conversationId = activeClients[0].id
|
|
1210
|
+
streamToConversation.set(streamId, conversationId)
|
|
1312
1211
|
}
|
|
1212
|
+
}
|
|
1313
1213
|
|
|
1314
|
-
|
|
1214
|
+
if (!conversationId) return
|
|
1315
1215
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1216
|
+
const conv = state.conversations[conversationId]
|
|
1217
|
+
if (!conv) return
|
|
1318
1218
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
{ withEventTarget: false },
|
|
1329
|
-
),
|
|
1219
|
+
// Only update existing assistant messages, don't create new ones
|
|
1220
|
+
// (client:message-appended is responsible for creating messages)
|
|
1221
|
+
const lastMessage = conv.messages[conv.messages.length - 1]
|
|
1222
|
+
if (lastMessage && lastMessage.role === 'assistant') {
|
|
1223
|
+
updateMessage(conversationId, conv.messages.length - 1, {
|
|
1224
|
+
content: e.payload.content,
|
|
1225
|
+
})
|
|
1226
|
+
}
|
|
1227
|
+
}),
|
|
1330
1228
|
)
|
|
1331
1229
|
|
|
1332
1230
|
cleanupFns.push(
|
|
1333
|
-
aiEventClient.on(
|
|
1334
|
-
|
|
1335
|
-
(
|
|
1336
|
-
const streamId = e.payload.streamId
|
|
1337
|
-
const conversationId = streamToConversation.get(streamId)
|
|
1231
|
+
aiEventClient.on('processor:tool-call-state-changed', (e) => {
|
|
1232
|
+
const streamId = e.payload.streamId
|
|
1233
|
+
const conversationId = streamToConversation.get(streamId)
|
|
1338
1234
|
|
|
1339
|
-
|
|
1235
|
+
if (!conversationId || !state.conversations[conversationId]) return
|
|
1340
1236
|
|
|
1341
|
-
|
|
1342
|
-
|
|
1237
|
+
const conv = state.conversations[conversationId]
|
|
1238
|
+
const lastMessage = conv.messages[conv.messages.length - 1]
|
|
1343
1239
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1240
|
+
if (lastMessage && lastMessage.role === 'assistant') {
|
|
1241
|
+
const toolCalls = lastMessage.toolCalls || []
|
|
1242
|
+
const existingToolIndex = toolCalls.findIndex(
|
|
1243
|
+
(t) => t.id === e.payload.toolCallId,
|
|
1244
|
+
)
|
|
1349
1245
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1246
|
+
const toolCall: ToolCall = {
|
|
1247
|
+
id: e.payload.toolCallId,
|
|
1248
|
+
name: e.payload.toolName,
|
|
1249
|
+
arguments: JSON.stringify(e.payload.arguments, null, 2),
|
|
1250
|
+
state: e.payload.state,
|
|
1251
|
+
}
|
|
1356
1252
|
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
}
|
|
1253
|
+
if (existingToolIndex >= 0) {
|
|
1254
|
+
updateToolCall(
|
|
1255
|
+
conversationId,
|
|
1256
|
+
conv.messages.length - 1,
|
|
1257
|
+
existingToolIndex,
|
|
1258
|
+
toolCall,
|
|
1259
|
+
)
|
|
1260
|
+
} else {
|
|
1261
|
+
setToolCalls(conversationId, conv.messages.length - 1, [
|
|
1262
|
+
...toolCalls,
|
|
1263
|
+
toolCall,
|
|
1264
|
+
])
|
|
1370
1265
|
}
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
),
|
|
1266
|
+
}
|
|
1267
|
+
}),
|
|
1374
1268
|
)
|
|
1375
1269
|
|
|
1376
1270
|
cleanupFns.push(
|
|
1377
|
-
aiEventClient.on(
|
|
1378
|
-
|
|
1379
|
-
(
|
|
1380
|
-
const streamId = e.payload.streamId
|
|
1381
|
-
const conversationId = streamToConversation.get(streamId)
|
|
1271
|
+
aiEventClient.on('processor:tool-result-state-changed', (e) => {
|
|
1272
|
+
const streamId = e.payload.streamId
|
|
1273
|
+
const conversationId = streamToConversation.get(streamId)
|
|
1382
1274
|
|
|
1383
|
-
|
|
1275
|
+
if (!conversationId || !state.conversations[conversationId]) return
|
|
1384
1276
|
|
|
1385
|
-
|
|
1277
|
+
const conv = state.conversations[conversationId]
|
|
1386
1278
|
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1279
|
+
for (let i = conv.messages.length - 1; i >= 0; i--) {
|
|
1280
|
+
const message = conv.messages[i]
|
|
1281
|
+
if (!message?.toolCalls) continue
|
|
1390
1282
|
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
}
|
|
1283
|
+
const toolCallIndex = message.toolCalls.findIndex(
|
|
1284
|
+
(t) => t.id === e.payload.toolCallId,
|
|
1285
|
+
)
|
|
1286
|
+
if (toolCallIndex >= 0) {
|
|
1287
|
+
updateToolCall(conversationId, i, toolCallIndex, {
|
|
1288
|
+
result: e.payload.content,
|
|
1289
|
+
state: e.payload.error ? 'error' : e.payload.state,
|
|
1290
|
+
})
|
|
1291
|
+
return
|
|
1401
1292
|
}
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
),
|
|
1293
|
+
}
|
|
1294
|
+
}),
|
|
1405
1295
|
)
|
|
1406
1296
|
|
|
1407
1297
|
// ============= Chat Events (for usage tracking) =============
|
|
1408
1298
|
|
|
1409
1299
|
cleanupFns.push(
|
|
1410
|
-
aiEventClient.on(
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
return
|
|
1422
|
-
}
|
|
1300
|
+
aiEventClient.on('text:started', (e) => {
|
|
1301
|
+
const streamId = e.payload.streamId
|
|
1302
|
+
const model = e.payload.model
|
|
1303
|
+
const provider = e.payload.provider
|
|
1304
|
+
const clientId = e.payload.clientId
|
|
1305
|
+
|
|
1306
|
+
if (clientId && state.conversations[clientId]) {
|
|
1307
|
+
streamToConversation.set(streamId, clientId)
|
|
1308
|
+
updateConversation(clientId, { status: 'active', ...e.payload })
|
|
1309
|
+
return
|
|
1310
|
+
}
|
|
1423
1311
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1312
|
+
const activeClient = Object.values(state.conversations).find(
|
|
1313
|
+
(c) => c.type === 'client' && c.status === 'active' && !c.model,
|
|
1314
|
+
)
|
|
1315
|
+
|
|
1316
|
+
if (activeClient) {
|
|
1317
|
+
streamToConversation.set(streamId, activeClient.id)
|
|
1318
|
+
updateConversation(activeClient.id, { ...e.payload })
|
|
1319
|
+
} else {
|
|
1320
|
+
const existingServerConv = Object.values(state.conversations).find(
|
|
1321
|
+
(c) => c.type === 'server' && c.model === model,
|
|
1426
1322
|
)
|
|
1427
1323
|
|
|
1428
|
-
if (
|
|
1429
|
-
streamToConversation.set(streamId,
|
|
1430
|
-
updateConversation(
|
|
1324
|
+
if (existingServerConv) {
|
|
1325
|
+
streamToConversation.set(streamId, existingServerConv.id)
|
|
1326
|
+
updateConversation(existingServerConv.id, {
|
|
1327
|
+
status: 'active',
|
|
1328
|
+
...e.payload,
|
|
1329
|
+
})
|
|
1431
1330
|
} else {
|
|
1432
|
-
const
|
|
1433
|
-
|
|
1434
|
-
)
|
|
1435
|
-
|
|
1436
|
-
if (existingServerConv) {
|
|
1437
|
-
streamToConversation.set(streamId, existingServerConv.id)
|
|
1438
|
-
updateConversation(existingServerConv.id, {
|
|
1439
|
-
status: 'active',
|
|
1440
|
-
...e.payload,
|
|
1441
|
-
})
|
|
1442
|
-
} else {
|
|
1443
|
-
const serverId = `server-${model}`
|
|
1444
|
-
getOrCreateConversation(serverId, 'server', `${model} Server`)
|
|
1445
|
-
streamToConversation.set(streamId, serverId)
|
|
1446
|
-
updateConversation(serverId, { ...e.payload })
|
|
1447
|
-
}
|
|
1331
|
+
const serverId = `server-${model}`
|
|
1332
|
+
getOrCreateConversation(serverId, 'server', `${model} Server`)
|
|
1333
|
+
streamToConversation.set(streamId, serverId)
|
|
1334
|
+
updateConversation(serverId, { ...e.payload })
|
|
1448
1335
|
}
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
),
|
|
1336
|
+
}
|
|
1337
|
+
}),
|
|
1452
1338
|
)
|
|
1453
1339
|
|
|
1454
1340
|
cleanupFns.push(
|
|
1455
|
-
aiEventClient.on(
|
|
1456
|
-
|
|
1457
|
-
(e) => {
|
|
1458
|
-
const { requestId, usage } = e.payload
|
|
1459
|
-
|
|
1460
|
-
const conversationId = requestToConversation.get(requestId)
|
|
1461
|
-
if (conversationId && state.conversations[conversationId]) {
|
|
1462
|
-
const updates: Partial<Conversation> = {
|
|
1463
|
-
status: 'completed',
|
|
1464
|
-
completedAt: e.payload.timestamp,
|
|
1465
|
-
}
|
|
1466
|
-
if (usage) {
|
|
1467
|
-
updates.usage = usage
|
|
1468
|
-
}
|
|
1469
|
-
updateConversation(conversationId, updates)
|
|
1470
|
-
if (usage) {
|
|
1471
|
-
updateMessageUsage(conversationId, e.payload.messageId, usage)
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
},
|
|
1475
|
-
{ withEventTarget: false },
|
|
1476
|
-
),
|
|
1477
|
-
)
|
|
1341
|
+
aiEventClient.on('text:completed', (e) => {
|
|
1342
|
+
const { requestId, usage } = e.payload
|
|
1478
1343
|
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
const conversationId = requestToConversation.get(requestId)
|
|
1486
|
-
if (conversationId && state.conversations[conversationId]) {
|
|
1487
|
-
updateConversation(conversationId, {
|
|
1488
|
-
iterationCount: iterationNumber,
|
|
1489
|
-
})
|
|
1344
|
+
const conversationId = requestToConversation.get(requestId)
|
|
1345
|
+
if (conversationId && state.conversations[conversationId]) {
|
|
1346
|
+
const updates: Partial<Conversation> = {
|
|
1347
|
+
status: 'completed',
|
|
1348
|
+
completedAt: e.payload.timestamp,
|
|
1490
1349
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
),
|
|
1494
|
-
)
|
|
1495
|
-
|
|
1496
|
-
cleanupFns.push(
|
|
1497
|
-
aiEventClient.on(
|
|
1498
|
-
'usage:tokens',
|
|
1499
|
-
(e) => {
|
|
1500
|
-
const { requestId, usage, messageId } = e.payload
|
|
1501
|
-
|
|
1502
|
-
const conversationId = requestToConversation.get(requestId)
|
|
1503
|
-
if (conversationId && state.conversations[conversationId]) {
|
|
1504
|
-
updateConversation(conversationId, { usage })
|
|
1505
|
-
updateMessageUsage(conversationId, messageId, usage)
|
|
1350
|
+
if (usage) {
|
|
1351
|
+
updates.usage = usage
|
|
1506
1352
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1353
|
+
updateConversation(conversationId, updates)
|
|
1354
|
+
if (usage) {
|
|
1355
|
+
updateMessageUsage(conversationId, e.payload.messageId, usage)
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}),
|
|
1510
1359
|
)
|
|
1511
1360
|
|
|
1512
|
-
// ============= Tool Call Completed (with duration) =============
|
|
1513
|
-
|
|
1514
1361
|
cleanupFns.push(
|
|
1515
|
-
aiEventClient.on(
|
|
1516
|
-
|
|
1517
|
-
(e) => {
|
|
1518
|
-
const {
|
|
1519
|
-
streamId,
|
|
1520
|
-
toolCallId,
|
|
1521
|
-
toolName,
|
|
1522
|
-
result,
|
|
1523
|
-
duration,
|
|
1524
|
-
messageId,
|
|
1525
|
-
timestamp,
|
|
1526
|
-
} = e.payload
|
|
1527
|
-
|
|
1528
|
-
const conversationId = streamToConversation.get(streamId)
|
|
1529
|
-
if (!conversationId || !state.conversations[conversationId]) return
|
|
1530
|
-
|
|
1531
|
-
const conv = state.conversations[conversationId]
|
|
1532
|
-
|
|
1533
|
-
// Add a tool_result chunk to show the result in the chunks view
|
|
1534
|
-
const chunk: Chunk = {
|
|
1535
|
-
id: `chunk-tool-result-${toolCallId}-${Date.now()}`,
|
|
1536
|
-
type: 'tool_result',
|
|
1537
|
-
messageId: messageId,
|
|
1538
|
-
toolCallId,
|
|
1539
|
-
toolName,
|
|
1540
|
-
result,
|
|
1541
|
-
duration,
|
|
1542
|
-
timestamp,
|
|
1543
|
-
chunkCount: 1,
|
|
1544
|
-
}
|
|
1362
|
+
aiEventClient.on('text:iteration', (e) => {
|
|
1363
|
+
const { requestId, iterationNumber } = e.payload
|
|
1545
1364
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
// If message not found, add to last assistant message
|
|
1555
|
-
for (let i = conv.messages.length - 1; i >= 0; i--) {
|
|
1556
|
-
if (conv.messages[i]?.role === 'assistant') {
|
|
1557
|
-
queueMessageChunk(conversationId, i, chunk)
|
|
1558
|
-
break
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
} else {
|
|
1563
|
-
addChunk(conversationId, chunk)
|
|
1564
|
-
}
|
|
1365
|
+
const conversationId = requestToConversation.get(requestId)
|
|
1366
|
+
if (conversationId && state.conversations[conversationId]) {
|
|
1367
|
+
updateConversation(conversationId, {
|
|
1368
|
+
iterationCount: iterationNumber,
|
|
1369
|
+
})
|
|
1370
|
+
}
|
|
1371
|
+
}),
|
|
1372
|
+
)
|
|
1565
1373
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
if (!message?.toolCalls) continue
|
|
1374
|
+
cleanupFns.push(
|
|
1375
|
+
aiEventClient.on('usage:tokens', (e) => {
|
|
1376
|
+
const { requestId, usage, messageId } = e.payload
|
|
1570
1377
|
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
result,
|
|
1578
|
-
})
|
|
1579
|
-
return
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
},
|
|
1583
|
-
{ withEventTarget: false },
|
|
1584
|
-
),
|
|
1378
|
+
const conversationId = requestToConversation.get(requestId)
|
|
1379
|
+
if (conversationId && state.conversations[conversationId]) {
|
|
1380
|
+
updateConversation(conversationId, { usage })
|
|
1381
|
+
updateMessageUsage(conversationId, messageId, usage)
|
|
1382
|
+
}
|
|
1383
|
+
}),
|
|
1585
1384
|
)
|
|
1586
1385
|
|
|
1587
|
-
// =============
|
|
1386
|
+
// ============= Tool Call Completed (with duration) =============
|
|
1588
1387
|
|
|
1589
1388
|
cleanupFns.push(
|
|
1590
|
-
aiEventClient.on(
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
if (activeClients.length > 0 && activeClients[0]) {
|
|
1605
|
-
conversationId = activeClients[0].id
|
|
1606
|
-
} else {
|
|
1607
|
-
// Create a new conversation for embeddings
|
|
1608
|
-
conversationId = `embedding-${requestId}`
|
|
1609
|
-
getOrCreateConversation(
|
|
1610
|
-
conversationId,
|
|
1611
|
-
'server',
|
|
1612
|
-
`Embedding (${model})`,
|
|
1613
|
-
)
|
|
1614
|
-
updateConversation(conversationId, { model })
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1389
|
+
aiEventClient.on('tool:call-completed', (e) => {
|
|
1390
|
+
const {
|
|
1391
|
+
streamId,
|
|
1392
|
+
toolCallId,
|
|
1393
|
+
toolName,
|
|
1394
|
+
result,
|
|
1395
|
+
duration,
|
|
1396
|
+
messageId,
|
|
1397
|
+
timestamp,
|
|
1398
|
+
} = e.payload
|
|
1399
|
+
|
|
1400
|
+
const conversationId = streamToConversation.get(streamId)
|
|
1401
|
+
if (!conversationId || !state.conversations[conversationId]) return
|
|
1617
1402
|
|
|
1618
|
-
|
|
1403
|
+
const conv = state.conversations[conversationId]
|
|
1619
1404
|
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1405
|
+
// Add a tool_result chunk to show the result in the chunks view
|
|
1406
|
+
const chunk: Chunk = {
|
|
1407
|
+
id: `chunk-tool-result-${toolCallId}-${Date.now()}`,
|
|
1408
|
+
type: 'tool_result',
|
|
1409
|
+
messageId: messageId,
|
|
1410
|
+
toolCallId,
|
|
1411
|
+
toolName,
|
|
1412
|
+
result,
|
|
1413
|
+
duration,
|
|
1414
|
+
timestamp,
|
|
1415
|
+
chunkCount: 1,
|
|
1416
|
+
}
|
|
1628
1417
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1418
|
+
// Add chunk to message if it's a client conversation, otherwise to conversation
|
|
1419
|
+
if (conv.type === 'client' && messageId) {
|
|
1420
|
+
const messageIndex = conv.messages.findIndex(
|
|
1421
|
+
(m) => m.id === messageId,
|
|
1422
|
+
)
|
|
1423
|
+
if (messageIndex !== -1) {
|
|
1424
|
+
queueMessageChunk(conversationId, messageIndex, chunk)
|
|
1425
|
+
} else {
|
|
1426
|
+
// If message not found, add to last assistant message
|
|
1427
|
+
for (let i = conv.messages.length - 1; i >= 0; i--) {
|
|
1428
|
+
if (conv.messages[i]?.role === 'assistant') {
|
|
1429
|
+
queueMessageChunk(conversationId, i, chunk)
|
|
1430
|
+
break
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1637
1433
|
}
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
)
|
|
1642
|
-
|
|
1643
|
-
cleanupFns.push(
|
|
1644
|
-
aiEventClient.on(
|
|
1645
|
-
'embedding:completed',
|
|
1646
|
-
(e) => {
|
|
1647
|
-
const { requestId, duration } = e.payload
|
|
1648
|
-
|
|
1649
|
-
const conversationId = requestToConversation.get(requestId)
|
|
1650
|
-
if (!conversationId || !state.conversations[conversationId]) return
|
|
1434
|
+
} else {
|
|
1435
|
+
addChunk(conversationId, chunk)
|
|
1436
|
+
}
|
|
1651
1437
|
|
|
1652
|
-
|
|
1653
|
-
|
|
1438
|
+
// Update the tool call with duration
|
|
1439
|
+
for (let i = conv.messages.length - 1; i >= 0; i--) {
|
|
1440
|
+
const message = conv.messages[i]
|
|
1441
|
+
if (!message?.toolCalls) continue
|
|
1654
1442
|
|
|
1655
|
-
const
|
|
1656
|
-
(
|
|
1443
|
+
const toolCallIndex = message.toolCalls.findIndex(
|
|
1444
|
+
(t) => t.id === toolCallId,
|
|
1657
1445
|
)
|
|
1658
|
-
if (
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
produce((op: EmbeddingOperation) => {
|
|
1665
|
-
op.duration = duration
|
|
1666
|
-
op.status = 'completed'
|
|
1667
|
-
}),
|
|
1668
|
-
)
|
|
1446
|
+
if (toolCallIndex >= 0) {
|
|
1447
|
+
updateToolCall(conversationId, i, toolCallIndex, {
|
|
1448
|
+
duration,
|
|
1449
|
+
result,
|
|
1450
|
+
})
|
|
1451
|
+
return
|
|
1669
1452
|
}
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
|
-
),
|
|
1453
|
+
}
|
|
1454
|
+
}),
|
|
1673
1455
|
)
|
|
1674
1456
|
|
|
1675
1457
|
// ============= Summarize Events =============
|
|
1676
1458
|
|
|
1677
1459
|
cleanupFns.push(
|
|
1678
|
-
aiEventClient.on(
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
//
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
`Summarize (${model})`,
|
|
1701
|
-
)
|
|
1702
|
-
updateConversation(conversationId, { model })
|
|
1703
|
-
}
|
|
1460
|
+
aiEventClient.on('summarize:started', (e) => {
|
|
1461
|
+
const { requestId, model, inputLength, timestamp, clientId } = e.payload
|
|
1462
|
+
|
|
1463
|
+
// Try to find an active conversation to attach to, or create a new one
|
|
1464
|
+
let conversationId = clientId
|
|
1465
|
+
if (!conversationId || !state.conversations[conversationId]) {
|
|
1466
|
+
// Find most recent active client conversation
|
|
1467
|
+
const activeClients = Object.values(state.conversations)
|
|
1468
|
+
.filter((c) => c.type === 'client' && c.status === 'active')
|
|
1469
|
+
.sort((a, b) => b.startedAt - a.startedAt)
|
|
1470
|
+
|
|
1471
|
+
if (activeClients.length > 0 && activeClients[0]) {
|
|
1472
|
+
conversationId = activeClients[0].id
|
|
1473
|
+
} else {
|
|
1474
|
+
// Create a new conversation for summaries
|
|
1475
|
+
conversationId = `summarize-${requestId}`
|
|
1476
|
+
getOrCreateConversation(
|
|
1477
|
+
conversationId,
|
|
1478
|
+
'server',
|
|
1479
|
+
`Summarize (${model})`,
|
|
1480
|
+
)
|
|
1481
|
+
updateConversation(conversationId, { model })
|
|
1704
1482
|
}
|
|
1483
|
+
}
|
|
1705
1484
|
|
|
1706
|
-
|
|
1485
|
+
requestToConversation.set(requestId, conversationId)
|
|
1707
1486
|
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1487
|
+
const summarizeOp: SummarizeOperation = {
|
|
1488
|
+
id: requestId,
|
|
1489
|
+
model,
|
|
1490
|
+
inputLength,
|
|
1491
|
+
timestamp,
|
|
1492
|
+
status: 'started',
|
|
1493
|
+
}
|
|
1715
1494
|
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
{ withEventTarget: false },
|
|
1727
|
-
),
|
|
1495
|
+
const conv = state.conversations[conversationId]
|
|
1496
|
+
if (conv) {
|
|
1497
|
+
const summaries = conv.summaries || []
|
|
1498
|
+
setState('conversations', conversationId, 'summaries', [
|
|
1499
|
+
...summaries,
|
|
1500
|
+
summarizeOp,
|
|
1501
|
+
])
|
|
1502
|
+
setState('conversations', conversationId, 'hasSummarize', true)
|
|
1503
|
+
}
|
|
1504
|
+
}),
|
|
1728
1505
|
)
|
|
1729
1506
|
|
|
1730
1507
|
cleanupFns.push(
|
|
1731
|
-
aiEventClient.on(
|
|
1732
|
-
|
|
1733
|
-
(e) => {
|
|
1734
|
-
const { requestId, outputLength, duration } = e.payload
|
|
1508
|
+
aiEventClient.on('summarize:completed', (e) => {
|
|
1509
|
+
const { requestId, outputLength, duration } = e.payload
|
|
1735
1510
|
|
|
1736
|
-
|
|
1737
|
-
|
|
1511
|
+
const conversationId = requestToConversation.get(requestId)
|
|
1512
|
+
if (!conversationId || !state.conversations[conversationId]) return
|
|
1738
1513
|
|
|
1739
|
-
|
|
1740
|
-
|
|
1514
|
+
const conv = state.conversations[conversationId]
|
|
1515
|
+
if (!conv.summaries) return
|
|
1741
1516
|
|
|
1742
|
-
|
|
1743
|
-
|
|
1517
|
+
const summaryIndex = conv.summaries.findIndex(
|
|
1518
|
+
(op) => op.id === requestId,
|
|
1519
|
+
)
|
|
1520
|
+
if (summaryIndex >= 0) {
|
|
1521
|
+
setState(
|
|
1522
|
+
'conversations',
|
|
1523
|
+
conversationId,
|
|
1524
|
+
'summaries',
|
|
1525
|
+
summaryIndex,
|
|
1526
|
+
produce((op: SummarizeOperation) => {
|
|
1527
|
+
op.duration = duration
|
|
1528
|
+
op.outputLength = outputLength
|
|
1529
|
+
op.status = 'completed'
|
|
1530
|
+
}),
|
|
1744
1531
|
)
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
'conversations',
|
|
1748
|
-
conversationId,
|
|
1749
|
-
'summaries',
|
|
1750
|
-
summaryIndex,
|
|
1751
|
-
produce((op: SummarizeOperation) => {
|
|
1752
|
-
op.duration = duration
|
|
1753
|
-
op.outputLength = outputLength
|
|
1754
|
-
op.status = 'completed'
|
|
1755
|
-
}),
|
|
1756
|
-
)
|
|
1757
|
-
}
|
|
1758
|
-
},
|
|
1759
|
-
{ withEventTarget: false },
|
|
1760
|
-
),
|
|
1532
|
+
}
|
|
1533
|
+
}),
|
|
1761
1534
|
)
|
|
1762
1535
|
|
|
1763
1536
|
// Cleanup all listeners on unmount
|