graphlit-client 1.0.20250615004 → 1.0.20250615006

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
@@ -1697,12 +1697,28 @@ class Graphlit {
1697
1697
  }
1698
1698
  // 2. Initial prompt
1699
1699
  const promptResponse = await this.promptConversation(prompt, actualConversationId, specification, mimeType, data, tools, false, // requireTool
1700
- false, // includeDetails
1700
+ true, // includeDetails - needed for context window tracking
1701
1701
  correlationId);
1702
1702
  let currentMessage = promptResponse.promptConversation?.message;
1703
1703
  if (!currentMessage) {
1704
1704
  throw new Error("No message in prompt response");
1705
1705
  }
1706
+ // Calculate and return context window usage in result
1707
+ const details = promptResponse.promptConversation?.details;
1708
+ let contextWindowUsage;
1709
+ if (details?.tokenLimit && details?.messages) {
1710
+ // Sum up all message tokens
1711
+ const usedTokens = details.messages.reduce((sum, msg) => sum + (msg?.tokens || 0), 0);
1712
+ contextWindowUsage = {
1713
+ usedTokens,
1714
+ maxTokens: details.tokenLimit,
1715
+ percentage: Math.round((usedTokens / details.tokenLimit) * 100),
1716
+ remainingTokens: Math.max(0, details.tokenLimit - usedTokens),
1717
+ };
1718
+ if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
1719
+ console.log(`📊 [Context Window] Using ${usedTokens.toLocaleString()}/${details.tokenLimit.toLocaleString()} tokens (${Math.round((usedTokens / details.tokenLimit) * 100)}%)`);
1720
+ }
1721
+ }
1706
1722
  // 3. Tool calling loop
1707
1723
  const allToolCalls = [];
1708
1724
  let rounds = 0;
@@ -1759,6 +1775,7 @@ class Graphlit {
1759
1775
  toolResults: allToolCalls,
1760
1776
  metrics,
1761
1777
  usage,
1778
+ contextWindow: contextWindowUsage,
1762
1779
  };
1763
1780
  }
1764
1781
  catch (error) {
@@ -1956,6 +1973,24 @@ class Graphlit {
1956
1973
  });
1957
1974
  }
1958
1975
  }
1976
+ // Emit context window usage event
1977
+ const details = formatResponse.formatConversation?.details;
1978
+ if (details?.tokenLimit && details?.messages) {
1979
+ // Sum up all message tokens
1980
+ const usedTokens = details.messages.reduce((sum, msg) => sum + (msg?.tokens || 0), 0);
1981
+ uiAdapter.handleEvent({
1982
+ type: "context_window",
1983
+ usage: {
1984
+ usedTokens,
1985
+ maxTokens: details.tokenLimit,
1986
+ percentage: Math.round((usedTokens / details.tokenLimit) * 100),
1987
+ remainingTokens: Math.max(0, details.tokenLimit - usedTokens),
1988
+ },
1989
+ });
1990
+ if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
1991
+ console.log(`📊 [Context Window] Using ${usedTokens.toLocaleString()}/${details.tokenLimit.toLocaleString()} tokens (${Math.round((usedTokens / details.tokenLimit) * 100)}%)`);
1992
+ }
1993
+ }
1959
1994
  // Build message array with conversation history
1960
1995
  const messages = [];
1961
1996
  // Add system prompt if specified
@@ -11,6 +11,36 @@ function isValidJSON(str) {
11
11
  return false;
12
12
  }
13
13
  }
14
+ /**
15
+ * Simplify schema for Groq by removing complex features that may cause issues
16
+ */
17
+ function simplifySchemaForGroq(schema) {
18
+ if (typeof schema !== "object" || schema === null) {
19
+ return JSON.stringify(schema);
20
+ }
21
+ // Remove complex JSON Schema features that Groq might not support
22
+ const simplified = {
23
+ type: schema.type || "object",
24
+ properties: {},
25
+ required: schema.required || []
26
+ };
27
+ // Only keep basic properties and types
28
+ if (schema.properties) {
29
+ for (const [key, value] of Object.entries(schema.properties)) {
30
+ const prop = value;
31
+ simplified.properties[key] = {
32
+ type: prop.type || "string",
33
+ description: prop.description || "",
34
+ // Remove complex features like patterns, formats, etc.
35
+ };
36
+ // Keep enum if present (but simplified)
37
+ if (prop.enum && Array.isArray(prop.enum)) {
38
+ simplified.properties[key].enum = prop.enum;
39
+ }
40
+ }
41
+ }
42
+ return JSON.stringify(simplified);
43
+ }
14
44
  /**
15
45
  * Clean schema for Google Gemini by removing unsupported fields
16
46
  */
