graphlit-client 1.0.20250613001 → 1.0.20250613002

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
@@ -1983,14 +1983,21 @@ class Graphlit {
1983
1983
  }
1984
1984
  currentRound++;
1985
1985
  }
1986
- // Complete the conversation
1986
+ // Complete the conversation and get token count
1987
+ let finalTokens;
1987
1988
  if (fullMessage) {
1988
- await this.completeConversation(fullMessage.trim(), conversationId, correlationId);
1989
+ const completeResponse = await this.completeConversation(fullMessage.trim(), conversationId, correlationId);
1990
+ // Extract token count from the response
1991
+ finalTokens = completeResponse.completeConversation?.message?.tokens ?? undefined;
1992
+ if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
1993
+ console.log(`📊 [completeConversation] Tokens used: ${finalTokens || 'unknown'}`);
1994
+ }
1989
1995
  }
1990
- // Emit completion event
1996
+ // Emit completion event with token count
1991
1997
  uiAdapter.handleEvent({
1992
1998
  type: "complete",
1993
1999
  conversationId,
2000
+ tokens: finalTokens,
1994
2001
  });
1995
2002
  }
1996
2003
  /**
@@ -13,9 +13,11 @@ export declare class UIEventAdapter {
13
13
  private tokenCount;
14
14
  private currentMessage;
15
15
  private isStreaming;
16
+ private conversationStartTime;
16
17
  private streamStartTime;
17
18
  private firstTokenTime;
18
19
  private lastTokenTime;
20
+ private tokenDelays;
19
21
  private activeToolCalls;
20
22
  private lastUpdateTime;
21
23
  private updateTimer?;
@@ -12,9 +12,11 @@ export class UIEventAdapter {
12
12
  tokenCount = 0;
13
13
  currentMessage = "";
14
14
  isStreaming = false;
15
- streamStartTime = 0;
15
+ conversationStartTime = 0; // When user sent the message
16
+ streamStartTime = 0; // When streaming actually began
16
17
  firstTokenTime = 0;
17
18
  lastTokenTime = 0;
19
+ tokenDelays = [];
18
20
  activeToolCalls = new Map();
19
21
  lastUpdateTime = 0;
20
22
  updateTimer;
@@ -27,6 +29,7 @@ export class UIEventAdapter {
27
29
  this.smoothingDelay = options.smoothingDelay ?? 30;
28
30
  this.model = options.model;
29
31
  this.modelService = options.modelService;
32
+ this.conversationStartTime = Date.now(); // Capture when conversation began
30
33
  if (options.smoothingEnabled) {
31
34
  this.chunkBuffer = new ChunkBuffer(options.chunkingStrategy || "word");
32
35
  }
@@ -55,7 +58,7 @@ export class UIEventAdapter {
55
58
  this.handleToolCallComplete(event.toolCall);
56
59
  break;
57
60
  case "complete":
58
- this.handleComplete();
61
+ this.handleComplete(event.tokens);
59
62
  break;
60
63
  case "error":
61
64
  this.handleError(event.error);
@@ -94,6 +97,8 @@ export class UIEventAdapter {
94
97
  this.streamStartTime = Date.now();
95
98
  this.firstTokenTime = 0;
96
99
  this.lastTokenTime = 0;
100
+ this.tokenCount = 0;
101
+ this.tokenDelays = [];
97
102
  this.emitUIEvent({
98
103
  type: "conversation_started",
99
104
  conversationId,
@@ -107,7 +112,12 @@ export class UIEventAdapter {
107
112
  if (this.firstTokenTime === 0) {
108
113
  this.firstTokenTime = now;
109
114
  }
115
+ // Track inter-token delays
116
+ if (this.lastTokenTime > 0) {
117
+ this.tokenDelays.push(now - this.lastTokenTime);
118
+ }
110
119
  this.lastTokenTime = now;
120
+ this.tokenCount++;
111
121
  if (this.chunkBuffer) {
112
122
  const chunks = this.chunkBuffer.addToken(token);
113
123
  // Add chunks to queue for all chunking modes (character, word, sentence)
@@ -165,7 +175,7 @@ export class UIEventAdapter {
165
175
  });
166
176
  }
167
177
  }
168
- handleComplete() {
178
+ handleComplete(tokens) {
169
179
  // Clear any pending updates
170
180
  if (this.updateTimer) {
171
181
  globalThis.clearTimeout(this.updateTimer);
@@ -200,7 +210,7 @@ export class UIEventAdapter {
200
210
  role: ConversationRoleTypes.Assistant,
201
211
  message: this.currentMessage,
202
212
  timestamp: new Date().toISOString(),
203
- tokens: undefined, // Will be set by caller if available
213
+ tokens: tokens, // Now we have the actual LLM token count!
204
214
  toolCalls: Array.from(this.activeToolCalls.values()).map((t) => t.toolCall),
205
215
  model: this.model,
206
216
  modelService: this.modelService,
@@ -222,9 +232,32 @@ export class UIEventAdapter {
222
232
  }
223
233
  }
224
234
  }
235
+ // Build final metrics
236
+ const completionTime = Date.now();
237
+ const finalMetrics = {
238
+ totalTime: this.streamStartTime > 0 ? completionTime - this.streamStartTime : 0,
239
+ conversationDuration: this.conversationStartTime > 0 ? completionTime - this.conversationStartTime : 0,
240
+ };
241
+ // Add TTFT if we have it
242
+ if (this.firstTokenTime > 0 && this.streamStartTime > 0) {
243
+ finalMetrics.ttft = this.firstTokenTime - this.streamStartTime;
244
+ }
245
+ // Add token counts
246
+ if (this.tokenCount > 0) {
247
+ finalMetrics.tokenCount = this.tokenCount; // Streaming chunks
248
+ }
249
+ if (tokens) {
250
+ finalMetrics.llmTokens = tokens; // Actual LLM tokens used
251
+ }
252
+ // Calculate average token delay
253
+ if (this.tokenDelays.length > 0) {
254
+ const avgDelay = this.tokenDelays.reduce((a, b) => a + b, 0) / this.tokenDelays.length;
255
+ finalMetrics.avgTokenDelay = Math.round(avgDelay);
256
+ }
225
257
  this.emitUIEvent({
226
258
  type: "conversation_completed",
227
259
  message: finalMessage,
260
+ metrics: finalMetrics,
228
261
  });
229
262
  }
230
263
  handleError(error) {
@@ -330,10 +363,30 @@ export class UIEventAdapter {
330
363
  message.completionTime = elapsedTime / 1000;
331
364
  }
332
365
  }
366
+ // Build metrics object
367
+ const now = Date.now();
368
+ const metrics = {
369
+ elapsedTime: this.streamStartTime > 0 ? now - this.streamStartTime : 0,
370
+ conversationDuration: this.conversationStartTime > 0 ? now - this.conversationStartTime : 0,
371
+ };
372
+ // Add TTFT if we have it
373
+ if (this.firstTokenTime > 0 && this.streamStartTime > 0) {
374
+ metrics.ttft = this.firstTokenTime - this.streamStartTime;
375
+ }
376
+ // Add token count if available
377
+ if (this.tokenCount > 0) {
378
+ metrics.tokenCount = this.tokenCount;
379
+ }
380
+ // Calculate average token delay
381
+ if (this.tokenDelays.length > 0) {
382
+ const avgDelay = this.tokenDelays.reduce((a, b) => a + b, 0) / this.tokenDelays.length;
383
+ metrics.avgTokenDelay = Math.round(avgDelay);
384
+ }
333
385
  this.emitUIEvent({
334
386
  type: "message_update",
335
387
  message,
336
388
  isStreaming,
389
+ metrics,
337
390
  });
338
391
  }
339
392
  emitUIEvent(event) {
@@ -36,6 +36,7 @@ export type StreamEvent = {
36
36
  type: "complete";
37
37
  messageId?: string;
38
38
  conversationId?: string;
39
+ tokens?: number;
39
40
  } | {
40
41
  type: "error";
41
42
  error: string;
@@ -17,6 +17,13 @@ export type AgentStreamEvent = {
17
17
  message: string;
18
18
  };
19
19
  isStreaming: boolean;
20
+ metrics?: {
21
+ ttft?: number;
22
+ elapsedTime: number;
23
+ conversationDuration: number;
24
+ tokenCount?: number;
25
+ avgTokenDelay?: number;
26
+ };
20
27
  } | {
21
28
  type: "tool_update";
22
29
  toolCall: ConversationToolCall;
@@ -26,6 +33,14 @@ export type AgentStreamEvent = {
26
33
  } | {
27
34
  type: "conversation_completed";
28
35
  message: ConversationMessage;
36
+ metrics?: {
37
+ ttft?: number;
38
+ totalTime: number;
39
+ conversationDuration: number;
40
+ tokenCount?: number;
41
+ llmTokens?: number;
42
+ avgTokenDelay?: number;
43
+ };
29
44
  } | {
30
45
  type: "error";
31
46
  error: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphlit-client",
3
- "version": "1.0.20250613001",
3
+ "version": "1.0.20250613002",
4
4
  "description": "Graphlit API Client for TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/client.js",