graphlit-client 1.0.20250612002 → 1.0.20250612004
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
@@ -18,13 +18,13 @@ let Anthropic;
|
|
18
18
|
let GoogleGenerativeAI;
|
19
19
|
try {
|
20
20
|
OpenAI = optionalRequire("openai").default || optionalRequire("openai");
|
21
|
-
if (process.env.
|
21
|
+
if (process.env.DEBUG_GRAPHLIT_INITIALIZATION) {
|
22
22
|
console.log("[SDK Loading] OpenAI SDK loaded successfully");
|
23
23
|
}
|
24
24
|
}
|
25
25
|
catch (e) {
|
26
26
|
// OpenAI not installed
|
27
|
-
if (process.env.
|
27
|
+
if (process.env.DEBUG_GRAPHLIT_INITIALIZATION) {
|
28
28
|
console.log("[SDK Loading] OpenAI SDK not found:", e.message);
|
29
29
|
}
|
30
30
|
}
|
@@ -32,25 +32,25 @@ try {
|
|
32
32
|
Anthropic =
|
33
33
|
optionalRequire("@anthropic-ai/sdk").default ||
|
34
34
|
optionalRequire("@anthropic-ai/sdk");
|
35
|
-
if (process.env.
|
35
|
+
if (process.env.DEBUG_GRAPHLIT_INITIALIZATION) {
|
36
36
|
console.log("[SDK Loading] Anthropic SDK loaded successfully");
|
37
37
|
}
|
38
38
|
}
|
39
39
|
catch (e) {
|
40
40
|
// Anthropic SDK not installed
|
41
|
-
if (process.env.
|
41
|
+
if (process.env.DEBUG_GRAPHLIT_INITIALIZATION) {
|
42
42
|
console.log("[SDK Loading] Anthropic SDK not found:", e.message);
|
43
43
|
}
|
44
44
|
}
|
45
45
|
try {
|
46
46
|
GoogleGenerativeAI = optionalRequire("@google/generative-ai").GoogleGenerativeAI;
|
47
|
-
if (process.env.
|
47
|
+
if (process.env.DEBUG_GRAPHLIT_INITIALIZATION) {
|
48
48
|
console.log("[SDK Loading] Google Generative AI SDK loaded successfully");
|
49
49
|
}
|
50
50
|
}
|
51
51
|
catch (e) {
|
52
52
|
// Google Generative AI not installed
|
53
|
-
if (process.env.
|
53
|
+
if (process.env.DEBUG_GRAPHLIT_INITIALIZATION) {
|
54
54
|
console.log("[SDK Loading] Google Generative AI SDK not found:", e.message);
|
55
55
|
}
|
56
56
|
}
|
@@ -1416,7 +1416,7 @@ class Graphlit {
|
|
1416
1416
|
// If we have a full specification, check its service type
|
1417
1417
|
if (specification) {
|
1418
1418
|
const serviceType = specification.serviceType;
|
1419
|
-
if (process.env.
|
1419
|
+
if (process.env.DEBUG_GRAPHLIT_INITIALIZATION) {
|
1420
1420
|
console.log("[supportsStreaming] Checking support for:", {
|
1421
1421
|
serviceType,
|
1422
1422
|
hasOpenAI: OpenAI !== undefined || this.openaiClient !== undefined,
|
@@ -1430,7 +1430,7 @@ class Graphlit {
|
|
1430
1430
|
case Types.ModelServiceTypes.Anthropic:
|
1431
1431
|
return Anthropic !== undefined || this.anthropicClient !== undefined;
|
1432
1432
|
case Types.ModelServiceTypes.Google:
|
1433
|
-
return GoogleGenerativeAI !== undefined || this.googleClient !== undefined;
|
1433
|
+
return (GoogleGenerativeAI !== undefined || this.googleClient !== undefined);
|
1434
1434
|
default:
|
1435
1435
|
return false;
|
1436
1436
|
}
|
@@ -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,
|
@@ -1692,15 +1728,14 @@ class Graphlit {
|
|
1692
1728
|
console.log(` Anthropic available: ${!!(Anthropic || this.anthropicClient)}`);
|
1693
1729
|
console.log(` Google available: ${!!(GoogleGenerativeAI || this.googleClient)}`);
|
1694
1730
|
}
|
1695
|
-
if (serviceType === Types.ModelServiceTypes.OpenAi &&
|
1731
|
+
if (serviceType === Types.ModelServiceTypes.OpenAi &&
|
1732
|
+
(OpenAI || this.openaiClient)) {
|
1696
1733
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
1697
1734
|
console.log(`\n✅ [Streaming] Using OpenAI native streaming (Round ${currentRound})`);
|
1698
1735
|
}
|
1699
1736
|
const openaiMessages = formatMessagesForOpenAI(messages);
|
1700
|
-
if (process.env.
|
1701
|
-
console.log(
|
1702
|
-
console.log(JSON.stringify(openaiMessages, null, 2));
|
1703
|
-
console.log("Total messages:", openaiMessages.length);
|
1737
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING_MESSAGES) {
|
1738
|
+
console.log(`🔍 [OpenAI] Sending ${openaiMessages.length} messages to LLM: ${JSON.stringify(openaiMessages)}`);
|
1704
1739
|
}
|
1705
1740
|
await this.streamWithOpenAI(specification, openaiMessages, tools, uiAdapter, (message, calls) => {
|
1706
1741
|
roundMessage = message;
|
@@ -1716,11 +1751,8 @@ class Graphlit {
|
|
1716
1751
|
console.log(`\n✅ [Streaming] Using Anthropic native streaming (Round ${currentRound})`);
|
1717
1752
|
}
|
1718
1753
|
const { system, messages: anthropicMessages } = formatMessagesForAnthropic(messages);
|
1719
|
-
if (process.env.
|
1720
|
-
console.log(
|
1721
|
-
console.log("System prompt:", system);
|
1722
|
-
console.log(JSON.stringify(anthropicMessages, null, 2));
|
1723
|
-
console.log("Total messages:", anthropicMessages.length);
|
1754
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING_MESSAGES) {
|
1755
|
+
console.log(`🔍 [Anthropic] Sending ${anthropicMessages.length} messages to LLM (system: ${system ? "yes" : "no"}): ${JSON.stringify(anthropicMessages)}`);
|
1724
1756
|
}
|
1725
1757
|
await this.streamWithAnthropic(specification, anthropicMessages, system, tools, uiAdapter, (message, calls) => {
|
1726
1758
|
roundMessage = message;
|
@@ -1736,10 +1768,8 @@ class Graphlit {
|
|
1736
1768
|
console.log(`\n✅ [Streaming] Using Google native streaming (Round ${currentRound})`);
|
1737
1769
|
}
|
1738
1770
|
const googleMessages = formatMessagesForGoogle(messages);
|
1739
|
-
if (process.env.
|
1740
|
-
console.log(
|
1741
|
-
console.log(JSON.stringify(googleMessages, null, 2));
|
1742
|
-
console.log("Total messages:", googleMessages.length);
|
1771
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING_MESSAGES) {
|
1772
|
+
console.log(`🔍 [Google] Sending ${googleMessages.length} messages to LLM: ${JSON.stringify(googleMessages)}`);
|
1743
1773
|
}
|
1744
1774
|
// Google doesn't use system prompts separately, they're incorporated into messages
|
1745
1775
|
await this.streamWithGoogle(specification, googleMessages, undefined, // systemPrompt - Google handles this differently
|
@@ -2006,19 +2036,12 @@ class Graphlit {
|
|
2006
2036
|
*/
|
2007
2037
|
async fallbackToNonStreaming(prompt, conversationId, specification, tools, mimeType, data, uiAdapter, correlationId) {
|
2008
2038
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
2009
|
-
console.log(
|
2010
|
-
console.log(` Conversation ID: ${conversationId}`);
|
2011
|
-
console.log(` Specification: ${specification.name} (${specification.serviceType})`);
|
2012
|
-
console.log(` Prompt: "${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}"`);
|
2013
|
-
console.log(` About to call promptConversation...`);
|
2039
|
+
console.log(`🔄 [Fallback] Starting non-streaming fallback | ConvID: ${conversationId} | Spec: ${specification.name} (${specification.serviceType}) | Prompt: "${prompt.substring(0, 50)}${prompt.length > 50 ? "..." : ""}"`);
|
2014
2040
|
}
|
2015
2041
|
const response = await this.promptConversation(prompt, conversationId, { id: specification.id }, mimeType, data, tools, false, false, correlationId);
|
2016
2042
|
const message = response.promptConversation?.message;
|
2017
2043
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
2018
|
-
console.log(
|
2019
|
-
console.log(` Response message length: ${message?.message?.length || 0} chars`);
|
2020
|
-
console.log(` Response preview: "${message?.message?.substring(0, 100) || 'NO MESSAGE'}${(message?.message?.length || 0) > 100 ? '...' : ''}"`);
|
2021
|
-
console.log(` Now simulating streaming by splitting into tokens...`);
|
2044
|
+
console.log(`✅ [Fallback] promptConversation completed | Length: ${message?.message?.length || 0} chars | Preview: "${message?.message?.substring(0, 50) || "NO MESSAGE"}${(message?.message?.length || 0) > 50 ? "..." : ""}"`);
|
2022
2045
|
}
|
2023
2046
|
if (message?.message) {
|
2024
2047
|
// Simulate streaming by emitting tokens
|
@@ -2043,14 +2066,15 @@ class Graphlit {
|
|
2043
2066
|
}
|
2044
2067
|
// Use provided client or create a new one
|
2045
2068
|
const openaiClient = this.openaiClient ||
|
2046
|
-
(OpenAI
|
2047
|
-
|
2048
|
-
|
2069
|
+
(OpenAI
|
2070
|
+
? new OpenAI({
|
2071
|
+
apiKey: process.env.OPENAI_API_KEY || "",
|
2072
|
+
})
|
2073
|
+
: (() => {
|
2074
|
+
throw new Error("OpenAI module not available");
|
2075
|
+
})());
|
2049
2076
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
2050
|
-
console.log(
|
2051
|
-
console.log(` 📋 Specification: ${specification.name} (${specification.id})`);
|
2052
|
-
console.log(` 📝 Messages: ${messages.length}`);
|
2053
|
-
console.log(` 🔧 Tools: ${tools?.length || 0}`);
|
2077
|
+
console.log(`🚀 [Graphlit SDK] Routing to OpenAI streaming provider | Spec: ${specification.name} (${specification.id}) | Messages: ${messages.length} | Tools: ${tools?.length || 0}`);
|
2054
2078
|
}
|
2055
2079
|
await streamWithOpenAI(specification, messages, tools, openaiClient, (event) => uiAdapter.handleEvent(event), onComplete);
|
2056
2080
|
}
|
@@ -2064,15 +2088,15 @@ class Graphlit {
|
|
2064
2088
|
}
|
2065
2089
|
// Use provided client or create a new one
|
2066
2090
|
const anthropicClient = this.anthropicClient ||
|
2067
|
-
(Anthropic
|
2068
|
-
|
2069
|
-
|
2091
|
+
(Anthropic
|
2092
|
+
? new Anthropic({
|
2093
|
+
apiKey: process.env.ANTHROPIC_API_KEY || "",
|
2094
|
+
})
|
2095
|
+
: (() => {
|
2096
|
+
throw new Error("Anthropic module not available");
|
2097
|
+
})());
|
2070
2098
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
2071
|
-
console.log(
|
2072
|
-
console.log(` 📋 Specification: ${specification.name} (${specification.id})`);
|
2073
|
-
console.log(` 📝 Messages: ${messages.length}`);
|
2074
|
-
console.log(` 🔧 Tools: ${tools?.length || 0}`);
|
2075
|
-
console.log(` 💬 System Prompt: ${systemPrompt ? 'Yes' : 'No'}`);
|
2099
|
+
console.log(`🚀 [Graphlit SDK] Routing to Anthropic streaming provider | Spec: ${specification.name} (${specification.id}) | Messages: ${messages.length} | Tools: ${tools?.length || 0} | SystemPrompt: ${systemPrompt ? "Yes" : "No"}`);
|
2076
2100
|
}
|
2077
2101
|
await streamWithAnthropic(specification, messages, systemPrompt, tools, anthropicClient, (event) => uiAdapter.handleEvent(event), onComplete);
|
2078
2102
|
}
|
@@ -2086,13 +2110,13 @@ class Graphlit {
|
|
2086
2110
|
}
|
2087
2111
|
// Use provided client or create a new one
|
2088
2112
|
const googleClient = this.googleClient ||
|
2089
|
-
(GoogleGenerativeAI
|
2113
|
+
(GoogleGenerativeAI
|
2114
|
+
? new GoogleGenerativeAI(process.env.GOOGLE_API_KEY || "")
|
2115
|
+
: (() => {
|
2116
|
+
throw new Error("Google GenerativeAI module not available");
|
2117
|
+
})());
|
2090
2118
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
2091
|
-
console.log(
|
2092
|
-
console.log(` 📋 Specification: ${specification.name} (${specification.id})`);
|
2093
|
-
console.log(` 📝 Messages: ${messages.length}`);
|
2094
|
-
console.log(` 🔧 Tools: ${tools?.length || 0}`);
|
2095
|
-
console.log(` 💬 System Prompt: ${systemPrompt ? 'Yes' : 'No'}`);
|
2119
|
+
console.log(`🚀 [Graphlit SDK] Routing to Google streaming provider | Spec: ${specification.name} (${specification.id}) | Messages: ${messages.length} | Tools: ${tools?.length || 0} | SystemPrompt: ${systemPrompt ? "Yes" : "No"}`);
|
2096
2120
|
}
|
2097
2121
|
await streamWithGoogle(specification, messages, systemPrompt, tools, googleClient, (event) => uiAdapter.handleEvent(event), onComplete);
|
2098
2122
|
}
|
@@ -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,22 +21,29 @@ 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) {
|
30
43
|
throw new Error(`No model name found for OpenAI specification: ${specification.name}`);
|
31
44
|
}
|
32
45
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
33
|
-
console.log(
|
34
|
-
console.log(" Service: OpenAI");
|
35
|
-
console.log(" Model:", modelName);
|
36
|
-
console.log(" Temperature:", specification.openAI?.temperature);
|
37
|
-
console.log(" Max Tokens:", specification.openAI?.completionTokenLimit);
|
38
|
-
console.log(" Tools:", tools?.length || 0);
|
39
|
-
console.log(" Specification Name:", specification.name);
|
46
|
+
console.log(`🤖 [OpenAI] Model Config: Service=OpenAI | Model=${modelName} | Temperature=${specification.openAI?.temperature} | MaxTokens=${specification.openAI?.completionTokenLimit || "null"} | Tools=${tools?.length || 0} | Spec="${specification.name}"`);
|
40
47
|
}
|
41
48
|
const streamConfig = {
|
42
49
|
model: modelName,
|
@@ -62,7 +69,7 @@ onEvent, onComplete) {
|
|
62
69
|
}));
|
63
70
|
}
|
64
71
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
65
|
-
console.log(
|
72
|
+
console.log(`⏱️ [OpenAI] Starting LLM call at: ${new Date().toISOString()}`);
|
66
73
|
}
|
67
74
|
const stream = await openaiClient.chat.completions.create(streamConfig);
|
68
75
|
for await (const chunk of stream) {
|
@@ -84,13 +91,20 @@ onEvent, onComplete) {
|
|
84
91
|
fullMessage += delta.content;
|
85
92
|
tokenCount++;
|
86
93
|
const currentTime = Date.now();
|
87
|
-
// Track TTFT
|
94
|
+
// Track TTFT (first token regardless of type)
|
88
95
|
if (firstTokenTime === 0) {
|
89
96
|
firstTokenTime = currentTime - startTime;
|
90
97
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
91
98
|
console.log(`\n⚡ [OpenAI] Time to First Token (TTFT): ${firstTokenTime}ms`);
|
92
99
|
}
|
93
100
|
}
|
101
|
+
// Track first meaningful content (excludes tool calls)
|
102
|
+
if (firstMeaningfulContentTime === 0 && delta.content.trim()) {
|
103
|
+
firstMeaningfulContentTime = currentTime - startTime;
|
104
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
105
|
+
console.log(`\n🎯 [OpenAI] Time to First Meaningful Content: ${firstMeaningfulContentTime}ms`);
|
106
|
+
}
|
107
|
+
}
|
94
108
|
// Track inter-token delays
|
95
109
|
if (lastEventTime > 0) {
|
96
110
|
const delay = currentTime - lastEventTime;
|
@@ -115,6 +129,22 @@ onEvent, onComplete) {
|
|
115
129
|
name: "",
|
116
130
|
arguments: "",
|
117
131
|
};
|
132
|
+
// Track tool metrics
|
133
|
+
toolMetrics.totalTools++;
|
134
|
+
toolMetrics.currentToolStart = Date.now();
|
135
|
+
toolMetrics.toolTimes.push({
|
136
|
+
name: toolCallDelta.function?.name || "unknown",
|
137
|
+
startTime: toolMetrics.currentToolStart,
|
138
|
+
argumentBuildTime: 0,
|
139
|
+
totalTime: 0,
|
140
|
+
});
|
141
|
+
// Track TTFT for first tool if no content yet
|
142
|
+
if (firstTokenTime === 0) {
|
143
|
+
firstTokenTime = Date.now() - startTime;
|
144
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
145
|
+
console.log(`\n⚡ [OpenAI] Time to First Token (Tool Call): ${firstTokenTime}ms`);
|
146
|
+
}
|
147
|
+
}
|
118
148
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
119
149
|
console.log(`[OpenAI] Starting new tool call: ${toolCalls[index].id}`);
|
120
150
|
}
|
@@ -134,6 +164,8 @@ onEvent, onComplete) {
|
|
134
164
|
}
|
135
165
|
if (toolCallDelta.function?.arguments) {
|
136
166
|
toolCalls[index].arguments += toolCallDelta.function.arguments;
|
167
|
+
// Count tool argument tokens (rough estimate: ~4 chars per token)
|
168
|
+
toolArgumentTokens += Math.ceil(toolCallDelta.function.arguments.length / 4);
|
137
169
|
// Debug logging for partial JSON accumulation
|
138
170
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
139
171
|
console.log(`[OpenAI] Tool ${toolCalls[index].name} - Partial JSON chunk: "${toolCallDelta.function.arguments}"`);
|
@@ -148,20 +180,33 @@ onEvent, onComplete) {
|
|
148
180
|
}
|
149
181
|
}
|
150
182
|
}
|
151
|
-
// Emit complete events for tool calls
|
152
|
-
for (
|
183
|
+
// Emit complete events for tool calls and finalize metrics
|
184
|
+
for (let i = 0; i < toolCalls.length; i++) {
|
185
|
+
const toolCall = toolCalls[i];
|
186
|
+
const currentTime = Date.now();
|
187
|
+
// Update tool metrics
|
188
|
+
if (i < toolMetrics.toolTimes.length) {
|
189
|
+
const toolTime = toolMetrics.toolTimes[i];
|
190
|
+
toolTime.argumentBuildTime = currentTime - toolTime.startTime;
|
191
|
+
toolTime.totalTime = toolTime.argumentBuildTime; // For streaming, this is the same
|
192
|
+
toolTime.name = toolCall.name; // Update with final name
|
193
|
+
}
|
194
|
+
// Track tool success/failure
|
195
|
+
try {
|
196
|
+
JSON.parse(toolCall.arguments);
|
197
|
+
toolMetrics.successfulTools++;
|
198
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
199
|
+
console.log(`[OpenAI] ✅ Valid JSON for ${toolCall.name}`);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
catch (e) {
|
203
|
+
toolMetrics.failedTools++;
|
204
|
+
console.error(`[OpenAI] ❌ Invalid JSON for ${toolCall.name}: ${e}`);
|
205
|
+
}
|
153
206
|
// Log the final JSON for debugging
|
154
207
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
155
208
|
console.log(`[OpenAI] Tool ${toolCall.name} complete with arguments (${toolCall.arguments.length} chars):`);
|
156
209
|
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
210
|
}
|
166
211
|
onEvent({
|
167
212
|
type: "tool_call_complete",
|
@@ -176,27 +221,72 @@ onEvent, onComplete) {
|
|
176
221
|
if (process.env.DEBUG_GRAPHLIT_STREAMING && toolCalls.length > 0) {
|
177
222
|
console.log(`[OpenAI] Successfully processed ${toolCalls.length} tool calls`);
|
178
223
|
}
|
179
|
-
// Calculate final metrics
|
224
|
+
// Calculate final metrics including tool calling insights
|
180
225
|
const totalTime = Date.now() - startTime;
|
181
|
-
const
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
226
|
+
const totalTokens = tokenCount + toolArgumentTokens;
|
227
|
+
const tokensPerSecond = totalTokens > 0 ? totalTokens / (totalTime / 1000) : 0;
|
228
|
+
// Finalize round metrics
|
229
|
+
if (toolCalls.length > 0) {
|
230
|
+
const roundEndTime = Date.now();
|
231
|
+
const totalToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0);
|
232
|
+
const llmTime = totalTime - totalToolTime;
|
233
|
+
toolMetrics.rounds.push({
|
234
|
+
roundNumber: toolMetrics.currentRound,
|
235
|
+
llmTime: llmTime,
|
236
|
+
toolTime: totalToolTime,
|
237
|
+
toolCount: toolCalls.length,
|
238
|
+
});
|
239
|
+
}
|
240
|
+
if (process.env.DEBUG_GRAPHLIT_METRICS) {
|
241
|
+
const metricsData = {
|
242
|
+
totalTime: `${totalTime}ms`,
|
243
|
+
ttft: `${firstTokenTime}ms`,
|
244
|
+
ttfmc: firstMeaningfulContentTime > 0
|
245
|
+
? `${firstMeaningfulContentTime}ms`
|
246
|
+
: null,
|
247
|
+
contentTokens: tokenCount,
|
248
|
+
toolTokens: toolArgumentTokens,
|
249
|
+
totalTokens: totalTokens,
|
250
|
+
tps: tokensPerSecond.toFixed(2),
|
251
|
+
};
|
252
|
+
console.log(`📊 [OpenAI] Performance: Total=${metricsData.totalTime} | TTFT=${metricsData.ttft}${metricsData.ttfmc ? ` | TTFMC=${metricsData.ttfmc}` : ""} | Tokens(content/tool/total)=${metricsData.contentTokens}/${metricsData.toolTokens}/${metricsData.totalTokens} | TPS=${metricsData.tps}`);
|
253
|
+
// Tool calling metrics
|
254
|
+
if (toolCalls.length > 0) {
|
255
|
+
const successRate = ((toolMetrics.successfulTools / toolMetrics.totalTools) *
|
256
|
+
100).toFixed(1);
|
257
|
+
const avgToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0) /
|
258
|
+
toolMetrics.toolTimes.length;
|
259
|
+
console.log(`🔧 [OpenAI] Tools: Total=${toolMetrics.totalTools} | Success=${toolMetrics.successfulTools} | Failed=${toolMetrics.failedTools} | SuccessRate=${successRate}% | AvgTime=${avgToolTime.toFixed(2)}ms`);
|
260
|
+
// Tool timing details (consolidated)
|
261
|
+
const toolTimings = toolMetrics.toolTimes
|
262
|
+
.map((tool, idx) => `${tool.name}:${tool.argumentBuildTime}ms`)
|
263
|
+
.join(" | ");
|
264
|
+
if (toolTimings) {
|
265
|
+
console.log(`🔨 [OpenAI] Tool Timings: ${toolTimings}`);
|
266
|
+
}
|
267
|
+
// Round metrics (consolidated)
|
268
|
+
const roundMetrics = toolMetrics.rounds
|
269
|
+
.map((round) => {
|
270
|
+
const efficiency = round.toolCount > 0
|
271
|
+
? ((round.llmTime / (round.llmTime + round.toolTime)) *
|
272
|
+
100).toFixed(1)
|
273
|
+
: 100;
|
274
|
+
return `R${round.roundNumber}(LLM:${round.llmTime}ms,Tools:${round.toolTime}ms,Eff:${efficiency}%)`;
|
275
|
+
})
|
276
|
+
.join(" | ");
|
277
|
+
if (roundMetrics) {
|
278
|
+
console.log(`🔄 [OpenAI] Rounds: ${roundMetrics}`);
|
279
|
+
}
|
280
|
+
}
|
188
281
|
if (interTokenDelays.length > 0) {
|
189
282
|
const avgDelay = interTokenDelays.reduce((a, b) => a + b, 0) / interTokenDelays.length;
|
190
283
|
const sortedDelays = [...interTokenDelays].sort((a, b) => a - b);
|
191
284
|
const p50Delay = sortedDelays[Math.floor(sortedDelays.length * 0.5)];
|
192
285
|
const p95Delay = sortedDelays[Math.floor(sortedDelays.length * 0.95)];
|
193
286
|
const p99Delay = sortedDelays[Math.floor(sortedDelays.length * 0.99)];
|
194
|
-
console.log(
|
195
|
-
console.log(` 📊 P50 Delay: ${p50Delay}ms`);
|
196
|
-
console.log(` ⚠️ P95 Delay: ${p95Delay}ms`);
|
197
|
-
console.log(` 🚨 P99 Delay: ${p99Delay}ms`);
|
287
|
+
console.log(`⏳ [OpenAI] Inter-Token: Avg=${avgDelay.toFixed(2)}ms | P50=${p50Delay}ms | P95=${p95Delay}ms | P99=${p99Delay}ms`);
|
198
288
|
}
|
199
|
-
console.log(
|
289
|
+
console.log(`✅ [OpenAI] Final message (${fullMessage.length} chars): "${fullMessage}"`);
|
200
290
|
}
|
201
291
|
onComplete(fullMessage, toolCalls);
|
202
292
|
}
|
@@ -215,23 +305,29 @@ onEvent, onComplete) {
|
|
215
305
|
// Performance metrics
|
216
306
|
const startTime = Date.now();
|
217
307
|
let firstTokenTime = 0;
|
308
|
+
let firstMeaningfulContentTime = 0;
|
218
309
|
let tokenCount = 0;
|
310
|
+
let toolArgumentTokens = 0;
|
219
311
|
let lastEventTime = 0;
|
220
312
|
const interTokenDelays = [];
|
313
|
+
// Tool calling metrics
|
314
|
+
const toolMetrics = {
|
315
|
+
totalTools: 0,
|
316
|
+
successfulTools: 0,
|
317
|
+
failedTools: 0,
|
318
|
+
toolTimes: [],
|
319
|
+
currentToolStart: 0,
|
320
|
+
roundStartTime: startTime,
|
321
|
+
rounds: [],
|
322
|
+
currentRound: 1,
|
323
|
+
};
|
221
324
|
try {
|
222
325
|
const modelName = getModelName(specification);
|
223
326
|
if (!modelName) {
|
224
327
|
throw new Error(`No model name found for Anthropic specification: ${specification.name}`);
|
225
328
|
}
|
226
329
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
227
|
-
console.log(
|
228
|
-
console.log(" Service: Anthropic");
|
229
|
-
console.log(" Model:", modelName);
|
230
|
-
console.log(" Temperature:", specification.anthropic?.temperature);
|
231
|
-
console.log(" Max Tokens:", specification.anthropic?.completionTokenLimit || 8192);
|
232
|
-
console.log(" System Prompt:", systemPrompt ? "Yes" : "No");
|
233
|
-
console.log(" Tools:", tools?.length || 0);
|
234
|
-
console.log(" Specification Name:", specification.name);
|
330
|
+
console.log(`🤖 [Anthropic] Model Config: Service=Anthropic | Model=${modelName} | Temperature=${specification.anthropic?.temperature} | MaxTokens=${specification.anthropic?.completionTokenLimit || 8192} | SystemPrompt=${systemPrompt ? "Yes" : "No"} | Tools=${tools?.length || 0} | Spec="${specification.name}"`);
|
235
331
|
}
|
236
332
|
const streamConfig = {
|
237
333
|
model: modelName,
|
@@ -253,7 +349,7 @@ onEvent, onComplete) {
|
|
253
349
|
}));
|
254
350
|
}
|
255
351
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
256
|
-
console.log(
|
352
|
+
console.log(`⏱️ [Anthropic] Starting LLM call at: ${new Date().toISOString()}`);
|
257
353
|
}
|
258
354
|
const stream = await anthropicClient.messages.create(streamConfig);
|
259
355
|
let activeContentBlock = false;
|
@@ -271,6 +367,22 @@ onEvent, onComplete) {
|
|
271
367
|
arguments: "",
|
272
368
|
};
|
273
369
|
toolCalls.push(toolCall);
|
370
|
+
// Track tool metrics
|
371
|
+
toolMetrics.totalTools++;
|
372
|
+
toolMetrics.currentToolStart = Date.now();
|
373
|
+
toolMetrics.toolTimes.push({
|
374
|
+
name: toolCall.name,
|
375
|
+
startTime: toolMetrics.currentToolStart,
|
376
|
+
argumentBuildTime: 0,
|
377
|
+
totalTime: 0,
|
378
|
+
});
|
379
|
+
// Track TTFT for first tool if no content yet
|
380
|
+
if (firstTokenTime === 0) {
|
381
|
+
firstTokenTime = Date.now() - startTime;
|
382
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
383
|
+
console.log(`\n⚡ [Anthropic] Time to First Token (Tool Call): ${firstTokenTime}ms`);
|
384
|
+
}
|
385
|
+
}
|
274
386
|
onEvent({
|
275
387
|
type: "tool_call_start",
|
276
388
|
toolCall: {
|
@@ -285,13 +397,20 @@ onEvent, onComplete) {
|
|
285
397
|
fullMessage += chunk.delta.text;
|
286
398
|
tokenCount++;
|
287
399
|
const currentTime = Date.now();
|
288
|
-
// Track TTFT
|
400
|
+
// Track TTFT (first token regardless of type)
|
289
401
|
if (firstTokenTime === 0) {
|
290
402
|
firstTokenTime = currentTime - startTime;
|
291
403
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
292
404
|
console.log(`\n⚡ [Anthropic] Time to First Token (TTFT): ${firstTokenTime}ms`);
|
293
405
|
}
|
294
406
|
}
|
407
|
+
// Track first meaningful content (excludes tool calls)
|
408
|
+
if (firstMeaningfulContentTime === 0 && chunk.delta.text.trim()) {
|
409
|
+
firstMeaningfulContentTime = currentTime - startTime;
|
410
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
411
|
+
console.log(`\n🎯 [Anthropic] Time to First Meaningful Content: ${firstMeaningfulContentTime}ms`);
|
412
|
+
}
|
413
|
+
}
|
295
414
|
// Track inter-token delays
|
296
415
|
if (lastEventTime > 0) {
|
297
416
|
const delay = currentTime - lastEventTime;
|
@@ -311,6 +430,8 @@ onEvent, onComplete) {
|
|
311
430
|
const currentTool = toolCalls[toolCalls.length - 1];
|
312
431
|
if (currentTool) {
|
313
432
|
currentTool.arguments += chunk.delta.partial_json;
|
433
|
+
// Count tool argument tokens (rough estimate: ~4 chars per token)
|
434
|
+
toolArgumentTokens += Math.ceil(chunk.delta.partial_json.length / 4);
|
314
435
|
// Debug logging for partial JSON accumulation
|
315
436
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
316
437
|
console.log(`[Anthropic] Tool ${currentTool.name} - Partial JSON chunk: "${chunk.delta.partial_json}"`);
|
@@ -329,6 +450,27 @@ onEvent, onComplete) {
|
|
329
450
|
// Tool call complete
|
330
451
|
const currentTool = toolCalls[toolCalls.length - 1];
|
331
452
|
if (currentTool) {
|
453
|
+
const currentTime = Date.now();
|
454
|
+
// Update tool metrics
|
455
|
+
const toolIndex = toolCalls.length - 1;
|
456
|
+
if (toolIndex < toolMetrics.toolTimes.length) {
|
457
|
+
const toolTime = toolMetrics.toolTimes[toolIndex];
|
458
|
+
toolTime.argumentBuildTime = currentTime - toolTime.startTime;
|
459
|
+
toolTime.totalTime = toolTime.argumentBuildTime;
|
460
|
+
toolTime.name = currentTool.name;
|
461
|
+
}
|
462
|
+
// Track tool success/failure
|
463
|
+
try {
|
464
|
+
JSON.parse(currentTool.arguments);
|
465
|
+
toolMetrics.successfulTools++;
|
466
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
467
|
+
console.log(`[Anthropic] ✅ Valid JSON for ${currentTool.name}`);
|
468
|
+
}
|
469
|
+
}
|
470
|
+
catch (e) {
|
471
|
+
toolMetrics.failedTools++;
|
472
|
+
console.error(`[Anthropic] ❌ Invalid JSON for ${currentTool.name}: ${e}`);
|
473
|
+
}
|
332
474
|
// Log the final JSON for debugging
|
333
475
|
if (process.env.DEBUG_GRAPHLIT_STREAMING ||
|
334
476
|
!isValidJSON(currentTool.arguments)) {
|
@@ -340,16 +482,6 @@ onEvent, onComplete) {
|
|
340
482
|
currentTool.arguments.length > 100) {
|
341
483
|
console.warn(`[Anthropic] WARNING: JSON may be truncated - doesn't end with '}': ...${lastChars}`);
|
342
484
|
}
|
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
485
|
}
|
354
486
|
onEvent({
|
355
487
|
type: "tool_call_complete",
|
@@ -399,27 +531,72 @@ onEvent, onComplete) {
|
|
399
531
|
console.log(`[Anthropic] Filtered out ${toolCalls.length - validToolCalls.length} incomplete tool calls`);
|
400
532
|
console.log(`[Anthropic] Successfully processed ${validToolCalls.length} valid tool calls`);
|
401
533
|
}
|
402
|
-
// Calculate final metrics
|
534
|
+
// Calculate final metrics including tool calling insights
|
403
535
|
const totalTime = Date.now() - startTime;
|
404
|
-
const
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
536
|
+
const totalTokens = tokenCount + toolArgumentTokens;
|
537
|
+
const tokensPerSecond = totalTokens > 0 ? totalTokens / (totalTime / 1000) : 0;
|
538
|
+
// Finalize round metrics
|
539
|
+
if (validToolCalls.length > 0) {
|
540
|
+
const roundEndTime = Date.now();
|
541
|
+
const totalToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0);
|
542
|
+
const llmTime = totalTime - totalToolTime;
|
543
|
+
toolMetrics.rounds.push({
|
544
|
+
roundNumber: toolMetrics.currentRound,
|
545
|
+
llmTime: llmTime,
|
546
|
+
toolTime: totalToolTime,
|
547
|
+
toolCount: validToolCalls.length,
|
548
|
+
});
|
549
|
+
}
|
550
|
+
if (process.env.DEBUG_GRAPHLIT_METRICS) {
|
551
|
+
const metricsData = {
|
552
|
+
totalTime: `${totalTime}ms`,
|
553
|
+
ttft: `${firstTokenTime}ms`,
|
554
|
+
ttfmc: firstMeaningfulContentTime > 0
|
555
|
+
? `${firstMeaningfulContentTime}ms`
|
556
|
+
: null,
|
557
|
+
contentTokens: tokenCount,
|
558
|
+
toolTokens: toolArgumentTokens,
|
559
|
+
totalTokens: totalTokens,
|
560
|
+
tps: tokensPerSecond.toFixed(2),
|
561
|
+
};
|
562
|
+
console.log(`📊 [Anthropic] Performance: Total=${metricsData.totalTime} | TTFT=${metricsData.ttft}${metricsData.ttfmc ? ` | TTFMC=${metricsData.ttfmc}` : ""} | Tokens(content/tool/total)=${metricsData.contentTokens}/${metricsData.toolTokens}/${metricsData.totalTokens} | TPS=${metricsData.tps}`);
|
563
|
+
// Tool calling metrics
|
564
|
+
if (validToolCalls.length > 0) {
|
565
|
+
const successRate = ((toolMetrics.successfulTools / toolMetrics.totalTools) *
|
566
|
+
100).toFixed(1);
|
567
|
+
const avgToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0) /
|
568
|
+
toolMetrics.toolTimes.length;
|
569
|
+
console.log(`🔧 [Anthropic] Tools: Total=${toolMetrics.totalTools} | Success=${toolMetrics.successfulTools} | Failed=${toolMetrics.failedTools} | SuccessRate=${successRate}% | AvgTime=${avgToolTime.toFixed(2)}ms`);
|
570
|
+
// Tool timing details (consolidated)
|
571
|
+
const toolTimings = toolMetrics.toolTimes
|
572
|
+
.map((tool, idx) => `${tool.name}:${tool.argumentBuildTime}ms`)
|
573
|
+
.join(" | ");
|
574
|
+
if (toolTimings) {
|
575
|
+
console.log(`🔨 [Anthropic] Tool Timings: ${toolTimings}`);
|
576
|
+
}
|
577
|
+
// Round metrics (consolidated)
|
578
|
+
const roundMetrics = toolMetrics.rounds
|
579
|
+
.map((round) => {
|
580
|
+
const efficiency = round.toolCount > 0
|
581
|
+
? ((round.llmTime / (round.llmTime + round.toolTime)) *
|
582
|
+
100).toFixed(1)
|
583
|
+
: 100;
|
584
|
+
return `R${round.roundNumber}(LLM:${round.llmTime}ms,Tools:${round.toolTime}ms,Eff:${efficiency}%)`;
|
585
|
+
})
|
586
|
+
.join(" | ");
|
587
|
+
if (roundMetrics) {
|
588
|
+
console.log(`🔄 [Anthropic] Rounds: ${roundMetrics}`);
|
589
|
+
}
|
590
|
+
}
|
411
591
|
if (interTokenDelays.length > 0) {
|
412
592
|
const avgDelay = interTokenDelays.reduce((a, b) => a + b, 0) / interTokenDelays.length;
|
413
593
|
const sortedDelays = [...interTokenDelays].sort((a, b) => a - b);
|
414
594
|
const p50Delay = sortedDelays[Math.floor(sortedDelays.length * 0.5)];
|
415
595
|
const p95Delay = sortedDelays[Math.floor(sortedDelays.length * 0.95)];
|
416
596
|
const p99Delay = sortedDelays[Math.floor(sortedDelays.length * 0.99)];
|
417
|
-
console.log(
|
418
|
-
console.log(` 📊 P50 Delay: ${p50Delay}ms`);
|
419
|
-
console.log(` ⚠️ P95 Delay: ${p95Delay}ms`);
|
420
|
-
console.log(` 🚨 P99 Delay: ${p99Delay}ms`);
|
597
|
+
console.log(`⏳ [Anthropic] Inter-Token: Avg=${avgDelay.toFixed(2)}ms | P50=${p50Delay}ms | P95=${p95Delay}ms | P99=${p99Delay}ms`);
|
421
598
|
}
|
422
|
-
console.log(
|
599
|
+
console.log(`✅ [Anthropic] Final message (${fullMessage.length} chars): "${fullMessage}"`);
|
423
600
|
}
|
424
601
|
onComplete(fullMessage, validToolCalls);
|
425
602
|
}
|
@@ -438,23 +615,29 @@ onEvent, onComplete) {
|
|
438
615
|
// Performance metrics
|
439
616
|
const startTime = Date.now();
|
440
617
|
let firstTokenTime = 0;
|
618
|
+
let firstMeaningfulContentTime = 0;
|
441
619
|
let tokenCount = 0;
|
620
|
+
let toolArgumentTokens = 0;
|
442
621
|
let lastEventTime = 0;
|
443
622
|
const interTokenDelays = [];
|
623
|
+
// Tool calling metrics
|
624
|
+
const toolMetrics = {
|
625
|
+
totalTools: 0,
|
626
|
+
successfulTools: 0,
|
627
|
+
failedTools: 0,
|
628
|
+
toolTimes: [],
|
629
|
+
currentToolStart: 0,
|
630
|
+
roundStartTime: startTime,
|
631
|
+
rounds: [],
|
632
|
+
currentRound: 1,
|
633
|
+
};
|
444
634
|
try {
|
445
635
|
const modelName = getModelName(specification);
|
446
636
|
if (!modelName) {
|
447
637
|
throw new Error(`No model name found for Google specification: ${specification.name}`);
|
448
638
|
}
|
449
639
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
450
|
-
console.log(
|
451
|
-
console.log(" Service: Google");
|
452
|
-
console.log(" Model:", modelName);
|
453
|
-
console.log(" Temperature:", specification.google?.temperature);
|
454
|
-
console.log(" Max Tokens:", specification.google?.completionTokenLimit);
|
455
|
-
console.log(" System Prompt:", systemPrompt ? "Yes" : "No");
|
456
|
-
console.log(" Tools:", tools?.length || 0);
|
457
|
-
console.log(" Specification Name:", specification.name);
|
640
|
+
console.log(`🤖 [Google] Model Config: Service=Google | Model=${modelName} | Temperature=${specification.google?.temperature} | MaxTokens=${specification.google?.completionTokenLimit || "null"} | SystemPrompt=${systemPrompt ? "Yes" : "No"} | Tools=${tools?.length || 0} | Spec="${specification.name}"`);
|
458
641
|
}
|
459
642
|
const streamConfig = {
|
460
643
|
model: modelName,
|
@@ -514,6 +697,22 @@ onEvent, onComplete) {
|
|
514
697
|
}
|
515
698
|
if (text) {
|
516
699
|
fullMessage += text;
|
700
|
+
tokenCount++;
|
701
|
+
const currentTime = Date.now();
|
702
|
+
// Track TTFT (first token regardless of type)
|
703
|
+
if (firstTokenTime === 0) {
|
704
|
+
firstTokenTime = currentTime - startTime;
|
705
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
706
|
+
console.log(`\n⚡ [Google] Time to First Token (TTFT): ${firstTokenTime}ms`);
|
707
|
+
}
|
708
|
+
}
|
709
|
+
// Track first meaningful content
|
710
|
+
if (firstMeaningfulContentTime === 0 && text.trim()) {
|
711
|
+
firstMeaningfulContentTime = currentTime - startTime;
|
712
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
713
|
+
console.log(`\n🎯 [Google] Time to First Meaningful Content: ${firstMeaningfulContentTime}ms`);
|
714
|
+
}
|
715
|
+
}
|
517
716
|
onEvent({
|
518
717
|
type: "token",
|
519
718
|
token: text,
|
@@ -536,6 +735,23 @@ onEvent, onComplete) {
|
|
536
735
|
arguments: JSON.stringify(part.functionCall.args || {}),
|
537
736
|
};
|
538
737
|
toolCalls.push(toolCall);
|
738
|
+
// Track tool metrics
|
739
|
+
toolMetrics.totalTools++;
|
740
|
+
const argumentString = JSON.stringify(part.functionCall.args || {});
|
741
|
+
toolArgumentTokens += Math.ceil(argumentString.length / 4);
|
742
|
+
toolMetrics.toolTimes.push({
|
743
|
+
name: part.functionCall.name,
|
744
|
+
startTime: Date.now(),
|
745
|
+
argumentBuildTime: 0, // Google returns complete args at once
|
746
|
+
totalTime: 0,
|
747
|
+
});
|
748
|
+
// Track TTFT for first tool if no content yet
|
749
|
+
if (firstTokenTime === 0) {
|
750
|
+
firstTokenTime = Date.now() - startTime;
|
751
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
752
|
+
console.log(`\n⚡ [Google] Time to First Token (Tool Call): ${firstTokenTime}ms`);
|
753
|
+
}
|
754
|
+
}
|
539
755
|
// Emit tool call events
|
540
756
|
onEvent({
|
541
757
|
type: "tool_call_start",
|
@@ -549,18 +765,28 @@ onEvent, onComplete) {
|
|
549
765
|
toolCallId: toolCall.id,
|
550
766
|
argumentDelta: toolCall.arguments,
|
551
767
|
});
|
552
|
-
//
|
768
|
+
// Update tool metrics and validate JSON
|
769
|
+
const toolIndex = toolCalls.length - 1;
|
770
|
+
if (toolIndex < toolMetrics.toolTimes.length) {
|
771
|
+
const toolTime = toolMetrics.toolTimes[toolIndex];
|
772
|
+
toolTime.totalTime = Date.now() - toolTime.startTime;
|
773
|
+
toolTime.argumentBuildTime = toolTime.totalTime; // Google returns complete args
|
774
|
+
}
|
775
|
+
try {
|
776
|
+
JSON.parse(toolCall.arguments);
|
777
|
+
toolMetrics.successfulTools++;
|
778
|
+
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
779
|
+
console.log(`[Google] ✅ Valid JSON for ${toolCall.name}`);
|
780
|
+
}
|
781
|
+
}
|
782
|
+
catch (e) {
|
783
|
+
toolMetrics.failedTools++;
|
784
|
+
console.error(`[Google] ❌ Invalid JSON for ${toolCall.name}: ${e}`);
|
785
|
+
}
|
786
|
+
// Log completion
|
553
787
|
if (process.env.DEBUG_GRAPHLIT_STREAMING) {
|
554
788
|
console.log(`[Google] Tool ${toolCall.name} complete with arguments (${toolCall.arguments.length} chars):`);
|
555
789
|
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
790
|
}
|
565
791
|
onEvent({
|
566
792
|
type: "tool_call_complete",
|
@@ -647,27 +873,72 @@ onEvent, onComplete) {
|
|
647
873
|
if (process.env.DEBUG_GRAPHLIT_STREAMING && toolCalls.length > 0) {
|
648
874
|
console.log(`[Google] Successfully processed ${toolCalls.length} tool calls`);
|
649
875
|
}
|
650
|
-
// Calculate final metrics
|
876
|
+
// Calculate final metrics including tool calling insights
|
651
877
|
const totalTime = Date.now() - startTime;
|
652
|
-
const
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
878
|
+
const totalTokens = tokenCount + toolArgumentTokens;
|
879
|
+
const tokensPerSecond = totalTokens > 0 ? totalTokens / (totalTime / 1000) : 0;
|
880
|
+
// Finalize round metrics
|
881
|
+
if (toolCalls.length > 0) {
|
882
|
+
const roundEndTime = Date.now();
|
883
|
+
const totalToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0);
|
884
|
+
const llmTime = totalTime - totalToolTime;
|
885
|
+
toolMetrics.rounds.push({
|
886
|
+
roundNumber: toolMetrics.currentRound,
|
887
|
+
llmTime: llmTime,
|
888
|
+
toolTime: totalToolTime,
|
889
|
+
toolCount: toolCalls.length,
|
890
|
+
});
|
891
|
+
}
|
892
|
+
if (process.env.DEBUG_GRAPHLIT_METRICS) {
|
893
|
+
const metricsData = {
|
894
|
+
totalTime: `${totalTime}ms`,
|
895
|
+
ttft: `${firstTokenTime}ms`,
|
896
|
+
ttfmc: firstMeaningfulContentTime > 0
|
897
|
+
? `${firstMeaningfulContentTime}ms`
|
898
|
+
: null,
|
899
|
+
contentTokens: tokenCount,
|
900
|
+
toolTokens: toolArgumentTokens,
|
901
|
+
totalTokens: totalTokens,
|
902
|
+
tps: tokensPerSecond.toFixed(2),
|
903
|
+
};
|
904
|
+
console.log(`📊 [Google] Performance: Total=${metricsData.totalTime} | TTFT=${metricsData.ttft}${metricsData.ttfmc ? ` | TTFMC=${metricsData.ttfmc}` : ""} | Tokens(content/tool/total)=${metricsData.contentTokens}/${metricsData.toolTokens}/${metricsData.totalTokens} | TPS=${metricsData.tps}`);
|
905
|
+
// Tool calling metrics
|
906
|
+
if (toolCalls.length > 0) {
|
907
|
+
const successRate = ((toolMetrics.successfulTools / toolMetrics.totalTools) *
|
908
|
+
100).toFixed(1);
|
909
|
+
const avgToolTime = toolMetrics.toolTimes.reduce((sum, tool) => sum + tool.totalTime, 0) /
|
910
|
+
toolMetrics.toolTimes.length;
|
911
|
+
console.log(`🔧 [Google] Tools: Total=${toolMetrics.totalTools} | Success=${toolMetrics.successfulTools} | Failed=${toolMetrics.failedTools} | SuccessRate=${successRate}% | AvgTime=${avgToolTime.toFixed(2)}ms`);
|
912
|
+
// Tool timing details (consolidated)
|
913
|
+
const toolTimings = toolMetrics.toolTimes
|
914
|
+
.map((tool, idx) => `${tool.name}:${tool.argumentBuildTime}ms`)
|
915
|
+
.join(" | ");
|
916
|
+
if (toolTimings) {
|
917
|
+
console.log(`🔨 [Google] Tool Timings: ${toolTimings}`);
|
918
|
+
}
|
919
|
+
// Round metrics (consolidated)
|
920
|
+
const roundMetrics = toolMetrics.rounds
|
921
|
+
.map((round) => {
|
922
|
+
const efficiency = round.toolCount > 0
|
923
|
+
? ((round.llmTime / (round.llmTime + round.toolTime)) *
|
924
|
+
100).toFixed(1)
|
925
|
+
: 100;
|
926
|
+
return `R${round.roundNumber}(LLM:${round.llmTime}ms,Tools:${round.toolTime}ms,Eff:${efficiency}%)`;
|
927
|
+
})
|
928
|
+
.join(" | ");
|
929
|
+
if (roundMetrics) {
|
930
|
+
console.log(`🔄 [Google] Rounds: ${roundMetrics}`);
|
931
|
+
}
|
932
|
+
}
|
659
933
|
if (interTokenDelays.length > 0) {
|
660
934
|
const avgDelay = interTokenDelays.reduce((a, b) => a + b, 0) / interTokenDelays.length;
|
661
935
|
const sortedDelays = [...interTokenDelays].sort((a, b) => a - b);
|
662
936
|
const p50Delay = sortedDelays[Math.floor(sortedDelays.length * 0.5)];
|
663
937
|
const p95Delay = sortedDelays[Math.floor(sortedDelays.length * 0.95)];
|
664
938
|
const p99Delay = sortedDelays[Math.floor(sortedDelays.length * 0.99)];
|
665
|
-
console.log(
|
666
|
-
console.log(` 📊 P50 Delay: ${p50Delay}ms`);
|
667
|
-
console.log(` ⚠️ P95 Delay: ${p95Delay}ms`);
|
668
|
-
console.log(` 🚨 P99 Delay: ${p99Delay}ms`);
|
939
|
+
console.log(`⏳ [Google] Inter-Token: Avg=${avgDelay.toFixed(2)}ms | P50=${p50Delay}ms | P95=${p95Delay}ms | P99=${p99Delay}ms`);
|
669
940
|
}
|
670
|
-
console.log(
|
941
|
+
console.log(`✅ [Google] Final message (${fullMessage.length} chars): "${fullMessage}"`);
|
671
942
|
}
|
672
943
|
onComplete(fullMessage, toolCalls);
|
673
944
|
}
|