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 +36 -1
- package/dist/streaming/providers.js +143 -37
- package/dist/streaming/ui-event-adapter.d.ts +2 -0
- package/dist/streaming/ui-event-adapter.js +23 -2
- package/dist/types/agent.d.ts +7 -0
- package/dist/types/internal.d.ts +8 -0
- package/dist/types/ui-events.d.ts +20 -1
- package/package.json +1 -1
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
|
-
|
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,
|
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
|
1328
|
-
const
|
1329
|
-
const
|
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
|
-
//
|
1340
|
-
|
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
|
-
|
1441
|
+
const cohereTools = tools.map((tool) => {
|
1349
1442
|
if (!tool.schema) {
|
1350
1443
|
return {
|
1351
|
-
name: tool.name,
|
1352
|
-
description: tool.description,
|
1353
|
-
|
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
|
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 || "
|
1457
|
+
type: prop.type || "str",
|
1365
1458
|
description: prop.description || "",
|
1366
1459
|
required: schema.required?.includes(key) || false,
|
1367
1460
|
};
|
1368
|
-
|
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
|
-
|
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
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
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
|
-
|
1410
|
-
console.error(`❌ [Cohere]
|
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
|
-
|
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
|
*/
|
package/dist/types/agent.d.ts
CHANGED
@@ -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 {
|
package/dist/types/internal.d.ts
CHANGED
@@ -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: {
|