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.DEBUG_GRAPHLIT_STREAMING) {
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.DEBUG_GRAPHLIT_STREAMING) {
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.DEBUG_GRAPHLIT_STREAMING) {
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.DEBUG_GRAPHLIT_STREAMING) {
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.DEBUG_GRAPHLIT_STREAMING) {
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.DEBUG_GRAPHLIT_STREAMING) {
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.DEBUG_GRAPHLIT_STREAMING) {
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
- // Check streaming support
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 && (OpenAI || this.openaiClient)) {
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.DEBUG_GRAPHLIT_STREAMING) {
1701
- console.log("\n🔍 [OpenAI] Formatted messages being sent to LLM:");
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.DEBUG_GRAPHLIT_STREAMING) {
1720
- console.log("\n🔍 [Anthropic] Formatted messages being sent to LLM:");
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.DEBUG_GRAPHLIT_STREAMING) {
1740
- console.log("\n🔍 [Google] Formatted messages being sent to LLM:");
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(`\n🔄 [Fallback] Starting non-streaming fallback`);
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(`\n✅ [Fallback] promptConversation completed`);
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 ? new OpenAI({
2047
- apiKey: process.env.OPENAI_API_KEY || "",
2048
- }) : (() => { throw new Error("OpenAI module not available"); })());
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("\n🚀 [Graphlit SDK] Routing to OpenAI streaming provider");
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 ? new Anthropic({
2068
- apiKey: process.env.ANTHROPIC_API_KEY || "",
2069
- }) : (() => { throw new Error("Anthropic module not available"); })());
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("\n🚀 [Graphlit SDK] Routing to Anthropic streaming provider");
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 ? new GoogleGenerativeAI(process.env.GOOGLE_API_KEY || "") : (() => { throw new Error("Google GenerativeAI module not available"); })());
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("\n🚀 [Graphlit SDK] Routing to Google streaming provider");
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("\n🤖 [OpenAI] Model Configuration:");
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("\n⏱️ [OpenAI] Starting LLM call at:", new Date().toISOString());
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 (const toolCall of toolCalls) {
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 tokensPerSecond = tokenCount > 0 ? tokenCount / (totalTime / 1000) : 0;
182
- if (process.env.DEBUG_GRAPHLIT_STREAMING) {
183
- console.log("\n📊 [OpenAI] Performance Metrics:");
184
- console.log(` ⏱️ Total Time: ${totalTime}ms`);
185
- console.log(` ⚡ Time to First Token (TTFT): ${firstTokenTime}ms`);
186
- console.log(` 📈 Tokens Generated: ${tokenCount}`);
187
- console.log(` 💨 Tokens Per Second (TPS): ${tokensPerSecond.toFixed(2)}`);
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(` ⏳ Average Inter-Token Delay: ${avgDelay.toFixed(2)}ms`);
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(`\n✅ [OpenAI] Final message (${fullMessage.length} chars): "${fullMessage}"`);
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("\n🤖 [Anthropic] Model Configuration:");
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("\n⏱️ [Anthropic] Starting LLM call at:", new Date().toISOString());
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 tokensPerSecond = tokenCount > 0 ? tokenCount / (totalTime / 1000) : 0;
405
- if (process.env.DEBUG_GRAPHLIT_STREAMING) {
406
- console.log("\n📊 [Anthropic] Performance Metrics:");
407
- console.log(` ⏱️ Total Time: ${totalTime}ms`);
408
- console.log(` ⚡ Time to First Token (TTFT): ${firstTokenTime}ms`);
409
- console.log(` 📈 Tokens Generated: ${tokenCount}`);
410
- console.log(` 💨 Tokens Per Second (TPS): ${tokensPerSecond.toFixed(2)}`);
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(` ⏳ Average Inter-Token Delay: ${avgDelay.toFixed(2)}ms`);
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(`\n✅ [Anthropic] Final message (${fullMessage.length} chars): "${fullMessage}"`);
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("\n🤖 [Google] Model Configuration:");
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
- // Log completion and validate JSON
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 tokensPerSecond = tokenCount > 0 ? tokenCount / (totalTime / 1000) : 0;
653
- if (process.env.DEBUG_GRAPHLIT_STREAMING) {
654
- console.log("\n📊 [Google] Performance Metrics:");
655
- console.log(` ⏱️ Total Time: ${totalTime}ms`);
656
- console.log(` ⚡ Time to First Token (TTFT): ${firstTokenTime}ms`);
657
- console.log(` 📈 Tokens Generated: ${tokenCount}`);
658
- console.log(` 💨 Tokens Per Second (TPS): ${tokensPerSecond.toFixed(2)}`);
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(` ⏳ Average Inter-Token Delay: ${avgDelay.toFixed(2)}ms`);
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(`\n✅ [Google] Final message (${fullMessage.length} chars): "${fullMessage}"`);
941
+ console.log(`✅ [Google] Final message (${fullMessage.length} chars): "${fullMessage}"`);
671
942
  }
672
943
  onComplete(fullMessage, toolCalls);
673
944
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphlit-client",
3
- "version": "1.0.20250612002",
3
+ "version": "1.0.20250612004",
4
4
  "description": "Graphlit API Client for TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/client.js",