@@ -1042,8 +1072,27 @@ onEvent, onComplete) {
1042
1072
  export async function streamWithGroq(specification, messages, tools, groqClient, // Groq client instance (OpenAI-compatible)
1043
1073
  onEvent, onComplete) {
1044
1074
  try {
1075
+ const modelName = getModelName(specification);
1076
+ // Filter or simplify tools for Groq models that have issues
1077
+ let groqTools = tools;
1078
+ if (tools && tools.length > 0) {
1079
+ // LLaMA 3.3 70B seems to have tool calling issues - disable tools for this model
1080
+ if (modelName && (modelName.includes("llama-3.3") || modelName.includes("LLAMA_3_3"))) {
1081
+ if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
1082
+ console.log(`⚠️ [Groq] Disabling tools for ${modelName} due to known compatibility issues`);
1083
+ }
1084
+ groqTools = undefined;
1085
+ }
1086
+ else {
1087
+ // For other models, simplify complex schemas
1088
+ groqTools = tools.map(tool => ({
1089
+ ...tool,
1090
+ schema: tool.schema ? simplifySchemaForGroq(JSON.parse(tool.schema)) : tool.schema
1091
+ }));
1092
+ }
1093
+ }
1045
1094
  // Groq uses the same API as OpenAI, so we can reuse the OpenAI streaming logic
1046
- return await streamWithOpenAI(specification, messages, tools, groqClient, onEvent, onComplete);
1095
+ return await streamWithOpenAI(specification, messages, groqTools, groqClient, onEvent, onComplete);
1047
1096
  }
1048
1097
  catch (error) {
1049
1098
  // Handle Groq-specific errors
@@ -1324,66 +1373,101 @@ onEvent, onComplete) {
1324
1373
  throw new Error("No messages found for Cohere streaming");
1325
1374
  }
1326
1375
  // Cohere v7 expects a single message and optional chatHistory
1327
- // Extract the last message as the current message
1328
- const lastMessage = messages[messages.length - 1];
1329
- const chatHistory = messages.slice(0, -1);
1376
+ // Extract system messages for preamble and filter them out of history
1377
+ const systemMessages = messages.filter(msg => msg.role === "SYSTEM");
1378
+ const nonSystemMessages = messages.filter(msg => msg.role !== "SYSTEM");
1379
+ // Extract the last non-system message as the current message
1380
+ const lastMessage = nonSystemMessages[nonSystemMessages.length - 1];
1381
+ const chatHistory = nonSystemMessages.slice(0, -1);
1330
1382
  if (!lastMessage || !lastMessage.message) {
1331
1383
  throw new Error("Last message must have message property for Cohere streaming");
1332
1384
  }
1385
+ // Build properly typed request using Cohere SDK types
1333
1386
  const streamConfig = {
1334
1387
  model: modelName,
1335
1388
  message: lastMessage.message, // Current message (singular)
1336
1389
  };
1390
+ // Add system message as preamble if present
1391
+ if (systemMessages.length > 0) {
1392
+ // Combine all system messages into preamble
1393
+ streamConfig.preamble = systemMessages.map(msg => msg.message).join("\n\n");
1394
+ }
1337
1395
  // Add chat history if there are previous messages
1338
1396
  if (chatHistory.length > 0) {
1339
- // Messages already have 'message' property from formatter
1340
- streamConfig.chatHistory = chatHistory;
1397
+ // Build properly typed chat history using Cohere SDK Message types
1398
+ // Note: SYSTEM messages are already filtered out and handled as preamble
1399
+ const cohereHistory = chatHistory.map((msg) => {
1400
+ switch (msg.role) {
1401
+ case "USER":
1402
+ return {
1403
+ role: "USER",
1404
+ message: msg.message,
1405
+ };
1406
+ case "CHATBOT":
1407
+ const chatbotMsg = {
1408
+ role: "CHATBOT",
1409
+ message: msg.message,
1410
+ };
1411
+ // Add tool calls if present
1412
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
1413
+ chatbotMsg.toolCalls = msg.tool_calls.map((tc) => ({
1414
+ name: tc.name,
1415
+ parameters: tc.parameters || {},
1416
+ }));
1417
+ }
1418
+ return chatbotMsg;
1419
+ case "TOOL":
1420
+ return {
1421
+ role: "TOOL",
1422
+ toolResults: msg.tool_results || [],
1423
+ };
1424
+ default:
1425
+ // Fallback - treat as USER
1426
+ return {
1427
+ role: "USER",
1428
+ message: msg.message,
1429
+ };
1430
+ }
1431
+ });
1432
+ streamConfig.chatHistory = cohereHistory;
1341
1433
  }
