graphlit-client 1.0.20250612002 → 1.0.20250612003
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/client.js
CHANGED
@@ -1566,12 +1566,7 @@ class Graphlit {
|
|
1566
1566
|
? (await this.getSpecification(specification.id))
|
1567
1567
|
.specification
|
1568
1568
|
: undefined;
|
1569
|
-
//
|
1570
|
-
if (fullSpec && !this.supportsStreaming(fullSpec)) {
|
1571
|
-
throw new Error("Streaming is not supported for this specification. " +
|
1572
|
-
"Use promptAgent() instead or configure a streaming client.");
|
1573
|
-
}
|
1574
|
-
// Ensure conversation
|
1569
|
+
// Ensure conversation exists first (before streaming check)
|
1575
1570
|
let actualConversationId = conversationId;
|
1576
1571
|
if (!actualConversationId) {
|
1577
1572
|
const createResponse = await this.createConversation({
|
@@ -1586,6 +1581,47 @@ class Graphlit {
|
|
1586
1581
|
throw new Error("Failed to create conversation");
|
1587
1582
|
}
|
1588
1583
|
}
|
1584
|
+
// Check streaming support - fallback to promptAgent if not supported
|
1585
|
+
if (fullSpec && !this.supportsStreaming(fullSpec)) {
|
1586
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
1587
|
+
console.log("\n⚠️ [streamAgent] Streaming not supported, falling back to promptAgent with same conversation");
|
1588
|
+
}
|
1589
|
+
// Fallback to promptAgent using the same conversation and parameters
|
1590
|
+
const promptResult = await this.promptAgent(prompt, actualConversationId, // Preserve conversation
|
1591
|
+
specification, tools, toolHandlers, {
|
1592
|
+
maxToolRounds: maxRounds,
|
1593
|
+
}, mimeType, data, contentFilter, augmentedFilter, correlationId);
|
1594
|
+
// Convert promptAgent result to streaming events
|
1595
|
+
onEvent({
|
1596
|
+
type: "conversation_started",
|
1597
|
+
conversationId: actualConversationId,
|
1598
|
+
timestamp: new Date(),
|
1599
|
+
});
|
1600
|
+
// Emit the final message as a single update (simulating streaming)
|
1601
|
+
onEvent({
|
1602
|
+
type: "message_update",
|
1603
|
+
message: {
|
1604
|
+
__typename: "ConversationMessage",
|
1605
|
+
message: promptResult.message,
|
1606
|
+
role: Types.ConversationRoleTypes.Assistant,
|
1607
|
+
timestamp: new Date().toISOString(),
|
1608
|
+
toolCalls: [],
|
1609
|
+
},
|
1610
|
+
isStreaming: false,
|
1611
|
+
});
|
1612
|
+
// Emit completion event
|
1613
|
+
onEvent({
|
1614
|
+
type: "conversation_completed",
|
1615
|
+
message: {
|
1616
|
+
__typename: "ConversationMessage",
|
1617
|
+
message: promptResult.message,
|
1618
|
+
role: Types.ConversationRoleTypes.Assistant,
|
1619
|
+
timestamp: new Date().toISOString(),
|
1620
|
+
toolCalls: [],
|
1621
|
+
},
|
1622
|
+
});
|
1623
|
+
return; // Exit early after successful fallback
|
1624
|
+
}
|
1589
1625
|
// Create UI event adapter
|
1590
1626
|
uiAdapter = new UIEventAdapter(onEvent, actualConversationId, {
|
1591
1627
|
smoothingEnabled: options?.smoothingEnabled ?? true,
|
@@ -636,6 +636,7 @@ export const DescribeEncodedImage = gql `
|
|
636
636
|
posts
|
637
637
|
chapters
|
638
638
|
questions
|
639
|
+
quotes
|
639
640
|
video {
|
640
641
|
width
|
641
642
|
height
|
@@ -770,6 +771,7 @@ export const DescribeImage = gql `
|
|
770
771
|
posts
|
771
772
|
chapters
|
772
773
|
questions
|
774
|
+
quotes
|
773
775
|
video {
|
774
776
|
width
|
775
777
|
height
|
@@ -1625,6 +1627,7 @@ export const PublishContents = gql `
|
|
1625
1627
|
posts
|
1626
1628
|
chapters
|
1627
1629
|
questions
|
1630
|
+
quotes
|
1628
1631
|
video {
|
1629
1632
|
width
|
1630
1633
|
height
|
@@ -1749,6 +1752,7 @@ export const PublishText = gql `
|
|
1749
1752
|
posts
|
1750
1753
|
chapters
|
1751
1754
|
questions
|
1755
|
+
quotes
|
1752
1756
|
video {
|
1753
1757
|
width
|
1754
1758
|
height
|
@@ -1880,6 +1884,7 @@ export const QueryContents = gql `
|
|
1880
1884
|
posts
|
1881
1885
|
chapters
|
1882
1886
|
questions
|
1887
|
+
quotes
|
1883
1888
|
video {
|
1884
1889
|
width
|
1885
1890
|
height
|
@@ -2316,6 +2321,7 @@ export const AskGraphlit = gql `
|
|
2316
2321
|
posts
|
2317
2322
|
chapters
|
2318
2323
|
questions
|
2324
|
+
quotes
|
2319
2325
|
video {
|
2320
2326
|
width
|
2321
2327
|
height
|
@@ -2485,6 +2491,7 @@ export const CompleteConversation = gql `
|
|
2485
2491
|
posts
|
2486
2492
|
chapters
|
2487
2493
|
questions
|
2494
|
+
quotes
|
2488
2495
|
video {
|
2489
2496
|
width
|
2490
2497
|
height
|
@@ -2662,6 +2669,7 @@ export const CompleteConversation = gql `
|
|
2662
2669
|
posts
|
2663
2670
|
chapters
|
2664
2671
|
questions
|
2672
|
+
quotes
|
2665
2673
|
video {
|
2666
2674
|
width
|
2667
2675
|
height
|
@@ -2801,6 +2809,7 @@ export const ContinueConversation = gql `
|
|
2801
2809
|
posts
|
2802
2810
|
chapters
|
2803
2811
|
questions
|
2812
|
+
quotes
|
2804
2813
|
video {
|
2805
2814
|
width
|
2806
2815
|
height
|
@@ -2978,6 +2987,7 @@ export const ContinueConversation = gql `
|
|
2978
2987
|
posts
|
2979
2988
|
chapters
|
2980
2989
|
questions
|
2990
|
+
quotes
|
2981
2991
|
video {
|
2982
2992
|
width
|
2983
2993
|
height
|
@@ -3165,6 +3175,7 @@ export const FormatConversation = gql `
|
|
3165
3175
|
posts
|
3166
3176
|
chapters
|
3167
3177
|
questions
|
3178
|
+
quotes
|
3168
3179
|
video {
|
3169
3180
|
width
|
3170
3181
|
height
|
@@ -3342,6 +3353,7 @@ export const FormatConversation = gql `
|
|
3342
3353
|
posts
|
3343
3354
|
chapters
|
3344
3355
|
questions
|
3356
|
+
quotes
|
3345
3357
|
video {
|
3346
3358
|
width
|
3347
3359
|
height
|
@@ -3484,6 +3496,7 @@ export const GetConversation = gql `
|
|
3484
3496
|
posts
|
3485
3497
|
chapters
|
3486
3498
|
questions
|
3499
|
+
quotes
|
3487
3500
|
video {
|
3488
3501
|
width
|
3489
3502
|
height
|
@@ -3799,6 +3812,7 @@ export const Prompt = gql `
|
|
3799
3812
|
posts
|
3800
3813
|
chapters
|
3801
3814
|
questions
|
3815
|
+
quotes
|
3802
3816
|
video {
|
3803
3817
|
width
|
3804
3818
|
height
|
@@ -3944,6 +3958,7 @@ export const PromptConversation = gql `
|
|
3944
3958
|
posts
|
3945
3959
|
chapters
|
3946
3960
|
questions
|
3961
|
+
quotes
|
3947
3962
|
video {
|
3948
3963
|
width
|
3949
3964
|
height
|
@@ -4121,6 +4136,7 @@ export const PromptConversation = gql `
|
|
4121
4136
|
posts
|
4122
4137
|
chapters
|
4123
4138
|
questions
|
4139
|
+
quotes
|
4124
4140
|
video {
|
4125
4141
|
width
|
4126
4142
|
height
|
@@ -4256,6 +4272,7 @@ export const PublishConversation = gql `
|
|
4256
4272
|
posts
|
4257
4273
|
chapters
|
4258
4274
|
questions
|
4275
|
+
quotes
|
4259
4276
|
video {
|
4260
4277
|
width
|
4261
4278
|
height
|
@@ -4387,6 +4404,7 @@ export const QueryConversations = gql `
|
|
4387
4404
|
posts
|
4388
4405
|
chapters
|
4389
4406
|
questions
|
4407
|
+
quotes
|
4390
4408
|
video {
|
4391
4409
|
width
|
4392
4410
|
height
|
@@ -4728,6 +4746,7 @@ export const ReviseContent = gql `
|
|
4728
4746
|
posts
|
4729
4747
|
chapters
|
4730
4748
|
questions
|
4749
|
+
quotes
|
4731
4750
|
video {
|
4732
4751
|
width
|
4733
4752
|
height
|
@@ -4870,6 +4889,7 @@ export const ReviseEncodedImage = gql `
|
|
4870
4889
|
posts
|
4871
4890
|
chapters
|
4872
4891
|
questions
|
4892
|
+
quotes
|
4873
4893
|
video {
|
4874
4894
|
width
|
4875
4895
|
height
|
@@ -5011,6 +5031,7 @@ export const ReviseImage = gql `
|
|
5011
5031
|
posts
|
5012
5032
|
chapters
|
5013
5033
|
questions
|
5034
|
+
quotes
|
5014
5035
|
video {
|
5015
5036
|
width
|
5016
5037
|
height
|
@@ -5152,6 +5173,7 @@ export const ReviseText = gql `
|
|
5152
5173
|
posts
|
5153
5174
|
chapters
|
5154
5175
|
questions
|
5176
|
+
quotes
|
5155
5177
|
video {
|
5156
5178
|
width
|
5157
5179
|
height
|
@@ -8187,6 +8209,7 @@ export const PromptSpecifications = gql `
|
|
8187
8209
|
posts
|
8188
8210
|
chapters
|
8189
8211
|
questions
|
8212
|
+
quotes
|
8190
8213
|
video {
|
8191
8214
|
width
|
8192
8215
|
height
|
@@ -1645,6 +1645,8 @@ export type Content = {
|
|
1645
1645
|
posts?: Maybe<Array<Scalars['String']['output']>>;
|
1646
1646
|
/** The followup questions which can be asked about the content. */
|
1647
1647
|
questions?: Maybe<Array<Scalars['String']['output']>>;
|
1648
|
+
/** Quotes extracted from the content. */
|
1649
|
+
quotes?: Maybe<Array<Scalars['String']['output']>>;
|
1648
1650
|
/** The relevance score of the content. */
|
1649
1651
|
relevance?: Maybe<Scalars['Float']['output']>;
|
1650
1652
|
/** The renditions generated from this content. */
|
@@ -2206,6 +2208,8 @@ export type ContentUpdateInput = {
|
|
2206
2208
|
posts?: InputMaybe<Array<Scalars['String']['input']>>;
|
2207
2209
|
/** The followup questions which can be asked about the content. */
|
2208
2210
|
questions?: InputMaybe<Array<Scalars['String']['input']>>;
|
2211
|
+
/** Quotes extracted from the content. */
|
2212
|
+
quotes?: InputMaybe<Array<Scalars['String']['input']>>;
|
2209
2213
|
/** The content shape metadata. */
|
2210
2214
|
shape?: InputMaybe<ShapeMetadataInput>;
|
2211
2215
|
/** The content summary. */
|
@@ -2529,6 +2533,8 @@ export type ConversationUpdateInput = {
|
|
2529
2533
|
filter?: InputMaybe<ContentCriteriaInput>;
|
2530
2534
|
/** The ID of the conversation to update. */
|
2531
2535
|
id: Scalars['ID']['input'];
|
2536
|
+
/** The conversation messages. */
|
2537
|
+
messages?: InputMaybe<Array<ConversationMessageInput>>;
|
2532
2538
|
/** The name of the conversation. */
|
2533
2539
|
name?: InputMaybe<Scalars['String']['input']>;
|
2534
2540
|
/** The LLM specification used by this conversation, optional. */
|
@@ -13039,6 +13045,8 @@ export declare enum SummarizationTypes {
|
|
13039
13045
|
Posts = "POSTS",
|
13040
13046
|
/** Questions */
|
13041
13047
|
Questions = "QUESTIONS",
|
13048
|
+
/** Quote */
|
13049
|
+
Quotes = "QUOTES",
|
13042
13050
|
/** Summary */
|
13043
13051
|
Summary = "SUMMARY"
|
13044
13052
|
}
|
@@ -14739,6 +14747,7 @@ export type DescribeEncodedImageMutation = {
|
|
14739
14747
|
posts?: Array<string> | null;
|
14740
14748
|
chapters?: Array<string> | null;
|
14741
14749
|
questions?: Array<string> | null;
|
14750
|
+
quotes?: Array<string> | null;
|
14742
14751
|
video?: {
|
14743
14752
|
__typename?: 'VideoMetadata';
|
14744
14753
|
width?: number | null;
|
@@ -14881,6 +14890,7 @@ export type DescribeImageMutation = {
|
|
14881
14890
|
posts?: Array<string> | null;
|
14882
14891
|
chapters?: Array<string> | null;
|
14883
14892
|
questions?: Array<string> | null;
|
14893
|
+
quotes?: Array<string> | null;
|
14884
14894
|
video?: {
|
14885
14895
|
__typename?: 'VideoMetadata';
|
14886
14896
|
width?: number | null;
|
@@ -15821,6 +15831,7 @@ export type PublishContentsMutation = {
|
|
15821
15831
|
posts?: Array<string> | null;
|
15822
15832
|
chapters?: Array<string> | null;
|
15823
15833
|
questions?: Array<string> | null;
|
15834
|
+
quotes?: Array<string> | null;
|
15824
15835
|
video?: {
|
15825
15836
|
__typename?: 'VideoMetadata';
|
15826
15837
|
width?: number | null;
|
@@ -15953,6 +15964,7 @@ export type PublishTextMutation = {
|
|
15953
15964
|
posts?: Array<string> | null;
|
15954
15965
|
chapters?: Array<string> | null;
|
15955
15966
|
questions?: Array<string> | null;
|
15967
|
+
quotes?: Array<string> | null;
|
15956
15968
|
video?: {
|
15957
15969
|
__typename?: 'VideoMetadata';
|
15958
15970
|
width?: number | null;
|
@@ -16081,6 +16093,7 @@ export type QueryContentsQuery = {
|
|
16081
16093
|
posts?: Array<string> | null;
|
16082
16094
|
chapters?: Array<string> | null;
|
16083
16095
|
questions?: Array<string> | null;
|
16096
|
+
quotes?: Array<string> | null;
|
16084
16097
|
error?: string | null;
|
16085
16098
|
owner: {
|
16086
16099
|
__typename?: 'Owner';
|
@@ -16623,6 +16636,7 @@ export type AskGraphlitMutation = {
|
|
16623
16636
|
posts?: Array<string> | null;
|
16624
16637
|
chapters?: Array<string> | null;
|
16625
16638
|
questions?: Array<string> | null;
|
16639
|
+
quotes?: Array<string> | null;
|
16626
16640
|
video?: {
|
16627
16641
|
__typename?: 'VideoMetadata';
|
16628
16642
|
width?: number | null;
|
@@ -16811,6 +16825,7 @@ export type CompleteConversationMutation = {
|
|
16811
16825
|
posts?: Array<string> | null;
|
16812
16826
|
chapters?: Array<string> | null;
|
16813
16827
|
questions?: Array<string> | null;
|
16828
|
+
quotes?: Array<string> | null;
|
16814
16829
|
video?: {
|
16815
16830
|
__typename?: 'VideoMetadata';
|
16816
16831
|
width?: number | null;
|
@@ -17003,6 +17018,7 @@ export type CompleteConversationMutation = {
|
|
17003
17018
|
posts?: Array<string> | null;
|
17004
17019
|
chapters?: Array<string> | null;
|
17005
17020
|
questions?: Array<string> | null;
|
17021
|
+
quotes?: Array<string> | null;
|
17006
17022
|
video?: {
|
17007
17023
|
__typename?: 'VideoMetadata';
|
17008
17024
|
width?: number | null;
|
@@ -17153,6 +17169,7 @@ export type ContinueConversationMutation = {
|
|
17153
17169
|
posts?: Array<string> | null;
|
17154
17170
|
chapters?: Array<string> | null;
|
17155
17171
|
questions?: Array<string> | null;
|
17172
|
+
quotes?: Array<string> | null;
|
17156
17173
|
video?: {
|
17157
17174
|
__typename?: 'VideoMetadata';
|
17158
17175
|
width?: number | null;
|
@@ -17345,6 +17362,7 @@ export type ContinueConversationMutation = {
|
|
17345
17362
|
posts?: Array<string> | null;
|
17346
17363
|
chapters?: Array<string> | null;
|
17347
17364
|
questions?: Array<string> | null;
|
17365
|
+
quotes?: Array<string> | null;
|
17348
17366
|
video?: {
|
17349
17367
|
__typename?: 'VideoMetadata';
|
17350
17368
|
width?: number | null;
|
@@ -17559,6 +17577,7 @@ export type FormatConversationMutation = {
|
|
17559
17577
|
posts?: Array<string> | null;
|
17560
17578
|
chapters?: Array<string> | null;
|
17561
17579
|
questions?: Array<string> | null;
|
17580
|
+
quotes?: Array<string> | null;
|
17562
17581
|
video?: {
|
17563
17582
|
__typename?: 'VideoMetadata';
|
17564
17583
|
width?: number | null;
|
@@ -17751,6 +17770,7 @@ export type FormatConversationMutation = {
|
|
17751
17770
|
posts?: Array<string> | null;
|
17752
17771
|
chapters?: Array<string> | null;
|
17753
17772
|
questions?: Array<string> | null;
|
17773
|
+
quotes?: Array<string> | null;
|
17754
17774
|
video?: {
|
17755
17775
|
__typename?: 'VideoMetadata';
|
17756
17776
|
width?: number | null;
|
@@ -17906,6 +17926,7 @@ export type GetConversationQuery = {
|
|
17906
17926
|
posts?: Array<string> | null;
|
17907
17927
|
chapters?: Array<string> | null;
|
17908
17928
|
questions?: Array<string> | null;
|
17929
|
+
quotes?: Array<string> | null;
|
17909
17930
|
video?: {
|
17910
17931
|
__typename?: 'VideoMetadata';
|
17911
17932
|
width?: number | null;
|
@@ -18284,6 +18305,7 @@ export type PromptMutation = {
|
|
18284
18305
|
posts?: Array<string> | null;
|
18285
18306
|
chapters?: Array<string> | null;
|
18286
18307
|
questions?: Array<string> | null;
|
18308
|
+
quotes?: Array<string> | null;
|
18287
18309
|
video?: {
|
18288
18310
|
__typename?: 'VideoMetadata';
|
18289
18311
|
width?: number | null;
|
@@ -18439,6 +18461,7 @@ export type PromptConversationMutation = {
|
|
18439
18461
|
posts?: Array<string> | null;
|
18440
18462
|
chapters?: Array<string> | null;
|
18441
18463
|
questions?: Array<string> | null;
|
18464
|
+
quotes?: Array<string> | null;
|
18442
18465
|
video?: {
|
18443
18466
|
__typename?: 'VideoMetadata';
|
18444
18467
|
width?: number | null;
|
@@ -18631,6 +18654,7 @@ export type PromptConversationMutation = {
|
|
18631
18654
|
posts?: Array<string> | null;
|
18632
18655
|
chapters?: Array<string> | null;
|
18633
18656
|
questions?: Array<string> | null;
|
18657
|
+
quotes?: Array<string> | null;
|
18634
18658
|
video?: {
|
18635
18659
|
__typename?: 'VideoMetadata';
|
18636
18660
|
width?: number | null;
|
@@ -18757,6 +18781,7 @@ export type PublishConversationMutation = {
|
|
18757
18781
|
posts?: Array<string> | null;
|
18758
18782
|
chapters?: Array<string> | null;
|
18759
18783
|
questions?: Array<string> | null;
|
18784
|
+
quotes?: Array<string> | null;
|
18760
18785
|
video?: {
|
18761
18786
|
__typename?: 'VideoMetadata';
|
18762
18787
|
width?: number | null;
|
@@ -18919,6 +18944,7 @@ export type QueryConversationsQuery = {
|
|
18919
18944
|
posts?: Array<string> | null;
|
18920
18945
|
chapters?: Array<string> | null;
|
18921
18946
|
questions?: Array<string> | null;
|
18947
|
+
quotes?: Array<string> | null;
|
18922
18948
|
video?: {
|
18923
18949
|
__typename?: 'VideoMetadata';
|
18924
18950
|
width?: number | null;
|
@@ -19326,6 +19352,7 @@ export type ReviseContentMutation = {
|
|
19326
19352
|
posts?: Array<string> | null;
|
19327
19353
|
chapters?: Array<string> | null;
|
19328
19354
|
questions?: Array<string> | null;
|
19355
|
+
quotes?: Array<string> | null;
|
19329
19356
|
video?: {
|
19330
19357
|
__typename?: 'VideoMetadata';
|
19331
19358
|
width?: number | null;
|
@@ -19478,6 +19505,7 @@ export type ReviseEncodedImageMutation = {
|
|
19478
19505
|
posts?: Array<string> | null;
|
19479
19506
|
chapters?: Array<string> | null;
|
19480
19507
|
questions?: Array<string> | null;
|
19508
|
+
quotes?: Array<string> | null;
|
19481
19509
|
video?: {
|
19482
19510
|
__typename?: 'VideoMetadata';
|
19483
19511
|
width?: number | null;
|
@@ -19629,6 +19657,7 @@ export type ReviseImageMutation = {
|
|
19629
19657
|
posts?: Array<string> | null;
|
19630
19658
|
chapters?: Array<string> | null;
|
19631
19659
|
questions?: Array<string> | null;
|
19660
|
+
quotes?: Array<string> | null;
|
19632
19661
|
video?: {
|
19633
19662
|
__typename?: 'VideoMetadata';
|
19634
19663
|
width?: number | null;
|
@@ -19780,6 +19809,7 @@ export type ReviseTextMutation = {
|
|
19780
19809
|
posts?: Array<string> | null;
|
19781
19810
|
chapters?: Array<string> | null;
|
19782
19811
|
questions?: Array<string> | null;
|
19812
|
+
quotes?: Array<string> | null;
|
19783
19813
|
video?: {
|
19784
19814
|
__typename?: 'VideoMetadata';
|
19785
19815
|
width?: number | null;
|
@@ -23592,6 +23622,7 @@ export type PromptSpecificationsMutation = {
|
|
23592
23622
|
posts?: Array<string> | null;
|
23593
23623
|
chapters?: Array<string> | null;
|
23594
23624
|
questions?: Array<string> | null;
|
23625
|
+
quotes?: Array<string> | null;
|
23595
23626
|
video?: {
|
23596
23627
|
__typename?: 'VideoMetadata';
|
23597
23628
|
width?: number | null;
|
@@ -1866,6 +1866,8 @@ export var SummarizationTypes;
|
|
1866
1866
|
SummarizationTypes["Posts"] = "POSTS";
|
1867
1867
|
/** Questions */
|
1868
1868
|
SummarizationTypes["Questions"] = "QUESTIONS";
|
1869
|
+
/** Quote */
|
1870
|
+
SummarizationTypes["Quotes"] = "QUOTES";
|
1869
1871
|
/** Summary */
|
1870
1872
|
SummarizationTypes["Summary"] = "SUMMARY";
|
1871
1873
|
})(SummarizationTypes || (SummarizationTypes = {}));
|
@@ -21,9 +21,22 @@ onEvent, onComplete) {
|
|
21
21
|
// Performance metrics
|
22
22
|
const startTime = Date.now();
|
23
23
|
let firstTokenTime = 0;
|
24
|
+
let firstMeaningfulContentTime = 0;
|
24
25
|
let tokenCount = 0;
|
26
|
+
let toolArgumentTokens = 0;
|
25
27
|
let lastEventTime = 0;
|
26
28
|
const interTokenDelays = [];
|
29
|
+
// Tool calling metrics
|
30
|
+
const toolMetrics = {
|
31
|
+
totalTools: 0,
|
32
|
+
successfulTools: 0,
|
33
|
+
failedTools: 0,
|
34
|
+
toolTimes: [],
|
35
|
+
currentToolStart: 0,
|
36
|
+
roundStartTime: startTime,
|
37
|
+
rounds: [],
|
38
|
+
currentRound: 1
|
39
|
+
};
|
27
40
|
try {
|
28
41
|
const modelName = getModelName(specification);
|
29
42
|
if (!modelName) {
|
@@ -84,13 +97,20 @@ onEvent, onComplete) {
|
|
84
97
|
fullMessage += delta.content;
|
85
98
|
tokenCount++;
|
86
99
|
const currentTime = Date.now();
|
87
|
-
// Track TTFT
|
100
|
+
// Track TTFT (first token regardless of type)
|
88
101
|
if (firstTokenTime === 0) {
|
89
102
|
firstTokenTime = currentTime - startTime;
|
90
103
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
91
104
|
console.log(`\n⚡ [OpenAI] Time to First Token (TTFT): ${firstTokenTime}ms`);
|
92
105
|
}
|
93
106
|
}
|
107
|
+
// Track first meaningful content (excludes tool calls)
|
108
|
+
if (firstMeaningfulContentTime === 0 && delta.content.trim()) {
|
109
|
+
firstMeaningfulContentTime = currentTime - startTime;
|
110
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
111
|
+
console.log(`\n🎯 [OpenAI] Time to First Meaningful Content: ${firstMeaningfulContentTime}ms`);
|
112
|
+
}
|
113
|
+
}
|
94
114
|
// Track inter-token delays
|
95
115
|
if (lastEventTime > 0) {
|
96
116
|
const delay = currentTime - lastEventTime;
|
@@ -115,6 +135,22 @@ onEvent, onComplete) {
|
|
115
135
|
name: "",
|
116
136
|
arguments: "",
|
117
137
|
};
|
138
|
+
// Track tool metrics
|
139
|
+
toolMetrics.totalTools++;
|
140
|
+
toolMetrics.currentToolStart = Date.now();
|
141
|
+
toolMetrics.toolTimes.push({
|
142
|
+
name: toolCallDelta.function?.name || "unknown",
|
143
|
+
startTime: toolMetrics.currentToolStart,
|
144
|
+
argumentBuildTime: 0,
|
145
|
+
totalTime: 0
|
146
|
+
});
|
147
|
+
// Track TTFT for first tool if no content yet
|
148
|
+
if (firstTokenTime === 0) {
|
149
|
+
firstTokenTime = Date.now() - startTime;
|
150
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
151
|
+
console.log(`\n⚡ [OpenAI] Time to First Token (Tool Call): ${firstTokenTime}ms`);
|
152
|
+
}
|
153
|
+
}
|
118
154
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
119
155
|
console.log(`[OpenAI] Starting new tool call: ${toolCalls[index].id}`);
|
120
156
|
}
|
@@ -134,6 +170,8 @@ onEvent, onComplete) {
|
|
134
170
|
}
|
135
171
|
if (toolCallDelta.function?.arguments) {
|
136
172
|
toolCalls[index].arguments += toolCallDelta.function.arguments;
|
173
|
+
// Count tool argument tokens (rough estimate: ~4 chars per token)
|
174
|
+
toolArgumentTokens += Math.ceil(toolCallDelta.function.arguments.length / 4);
|
137
175
|
// Debug logging for partial JSON accumulation
|
138
176
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
139
177
|
console.log(`[OpenAI] Tool ${toolCalls[index].name} - Partial JSON chunk: "${toolCallDelta.function.arguments}"`);
|
@@ -148,20 +186,33 @@ onEvent, onComplete) {
|
|
148
186
|
}
|
149
187
|
}
|
150
188
|
}
|
151
|
-
// Emit complete events for tool calls
|
152
|
-
for (
|
189
|
+
// Emit complete events for tool calls and finalize metrics
|
190
|
+
for (let i = 0; i < toolCalls.length; i++) {
|
191
|
+
const toolCall = toolCalls[i];
|
192
|
+
const currentTime = Date.now();
|
193
|
+
// Update tool metrics
|
194
|
+
if (i < toolMetrics.toolTimes.length) {
|
195
|
+
const toolTime = toolMetrics.toolTimes[i];
|
196
|
+
toolTime.argumentBuildTime = currentTime - toolTime.startTime;
|
197
|
+
toolTime.totalTime = toolTime.argumentBuildTime; // For streaming, this is the same
|
198
|
+
toolTime.name = toolCall.name; // Update with final name
|
199
|
+
}
|
200
|
+
// Track tool success/failure
|
201
|
+
try {
|
202
|
+
JSON.parse(toolCall.arguments);
|
203
|
+
toolMetrics.successfulTools++;
|
204
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
205
|
+
console.log(`[OpenAI] ✅ Valid JSON for ${toolCall.name}`);
|
206
|
+
}
|
207
|
+
}
|
208
|
+
catch (e) {
|
209
|
+
toolMetrics.failedTools++;
|
210
|
+
console.error(`[OpenAI] ❌ Invalid JSON for ${toolCall.name}: ${e}`);
|
211
|
+
}
|
153
212
|
// Log the final JSON for debugging
|
154
213
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
155
214
|
console.log(`[OpenAI] Tool ${toolCall.name} complete with arguments (${toolCall.arguments.length} chars):`);
|
156
215
|
console.log(toolCall.arguments);
|
157
|
-
// Validate JSON
|
158
|
-
try {
|
159
|
-
JSON.parse(toolCall.arguments);
|
160
|
-
console.log(`[OpenAI] ✅ Valid JSON for ${toolCall.name}`);
|
161
|
-
}
|
162
|
-
catch (e) {
|
163
|
-
console.error(`[OpenAI] ❌ Invalid JSON for ${toolCall.name}: ${e}`);
|
164
|
-
}
|
165
216
|
}
|
166
217
|
onEvent({
|
167
218
|
type: "tool_call_complete",
|
@@ -176,22 +227,60 @@ onEvent, onComplete) {
|
|
176
227
|
if (process.env.DEBUG_GRAPHLIT_STREAMING && toolCalls.length > 0) {
|
177
228
|
console.log(`[OpenAI] Successfully processed ${toolCalls.length} tool calls`);
|
178
229
|
}
|
179
|
-
// Calculate final metrics
|
230
|
+
// Calculate final metrics including tool calling insights
|
180
231
|
const totalTime = Date.now() - startTime;
|
181
|
-
const
|
232
|
+
const totalTokens = tokenCount + toolArgumentTokens;
|
233
|
+
const tokensPerSecond = totalTokens > 0 ? totalTokens / (totalTime / 1000) : 0;
|
234
|
+
// Finalize round metrics
|
235
|
+
if (toolCalls.length > 0) {
|
236
|
+
const roundEndTime = Date.now();
|
237
|
+
const totalToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0);
|
238
|
+
const llmTime = totalTime - totalToolTime;
|
239
|
+
toolMetrics.rounds.push({
|
240
|
+
roundNumber: toolMetrics.currentRound,
|
241
|
+
llmTime: llmTime,
|
242
|
+
toolTime: totalToolTime,
|
243
|
+
toolCount: toolCalls.length
|
244
|
+
});
|
245
|
+
}
|
182
246
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
183
247
|
console.log("\n📊 [OpenAI] Performance Metrics:");
|
184
248
|
console.log(` ⏱️ Total Time: ${totalTime}ms`);
|
185
249
|
console.log(` ⚡ Time to First Token (TTFT): ${firstTokenTime}ms`);
|
186
|
-
|
250
|
+
if (firstMeaningfulContentTime > 0) {
|
251
|
+
console.log(` 🎯 Time to First Meaningful Content: ${firstMeaningfulContentTime}ms`);
|
252
|
+
}
|
253
|
+
console.log(` 📈 Content Tokens: ${tokenCount}`);
|
254
|
+
console.log(` 🔧 Tool Argument Tokens: ${toolArgumentTokens}`);
|
255
|
+
console.log(` 📊 Total Tokens: ${totalTokens}`);
|
187
256
|
console.log(` 💨 Tokens Per Second (TPS): ${tokensPerSecond.toFixed(2)}`);
|
257
|
+
// Tool calling metrics
|
258
|
+
if (toolCalls.length > 0) {
|
259
|
+
console.log(`\n🔧 [OpenAI] Tool Calling Metrics:`);
|
260
|
+
console.log(` 🛠️ Total Tools Called: ${toolMetrics.totalTools}`);
|
261
|
+
console.log(` ✅ Successful Tools: ${toolMetrics.successfulTools}`);
|
262
|
+
console.log(` ❌ Failed Tools: ${toolMetrics.failedTools}`);
|
263
|
+
console.log(` 📊 Success Rate: ${((toolMetrics.successfulTools / toolMetrics.totalTools) * 100).toFixed(1)}%`);
|
264
|
+
// Tool timing details
|
265
|
+
toolMetrics.toolTimes.forEach((tool, idx) => {
|
266
|
+
console.log(` 🔨 Tool ${idx + 1} (${tool.name}): ${tool.argumentBuildTime}ms`);
|
267
|
+
});
|
268
|
+
const avgToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0) / toolMetrics.toolTimes.length;
|
269
|
+
console.log(` ⏱️ Average Tool Time: ${avgToolTime.toFixed(2)}ms`);
|
270
|
+
// Round metrics
|
271
|
+
toolMetrics.rounds.forEach(round => {
|
272
|
+
const efficiency = round.toolCount > 0 ? (round.llmTime / (round.llmTime + round.toolTime) * 100).toFixed(1) : 100;
|
273
|
+
console.log(` 🔄 Round ${round.roundNumber}: LLM=${round.llmTime}ms, Tools=${round.toolTime}ms (${round.toolCount} tools), Efficiency=${efficiency}%`);
|
274
|
+
});
|
275
|
+
}
|
188
276
|
if (interTokenDelays.length > 0) {
|
189
277
|
const avgDelay = interTokenDelays.reduce((a, b) => a + b, 0) / interTokenDelays.length;
|
190
278
|
const sortedDelays = [...interTokenDelays].sort((a, b) => a - b);
|
191
279
|
const p50Delay = sortedDelays[Math.floor(sortedDelays.length * 0.5)];
|
192
280
|
const p95Delay = sortedDelays[Math.floor(sortedDelays.length * 0.95)];
|
193
281
|
const p99Delay = sortedDelays[Math.floor(sortedDelays.length * 0.99)];
|
194
|
-
console.log(
|
282
|
+
console.log(`\n⏳ [OpenAI] Inter-Token Timing:`);
|
283
|
+
console.log(` 📊 Average Delay: ${avgDelay.toFixed(2)}ms`);
|
195
284
|
console.log(` 📊 P50 Delay: ${p50Delay}ms`);
|
196
285
|
console.log(` ⚠️ P95 Delay: ${p95Delay}ms`);
|
197
286
|
console.log(` 🚨 P99 Delay: ${p99Delay}ms`);
|
@@ -215,9 +304,22 @@ onEvent, onComplete) {
|
|
215
304
|
// Performance metrics
|
216
305
|
const startTime = Date.now();
|
217
306
|
let firstTokenTime = 0;
|
307
|
+
let firstMeaningfulContentTime = 0;
|
218
308
|
let tokenCount = 0;
|
309
|
+
let toolArgumentTokens = 0;
|
219
310
|
let lastEventTime = 0;
|
220
311
|
const interTokenDelays = [];
|
312
|
+
// Tool calling metrics
|
313
|
+
const toolMetrics = {
|
314
|
+
totalTools: 0,
|
315
|
+
successfulTools: 0,
|
316
|
+
failedTools: 0,
|
317
|
+
toolTimes: [],
|
318
|
+
currentToolStart: 0,
|
319
|
+
roundStartTime: startTime,
|
320
|
+
rounds: [],
|
321
|
+
currentRound: 1
|
322
|
+
};
|
221
323
|
try {
|
222
324
|
const modelName = getModelName(specification);
|
223
325
|
if (!modelName) {
|
@@ -271,6 +373,22 @@ onEvent, onComplete) {
|
|
271
373
|
arguments: "",
|
272
374
|
};
|
273
375
|
toolCalls.push(toolCall);
|
376
|
+
// Track tool metrics
|
377
|
+
toolMetrics.totalTools++;
|
378
|
+
toolMetrics.currentToolStart = Date.now();
|
379
|
+
toolMetrics.toolTimes.push({
|
380
|
+
name: toolCall.name,
|
381
|
+
startTime: toolMetrics.currentToolStart,
|
382
|
+
argumentBuildTime: 0,
|
383
|
+
totalTime: 0
|
384
|
+
});
|
385
|
+
// Track TTFT for first tool if no content yet
|
386
|
+
if (firstTokenTime === 0) {
|
387
|
+
firstTokenTime = Date.now() - startTime;
|
388
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
389
|
+
console.log(`\n⚡ [Anthropic] Time to First Token (Tool Call): ${firstTokenTime}ms`);
|
390
|
+
}
|
391
|
+
}
|
274
392
|
onEvent({
|
275
393
|
type: "tool_call_start",
|
276
394
|
toolCall: {
|
@@ -285,13 +403,20 @@ onEvent, onComplete) {
|
|
285
403
|
fullMessage += chunk.delta.text;
|
286
404
|
tokenCount++;
|
287
405
|
const currentTime = Date.now();
|
288
|
-
// Track TTFT
|
406
|
+
// Track TTFT (first token regardless of type)
|
289
407
|
if (firstTokenTime === 0) {
|
290
408
|
firstTokenTime = currentTime - startTime;
|
291
409
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
292
410
|
console.log(`\n⚡ [Anthropic] Time to First Token (TTFT): ${firstTokenTime}ms`);
|
293
411
|
}
|
294
412
|
}
|
413
|
+
// Track first meaningful content (excludes tool calls)
|
414
|
+
if (firstMeaningfulContentTime === 0 && chunk.delta.text.trim()) {
|
415
|
+
firstMeaningfulContentTime = currentTime - startTime;
|
416
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
417
|
+
console.log(`\n🎯 [Anthropic] Time to First Meaningful Content: ${firstMeaningfulContentTime}ms`);
|
418
|
+
}
|
419
|
+
}
|
295
420
|
// Track inter-token delays
|
296
421
|
if (lastEventTime > 0) {
|
297
422
|
const delay = currentTime - lastEventTime;
|
@@ -311,6 +436,8 @@ onEvent, onComplete) {
|
|
311
436
|
const currentTool = toolCalls[toolCalls.length - 1];
|
312
437
|
if (currentTool) {
|
313
438
|
currentTool.arguments += chunk.delta.partial_json;
|
439
|
+
// Count tool argument tokens (rough estimate: ~4 chars per token)
|
440
|
+
toolArgumentTokens += Math.ceil(chunk.delta.partial_json.length / 4);
|
314
441
|
// Debug logging for partial JSON accumulation
|
315
442
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
316
443
|
console.log(`[Anthropic] Tool ${currentTool.name} - Partial JSON chunk: "${chunk.delta.partial_json}"`);
|
@@ -329,6 +456,27 @@ onEvent, onComplete) {
|
|
329
456
|
// Tool call complete
|
330
457
|
const currentTool = toolCalls[toolCalls.length - 1];
|
331
458
|
if (currentTool) {
|
459
|
+
const currentTime = Date.now();
|
460
|
+
// Update tool metrics
|
461
|
+
const toolIndex = toolCalls.length - 1;
|
462
|
+
if (toolIndex < toolMetrics.toolTimes.length) {
|
463
|
+
const toolTime = toolMetrics.toolTimes[toolIndex];
|
464
|
+
toolTime.argumentBuildTime = currentTime - toolTime.startTime;
|
465
|
+
toolTime.totalTime = toolTime.argumentBuildTime;
|
466
|
+
toolTime.name = currentTool.name;
|
467
|
+
}
|
468
|
+
// Track tool success/failure
|
469
|
+
try {
|
470
|
+
JSON.parse(currentTool.arguments);
|
471
|
+
toolMetrics.successfulTools++;
|
472
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
473
|
+
console.log(`[Anthropic] ✅ Valid JSON for ${currentTool.name}`);
|
474
|
+
}
|
475
|
+
}
|
476
|
+
catch (e) {
|
477
|
+
toolMetrics.failedTools++;
|
478
|
+
console.error(`[Anthropic] ❌ Invalid JSON for ${currentTool.name}: ${e}`);
|
479
|
+
}
|
332
480
|
// Log the final JSON for debugging
|
333
481
|
if (process.env.DEBUG_GRAPHLIT_STREAMING ||
|
334
482
|
!isValidJSON(currentTool.arguments)) {
|
@@ -340,16 +488,6 @@ onEvent, onComplete) {
|
|
340
488
|
currentTool.arguments.length > 100) {
|
341
489
|
console.warn(`[Anthropic] WARNING: JSON may be truncated - doesn't end with '}': ...${lastChars}`);
|
342
490
|
}
|
343
|
-
// Validate JSON
|
344
|
-
try {
|
345
|
-
JSON.parse(currentTool.arguments);
|
346
|
-
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
347
|
-
console.log(`[Anthropic] ✅ Valid JSON for ${currentTool.name}`);
|
348
|
-
}
|
349
|
-
}
|
350
|
-
catch (e) {
|
351
|
-
console.error(`[Anthropic] ❌ Invalid JSON for ${currentTool.name}: ${e}`);
|
352
|
-
}
|
353
491
|
}
|
354
492
|
onEvent({
|
355
493
|
type: "tool_call_complete",
|
@@ -399,22 +537,60 @@ onEvent, onComplete) {
|
|
399
537
|
console.log(`[Anthropic] Filtered out ${toolCalls.length - validToolCalls.length} incomplete tool calls`);
|
400
538
|
console.log(`[Anthropic] Successfully processed ${validToolCalls.length} valid tool calls`);
|
401
539
|
}
|
402
|
-
// Calculate final metrics
|
540
|
+
// Calculate final metrics including tool calling insights
|
403
541
|
const totalTime = Date.now() - startTime;
|
404
|
-
const
|
542
|
+
const totalTokens = tokenCount + toolArgumentTokens;
|
543
|
+
const tokensPerSecond = totalTokens > 0 ? totalTokens / (totalTime / 1000) : 0;
|
544
|
+
// Finalize round metrics
|
545
|
+
if (validToolCalls.length > 0) {
|
546
|
+
const roundEndTime = Date.now();
|
547
|
+
const totalToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0);
|
548
|
+
const llmTime = totalTime - totalToolTime;
|
549
|
+
toolMetrics.rounds.push({
|
550
|
+
roundNumber: toolMetrics.currentRound,
|
551
|
+
llmTime: llmTime,
|
552
|
+
toolTime: totalToolTime,
|
553
|
+
toolCount: validToolCalls.length
|
554
|
+
});
|
555
|
+
}
|
405
556
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
406
557
|
console.log("\n📊 [Anthropic] Performance Metrics:");
|
407
558
|
console.log(` ⏱️ Total Time: ${totalTime}ms`);
|
408
559
|
console.log(` ⚡ Time to First Token (TTFT): ${firstTokenTime}ms`);
|
409
|
-
|
560
|
+
if (firstMeaningfulContentTime > 0) {
|
561
|
+
console.log(` 🎯 Time to First Meaningful Content: ${firstMeaningfulContentTime}ms`);
|
562
|
+
}
|
563
|
+
console.log(` 📈 Content Tokens: ${tokenCount}`);
|
564
|
+
console.log(` 🔧 Tool Argument Tokens: ${toolArgumentTokens}`);
|
565
|
+
console.log(` 📊 Total Tokens: ${totalTokens}`);
|
410
566
|
console.log(` 💨 Tokens Per Second (TPS): ${tokensPerSecond.toFixed(2)}`);
|
567
|
+
// Tool calling metrics
|
568
|
+
if (validToolCalls.length > 0) {
|
569
|
+
console.log(`\n🔧 [Anthropic] Tool Calling Metrics:`);
|
570
|
+
console.log(` 🛠️ Total Tools Called: ${toolMetrics.totalTools}`);
|
571
|
+
console.log(` ✅ Successful Tools: ${toolMetrics.successfulTools}`);
|
572
|
+
console.log(` ❌ Failed Tools: ${toolMetrics.failedTools}`);
|
573
|
+
console.log(` 📊 Success Rate: ${((toolMetrics.successfulTools / toolMetrics.totalTools) * 100).toFixed(1)}%`);
|
574
|
+
// Tool timing details
|
575
|
+
toolMetrics.toolTimes.forEach((tool, idx) => {
|
576
|
+
console.log(` 🔨 Tool ${idx + 1} (${tool.name}): ${tool.argumentBuildTime}ms`);
|
577
|
+
});
|
578
|
+
const avgToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0) / toolMetrics.toolTimes.length;
|
579
|
+
console.log(` ⏱️ Average Tool Time: ${avgToolTime.toFixed(2)}ms`);
|
580
|
+
// Round metrics
|
581
|
+
toolMetrics.rounds.forEach(round => {
|
582
|
+
const efficiency = round.toolCount > 0 ? (round.llmTime / (round.llmTime + round.toolTime) * 100).toFixed(1) : 100;
|
583
|
+
console.log(` 🔄 Round ${round.roundNumber}: LLM=${round.llmTime}ms, Tools=${round.toolTime}ms (${round.toolCount} tools), Efficiency=${efficiency}%`);
|
584
|
+
});
|
585
|
+
}
|
411
586
|
if (interTokenDelays.length > 0) {
|
412
587
|
const avgDelay = interTokenDelays.reduce((a, b) => a + b, 0) / interTokenDelays.length;
|
413
588
|
const sortedDelays = [...interTokenDelays].sort((a, b) => a - b);
|
414
589
|
const p50Delay = sortedDelays[Math.floor(sortedDelays.length * 0.5)];
|
415
590
|
const p95Delay = sortedDelays[Math.floor(sortedDelays.length * 0.95)];
|
416
591
|
const p99Delay = sortedDelays[Math.floor(sortedDelays.length * 0.99)];
|
417
|
-
console.log(
|
592
|
+
console.log(`\n⏳ [Anthropic] Inter-Token Timing:`);
|
593
|
+
console.log(` 📊 Average Delay: ${avgDelay.toFixed(2)}ms`);
|
418
594
|
console.log(` 📊 P50 Delay: ${p50Delay}ms`);
|
419
595
|
console.log(` ⚠️ P95 Delay: ${p95Delay}ms`);
|
420
596
|
console.log(` 🚨 P99 Delay: ${p99Delay}ms`);
|
@@ -438,9 +614,22 @@ onEvent, onComplete) {
|
|
438
614
|
// Performance metrics
|
439
615
|
const startTime = Date.now();
|
440
616
|
let firstTokenTime = 0;
|
617
|
+
let firstMeaningfulContentTime = 0;
|
441
618
|
let tokenCount = 0;
|
619
|
+
let toolArgumentTokens = 0;
|
442
620
|
let lastEventTime = 0;
|
443
621
|
const interTokenDelays = [];
|
622
|
+
// Tool calling metrics
|
623
|
+
const toolMetrics = {
|
624
|
+
totalTools: 0,
|
625
|
+
successfulTools: 0,
|
626
|
+
failedTools: 0,
|
627
|
+
toolTimes: [],
|
628
|
+
currentToolStart: 0,
|
629
|
+
roundStartTime: startTime,
|
630
|
+
rounds: [],
|
631
|
+
currentRound: 1
|
632
|
+
};
|
444
633
|
try {
|
445
634
|
const modelName = getModelName(specification);
|
446
635
|
if (!modelName) {
|
@@ -514,6 +703,22 @@ onEvent, onComplete) {
|
|
514
703
|
}
|
515
704
|
if (text) {
|
516
705
|
fullMessage += text;
|
706
|
+
tokenCount++;
|
707
|
+
const currentTime = Date.now();
|
708
|
+
// Track TTFT (first token regardless of type)
|
709
|
+
if (firstTokenTime === 0) {
|
710
|
+
firstTokenTime = currentTime - startTime;
|
711
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
712
|
+
console.log(`\n⚡ [Google] Time to First Token (TTFT): ${firstTokenTime}ms`);
|
713
|
+
}
|
714
|
+
}
|
715
|
+
// Track first meaningful content
|
716
|
+
if (firstMeaningfulContentTime === 0 && text.trim()) {
|
717
|
+
firstMeaningfulContentTime = currentTime - startTime;
|
718
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
719
|
+
console.log(`\n🎯 [Google] Time to First Meaningful Content: ${firstMeaningfulContentTime}ms`);
|
720
|
+
}
|
721
|
+
}
|
517
722
|
onEvent({
|
518
723
|
type: "token",
|
519
724
|
token: text,
|
@@ -536,6 +741,23 @@ onEvent, onComplete) {
|
|
536
741
|
arguments: JSON.stringify(part.functionCall.args || {}),
|
537
742
|
};
|
538
743
|
toolCalls.push(toolCall);
|
744
|
+
// Track tool metrics
|
745
|
+
toolMetrics.totalTools++;
|
746
|
+
const argumentString = JSON.stringify(part.functionCall.args || {});
|
747
|
+
toolArgumentTokens += Math.ceil(argumentString.length / 4);
|
748
|
+
toolMetrics.toolTimes.push({
|
749
|
+
name: part.functionCall.name,
|
750
|
+
startTime: Date.now(),
|
751
|
+
argumentBuildTime: 0, // Google returns complete args at once
|
752
|
+
totalTime: 0
|
753
|
+
});
|
754
|
+
// Track TTFT for first tool if no content yet
|
755
|
+
if (firstTokenTime === 0) {
|
756
|
+
firstTokenTime = Date.now() - startTime;
|
757
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
758
|
+
console.log(`\n⚡ [Google] Time to First Token (Tool Call): ${firstTokenTime}ms`);
|
759
|
+
}
|
760
|
+
}
|
539
761
|
// Emit tool call events
|
540
762
|
onEvent({
|
541
763
|
type: "tool_call_start",
|
@@ -549,18 +771,28 @@ onEvent, onComplete) {
|
|
549
771
|
toolCallId: toolCall.id,
|
550
772
|
argumentDelta: toolCall.arguments,
|
551
773
|
});
|
552
|
-
//
|
774
|
+
// Update tool metrics and validate JSON
|
775
|
+
const toolIndex = toolCalls.length - 1;
|
776
|
+
if (toolIndex < toolMetrics.toolTimes.length) {
|
777
|
+
const toolTime = toolMetrics.toolTimes[toolIndex];
|
778
|
+
toolTime.totalTime = Date.now() - toolTime.startTime;
|
779
|
+
toolTime.argumentBuildTime = toolTime.totalTime; // Google returns complete args
|
780
|
+
}
|
781
|
+
try {
|
782
|
+
JSON.parse(toolCall.arguments);
|
783
|
+
toolMetrics.successfulTools++;
|
784
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
785
|
+
console.log(`[Google] ✅ Valid JSON for ${toolCall.name}`);
|
786
|
+
}
|
787
|
+
}
|
788
|
+
catch (e) {
|
789
|
+
toolMetrics.failedTools++;
|
790
|
+
console.error(`[Google] ❌ Invalid JSON for ${toolCall.name}: ${e}`);
|
791
|
+
}
|
792
|
+
// Log completion
|
553
793
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
554
794
|
console.log(`[Google] Tool ${toolCall.name} complete with arguments (${toolCall.arguments.length} chars):`);
|
555
795
|
console.log(toolCall.arguments);
|
556
|
-
// Validate JSON
|
557
|
-
try {
|
558
|
-
JSON.parse(toolCall.arguments);
|
559
|
-
console.log(`[Google] ✅ Valid JSON for ${toolCall.name}`);
|
560
|
-
}
|
561
|
-
catch (e) {
|
562
|
-
console.error(`[Google] ❌ Invalid JSON for ${toolCall.name}: ${e}`);
|
563
|
-
}
|
564
796
|
}
|
565
797
|
onEvent({
|
566
798
|
type: "tool_call_complete",
|
@@ -647,22 +879,60 @@ onEvent, onComplete) {
|
|
647
879
|
if (process.env.DEBUG_GRAPHLIT_STREAMING && toolCalls.length > 0) {
|
648
880
|
console.log(`[Google] Successfully processed ${toolCalls.length} tool calls`);
|
649
881
|
}
|
650
|
-
// Calculate final metrics
|
882
|
+
// Calculate final metrics including tool calling insights
|
651
883
|
const totalTime = Date.now() - startTime;
|
652
|
-
const
|
884
|
+
const totalTokens = tokenCount + toolArgumentTokens;
|
885
|
+
const tokensPerSecond = totalTokens > 0 ? totalTokens / (totalTime / 1000) : 0;
|
886
|
+
// Finalize round metrics
|
887
|
+
if (toolCalls.length > 0) {
|
888
|
+
const roundEndTime = Date.now();
|
889
|
+
const totalToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0);
|
890
|
+
const llmTime = totalTime - totalToolTime;
|
891
|
+
toolMetrics.rounds.push({
|
892
|
+
roundNumber: toolMetrics.currentRound,
|
893
|
+
llmTime: llmTime,
|
894
|
+
toolTime: totalToolTime,
|
895
|
+
toolCount: toolCalls.length
|
896
|
+
});
|
897
|
+
}
|
653
898
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
654
899
|
console.log("\n📊 [Google] Performance Metrics:");
|
655
900
|
console.log(` ⏱️ Total Time: ${totalTime}ms`);
|
656
901
|
console.log(` ⚡ Time to First Token (TTFT): ${firstTokenTime}ms`);
|
657
|
-
|
902
|
+
if (firstMeaningfulContentTime > 0) {
|
903
|
+
console.log(` 🎯 Time to First Meaningful Content: ${firstMeaningfulContentTime}ms`);
|
904
|
+
}
|
905
|
+
console.log(` 📈 Content Tokens: ${tokenCount}`);
|
906
|
+
console.log(` 🔧 Tool Argument Tokens: ${toolArgumentTokens}`);
|
907
|
+
console.log(` 📊 Total Tokens: ${totalTokens}`);
|
658
908
|
console.log(` 💨 Tokens Per Second (TPS): ${tokensPerSecond.toFixed(2)}`);
|
909
|
+
// Tool calling metrics
|
910
|
+
if (toolCalls.length > 0) {
|
911
|
+
console.log(`\n🔧 [Google] Tool Calling Metrics:`);
|
912
|
+
console.log(` 🛠️ Total Tools Called: ${toolMetrics.totalTools}`);
|
913
|
+
console.log(` ✅ Successful Tools: ${toolMetrics.successfulTools}`);
|
914
|
+
console.log(` ❌ Failed Tools: ${toolMetrics.failedTools}`);
|
915
|
+
console.log(` 📊 Success Rate: ${((toolMetrics.successfulTools / toolMetrics.totalTools) * 100).toFixed(1)}%`);
|
916
|
+
// Tool timing details
|
917
|
+
toolMetrics.toolTimes.forEach((tool, idx) => {
|
918
|
+
console.log(` 🔨 Tool ${idx + 1} (${tool.name}): ${tool.argumentBuildTime}ms`);
|
919
|
+
});
|
920
|
+
const avgToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0) / toolMetrics.toolTimes.length;
|
921
|
+
console.log(` ⏱️ Average Tool Time: ${avgToolTime.toFixed(2)}ms`);
|
922
|
+
// Round metrics
|
923
|
+
toolMetrics.rounds.forEach(round => {
|
924
|
+
const efficiency = round.toolCount > 0 ? (round.llmTime / (round.llmTime + round.toolTime) * 100).toFixed(1) : 100;
|
925
|
+
console.log(` 🔄 Round ${round.roundNumber}: LLM=${round.llmTime}ms, Tools=${round.toolTime}ms (${round.toolCount} tools), Efficiency=${efficiency}%`);
|
926
|
+
});
|
927
|
+
}
|
659
928
|
if (interTokenDelays.length > 0) {
|
660
929
|
const avgDelay = interTokenDelays.reduce((a, b) => a + b, 0) / interTokenDelays.length;
|
661
930
|
const sortedDelays = [...interTokenDelays].sort((a, b) => a - b);
|
662
931
|
const p50Delay = sortedDelays[Math.floor(sortedDelays.length * 0.5)];
|
663
932
|
const p95Delay = sortedDelays[Math.floor(sortedDelays.length * 0.95)];
|
664
933
|
const p99Delay = sortedDelays[Math.floor(sortedDelays.length * 0.99)];
|
665
|
-
console.log(
|
934
|
+
console.log(`\n⏳ [Google] Inter-Token Timing:`);
|
935
|
+
console.log(` 📊 Average Delay: ${avgDelay.toFixed(2)}ms`);
|
666
936
|
console.log(` 📊 P50 Delay: ${p50Delay}ms`);
|
667
937
|
console.log(` ⚠️ P95 Delay: ${p95Delay}ms`);
|
668
938
|
console.log(` 🚨 P99 Delay: ${p99Delay}ms`);
|