1342
1434
  // Only add temperature if it's defined
1343
- if (specification.cohere?.temperature !== undefined) {
1435
+ if (specification.cohere?.temperature !== undefined &&
1436
+ specification.cohere.temperature !== null) {
1344
1437
  streamConfig.temperature = specification.cohere.temperature;
1345
1438
  }
1346
1439
  // Add tools if provided
1347
1440
  if (tools && tools.length > 0) {
1348
- streamConfig.tools = tools.map((tool) => {
1441
+ const cohereTools = tools.map((tool) => {
1349
1442
  if (!tool.schema) {
1350
1443
  return {
1351
- name: tool.name,
1352
- description: tool.description,
1353
- parameter_definitions: {},
1444
+ name: tool.name || "",
1445
+ description: tool.description || "",
1446
+ parameterDefinitions: {},
1354
1447
  };
1355
1448
  }
1356
1449
  // Parse the JSON schema
1357
1450
  const schema = JSON.parse(tool.schema);
1358
1451
  // Convert JSON Schema to Cohere's expected format
1359
- const parameter_definitions = {};
1452
+ const parameterDefinitions = {};
1360
1453
  if (schema.properties) {
1361
1454
  for (const [key, value] of Object.entries(schema.properties)) {
1362
1455
  const prop = value;
1363
1456
  const paramDef = {
1364
- type: prop.type || "string",
1457
+ type: prop.type || "str",
1365
1458
  description: prop.description || "",
1366
1459
  required: schema.required?.includes(key) || false,
1367
1460
  };
1368
- // Add additional properties that Cohere might expect
1369
- if (prop.enum) {
1370
- paramDef.options = prop.enum;
1371
- }
1372
- if (prop.default !== undefined) {
1373
- paramDef.default = prop.default;
1374
- }
1375
- if (prop.items) {
1376
- paramDef.items = prop.items;
1377
- }
1378
- parameter_definitions[key] = paramDef;
1461
+ parameterDefinitions[key] = paramDef;
1379
1462
  }
1380
1463
  }
1381
1464
  return {
1382
- name: tool.name,
1383
- description: tool.description,
1384
- parameter_definitions, // Use snake_case as expected by Cohere API
1465
+ name: tool.name || "",
1466
+ description: tool.description || "",
1467
+ parameterDefinitions, // Use camelCase as expected by Cohere SDK
1385
1468
  };
1386
1469
  });
1470
+ streamConfig.tools = cohereTools;
1387
1471
  }
1388
1472
  if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
1389
1473
  console.log(`🔍 [Cohere] Final stream config:`, JSON.stringify(streamConfig, null, 2));
@@ -1394,20 +1478,42 @@ onEvent, onComplete) {
1394
1478
  }
1395
1479
  let stream;
1396
1480
  try {
1481
+ // Always log the full config when debugging Command A errors
1482
+ if (modelName.includes("command-a") ||
1483
+ process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
1484
+ console.log(`🔍 [Cohere] Full streamConfig for ${modelName}:`, JSON.stringify(streamConfig, null, 2));
1485
+ }
1397
1486
  stream = await cohereClient.chatStream(streamConfig);
1398
1487
  }
1399
1488
  catch (streamError) {
1400
- if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
1401
- console.error(`❌ [Cohere] Stream creation failed:`, streamError);
1402
- if (streamError.response) {
1403
- console.error(`❌ [Cohere] Stream response status: ${streamError.response.status}`);
1404
- console.error(`❌ [Cohere] Stream response data:`, streamError.response.data);
1405
- }
1406
- if (streamError.status) {
1407
- console.error(`❌ [Cohere] Direct status: ${streamError.status}`);
1489
+ // Enhanced error logging
1490
+ console.error(`❌ [Cohere] Stream creation failed for model ${modelName}`);
1491
+ console.error(`❌ [Cohere] Error type: ${streamError.constructor.name}`);
1492
+ console.error(`❌ [Cohere] Status code: ${streamError.statusCode || streamError.status || "unknown"}`);
1493
+ console.error(`❌ [Cohere] Error message: ${streamError.message}`);
1494
+ // Try to read the body if it's a ReadableStream
1495
+ if (streamError.body &&
1496
+ typeof streamError.body.getReader === "function") {
1497
+ try {
1498
+ const reader = streamError.body.getReader();
1499
+ let fullBody = "";
1500
+ while (true) {
1501
+ const { done, value } = await reader.read();
1502
+ if (done)
1503
+ break;
1504
+ fullBody += new TextDecoder().decode(value);
1505
+ }
1506
+ console.error(`❌ [Cohere] Raw error body:`, fullBody);
1507
+ try {
1508
+ const parsed = JSON.parse(fullBody);
1509
+ console.error(`❌ [Cohere] Parsed error details:`, JSON.stringify(parsed, null, 2));
1510
+ }
1511
+ catch (e) {
1512
+ console.error(`❌ [Cohere] Could not parse error body as JSON`);
1513
+ }
1408
1514
  }
1409
- if (streamError.body) {
1410
- console.error(`❌ [Cohere] Response body:`, streamError.body);
1515
+ catch (e) {
1516
+ console.error(`❌ [Cohere] Could not read error body:`, e);
1411
1517
  }
1412
1518
  }
1413
1519
  throw streamError;
@@ -24,6 +24,7 @@ export declare class UIEventAdapter {
24
24
  private chunkBuffer?;
25
25
  private smoothingDelay;
26
26
  private chunkQueue;
27
+ private contextWindowUsage?;
27
28
  constructor(onEvent: (event: AgentStreamEvent) => void, conversationId: string, options?: {
28
29
  smoothingEnabled?: boolean;
29
30
  chunkingStrategy?: ChunkingStrategy;
@@ -49,6 +50,7 @@ export declare class UIEventAdapter {
49
50
  private emitNextChunk;
50
51
  private emitMessageUpdate;
51
52
  private emitUIEvent;
53
+ private handleContextWindow;
52
54
  /**
53
55
  * Clean up any pending timers
54
56
  */
@@ -23,6 +23,7 @@ export class UIEventAdapter {
23
23
  chunkBuffer;
24
24
  smoothingDelay = 30;
25
25
  chunkQueue = []; // Queue of chunks waiting to be emitted
26
+ contextWindowUsage;
26
27
  constructor(onEvent, conversationId, options = {}) {
27
28
  this.onEvent = onEvent;
28
29
  this.conversationId = conversationId;
@@ -66,6 +67,9 @@ export class UIEventAdapter {
66
67
  case "error":
67
68
  this.handleError(event.error);
68
69
  break;
70
+ case "context_window":
71
+ this.handleContextWindow(event.usage);
72
+ break;
69
73
  }
70
74
  }
71
75
  handleStart(conversationId) {
@@ -323,11 +327,16 @@ export class UIEventAdapter {
323
327
  finalMetrics.streamingThroughput = Math.round((this.currentMessage.length / streamingTime) * 1000);
324
328
  }
325
329
  }
326
- this.emitUIEvent({
330
+ // Include context window usage if available
331
+ const event = {
327
332
  type: "conversation_completed",
328
333
  message: finalMessage,
329
334
  metrics: finalMetrics,
330
- });
335
+ };
336
+ if (this.contextWindowUsage) {
337
+ event.contextWindow = this.contextWindowUsage;
338
+ }
339
+ this.emitUIEvent(event);
331
340
  }
332
341
  handleError(error) {
333
342
  this.isStreaming = false;
@@ -468,6 +477,18 @@ export class UIEventAdapter {
468
477
  emitUIEvent(event) {
469
478
  this.onEvent(event);
470
479
  }
480
+ handleContextWindow(usage) {
481
+ // Store for later inclusion in completion event
482
+ this.contextWindowUsage = usage;
483
+ if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
484
+ console.log(`📊 [UIEventAdapter] Context window: ${usage.usedTokens}/${usage.maxTokens} (${usage.percentage}%)`);
485
+ }
486
+ this.emitUIEvent({
487
+ type: "context_window",
488
+ usage,
489
+ timestamp: new Date(),
490
+ });
491
+ }
471
492
  /**
472
493
  * Clean up any pending timers
473
494
  */
@@ -13,6 +13,12 @@ export interface AgentMetrics {
13
13
  toolExecutions?: number;
14
14
  rounds?: number;
15
15
  }
16
+ export interface ContextWindowUsage {
17
+ usedTokens: number;
18
+ maxTokens: number;
19
+ percentage: number;
20
+ remainingTokens: number;
21
+ }
16
22
  export interface AgentResult {
17
23
  message: string;
18
24
  conversationId: string;
@@ -21,6 +27,7 @@ export interface AgentResult {
21
27
  toolResults?: ToolCallResult[];
22
28
  metrics?: AgentMetrics;
23
29
  usage?: UsageInfo;
30
+ contextWindow?: ContextWindowUsage;
24
31
  error?: AgentError;
25
32
  }
26
33
  export interface StreamAgentOptions {
@@ -49,4 +49,12 @@ export type StreamEvent = {
49
49
  } | {
50
50
  type: "error";
51
51
  error: string;
52
+ } | {
53
+ type: "context_window";
54
+ usage: {
55
+ usedTokens: number;
56
+ maxTokens: number;
57
+ percentage: number;
58
+ remainingTokens: number;
59
+ };
52
60
  };
@@ -3,6 +3,19 @@ import { ConversationMessage, ConversationToolCall } from "../generated/graphql-
3
3
  * Tool execution status for streaming
4
4
  */
5
5
  export type ToolExecutionStatus = "preparing" | "executing" | "ready" | "completed" | "failed";
6
+ /**
7
+ * Context window usage event - emitted at start of agent interaction
8
+ */
9
+ export type ContextWindowEvent = {
10
+ type: "context_window";
11
+ usage: {
12
+ usedTokens: number;
13
+ maxTokens: number;
14
+ percentage: number;
15
+ remainingTokens: number;
16
+ };
17
+ timestamp: Date;
18
+ };
6
19
  /**
7
20
  * Simplified UI-focused streaming events using GraphQL types
8
21
  */
@@ -11,7 +24,7 @@ export type AgentStreamEvent = {
11
24
  conversationId: string;
12
25
  timestamp: Date;
13
26
  model?: string;
14
- } | {
27
+ } | ContextWindowEvent | {
15
28
  type: "message_update";
16
29
  message: Partial<ConversationMessage> & {
17
30
  message: string;
@@ -43,6 +56,12 @@ export type AgentStreamEvent = {
43
56
  avgTokenDelay?: number;
44
57
  streamingThroughput?: number;
45
58
  };
59
+ contextWindow?: {
60
+ usedTokens: number;
61
+ maxTokens: number;
62
+ percentage: number;
63
+ remainingTokens: number;
64
+ };
46
65
  } | {
47
66
  type: "error";
48
67
  error: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphlit-client",
3
- "version": "1.0.20250615004",
3
+ "version": "1.0.20250615006",
4
4
  "description": "Graphlit API Client for TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/client.js",