graphlit-client 1.0.20250613009 → 1.0.20250615001
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/README.md +121 -21
- package/dist/client.d.ts +91 -1
- package/dist/client.js +473 -24
- package/dist/generated/graphql-documents.js +7 -0
- package/dist/generated/graphql-types.d.ts +7 -0
- package/dist/model-mapping.js +96 -1
- package/dist/streaming/llm-formatters.d.ts +57 -0
- package/dist/streaming/llm-formatters.js +172 -0
- package/dist/streaming/providers.d.ts +31 -1
- package/dist/streaming/providers.js +621 -1
- package/package.json +17 -1
@@ -61,7 +61,7 @@ onEvent, onComplete) {
|
|
61
61
|
try {
|
62
62
|
const modelName = getModelName(specification);
|
63
63
|
if (!modelName) {
|
64
|
-
throw new Error(`No model name found for
|
64
|
+
throw new Error(`No model name found for specification: ${specification.name} (service: ${specification.serviceType})`);
|
65
65
|
}
|
66
66
|
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
67
67
|
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}"`);
|
@@ -979,3 +979,623 @@ onEvent, onComplete) {
|
|
979
979
|
throw error;
|
980
980
|
}
|
981
981
|
}
|
982
|
+
/**
|
983
|
+
* Stream with Groq SDK (OpenAI-compatible)
|
984
|
+
*/
|
985
|
+
export async function streamWithGroq(specification, messages, tools, groqClient, // Groq client instance (OpenAI-compatible)
|
986
|
+
onEvent, onComplete) {
|
987
|
+
// Groq uses the same API as OpenAI, so we can reuse the OpenAI streaming logic
|
988
|
+
return streamWithOpenAI(specification, messages, tools, groqClient, onEvent, onComplete);
|
989
|
+
}
|
990
|
+
/**
|
991
|
+
* Stream with Cerebras SDK (OpenAI-compatible)
|
992
|
+
*/
|
993
|
+
export async function streamWithCerebras(specification, messages, tools, cerebrasClient, // OpenAI client instance configured for Cerebras
|
994
|
+
onEvent, onComplete) {
|
995
|
+
// Cerebras uses the same API as OpenAI, so we can reuse the OpenAI streaming logic
|
996
|
+
return streamWithOpenAI(specification, messages, tools, cerebrasClient, onEvent, onComplete);
|
997
|
+
}
|
998
|
+
/**
|
999
|
+
* Stream with Deepseek SDK (OpenAI-compatible)
|
1000
|
+
*/
|
1001
|
+
export async function streamWithDeepseek(specification, messages, tools, deepseekClient, // OpenAI client instance configured for Deepseek
|
1002
|
+
onEvent, onComplete) {
|
1003
|
+
let fullMessage = "";
|
1004
|
+
let toolCalls = [];
|
1005
|
+
// Performance metrics
|
1006
|
+
const startTime = Date.now();
|
1007
|
+
let firstTokenTime = 0;
|
1008
|
+
let firstMeaningfulContentTime = 0;
|
1009
|
+
let tokenCount = 0;
|
1010
|
+
let toolArgumentTokens = 0;
|
1011
|
+
let lastEventTime = 0;
|
1012
|
+
const interTokenDelays = [];
|
1013
|
+
// Tool calling metrics
|
1014
|
+
const toolMetrics = {
|
1015
|
+
totalTools: 0,
|
1016
|
+
successfulTools: 0,
|
1017
|
+
failedTools: 0,
|
1018
|
+
toolTimes: [],
|
1019
|
+
currentToolStart: 0,
|
1020
|
+
roundStartTime: startTime,
|
1021
|
+
rounds: [],
|
1022
|
+
currentRound: 1,
|
1023
|
+
};
|
1024
|
+
try {
|
1025
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1026
|
+
console.log(`🔍 [Deepseek] Specification object:`, {
|
1027
|
+
name: specification.name,
|
1028
|
+
serviceType: specification.serviceType,
|
1029
|
+
deepseek: specification.deepseek,
|
1030
|
+
hasDeepseekModel: !!specification.deepseek?.model,
|
1031
|
+
deepseekModelValue: specification.deepseek?.model
|
1032
|
+
});
|
1033
|
+
}
|
1034
|
+
const modelName = getModelName(specification);
|
1035
|
+
if (!modelName) {
|
1036
|
+
console.error(`❌ [Deepseek] Model resolution failed:`, {
|
1037
|
+
name: specification.name,
|
1038
|
+
serviceType: specification.serviceType,
|
1039
|
+
deepseek: specification.deepseek,
|
1040
|
+
hasCustomModelName: !!specification.deepseek?.modelName
|
1041
|
+
});
|
1042
|
+
throw new Error(`No model name found for specification: ${specification.name} (service: ${specification.serviceType})`);
|
1043
|
+
}
|
1044
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1045
|
+
console.log(`🤖 [Deepseek] Model Config: Service=Deepseek | Model=${modelName} | Temperature=${specification.deepseek?.temperature} | MaxTokens=${specification.deepseek?.completionTokenLimit || "null"} | Tools=${tools?.length || 0} | Spec="${specification.name}"`);
|
1046
|
+
}
|
1047
|
+
const streamConfig = {
|
1048
|
+
model: modelName,
|
1049
|
+
messages,
|
1050
|
+
stream: true,
|
1051
|
+
temperature: specification.deepseek?.temperature,
|
1052
|
+
};
|
1053
|
+
// Only add max_completion_tokens if it's defined
|
1054
|
+
if (specification.deepseek?.completionTokenLimit) {
|
1055
|
+
streamConfig.max_completion_tokens =
|
1056
|
+
specification.deepseek.completionTokenLimit;
|
1057
|
+
}
|
1058
|
+
// Add tools if provided
|
1059
|
+
if (tools && tools.length > 0) {
|
1060
|
+
streamConfig.tools = tools.map((tool) => ({
|
1061
|
+
type: "function",
|
1062
|
+
function: {
|
1063
|
+
name: tool.name,
|
1064
|
+
description: tool.description,
|
1065
|
+
parameters: tool.schema ? JSON.parse(tool.schema) : {},
|
1066
|
+
},
|
1067
|
+
}));
|
1068
|
+
}
|
1069
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1070
|
+
console.log(`⏱️ [Deepseek] Starting LLM call at: ${new Date().toISOString()}`);
|
1071
|
+
}
|
1072
|
+
const stream = await deepseekClient.chat.completions.create(streamConfig);
|
1073
|
+
for await (const chunk of stream) {
|
1074
|
+
const delta = chunk.choices[0]?.delta;
|
1075
|
+
if (!delta)
|
1076
|
+
continue;
|
1077
|
+
const currentTime = Date.now();
|
1078
|
+
// Track first token time
|
1079
|
+
if (firstTokenTime === 0) {
|
1080
|
+
firstTokenTime = currentTime - startTime;
|
1081
|
+
}
|
1082
|
+
// Track inter-token delays
|
1083
|
+
if (lastEventTime > 0) {
|
1084
|
+
const delay = currentTime - lastEventTime;
|
1085
|
+
interTokenDelays.push(delay);
|
1086
|
+
}
|
1087
|
+
lastEventTime = currentTime;
|
1088
|
+
// Handle message content
|
1089
|
+
if (delta.content) {
|
1090
|
+
tokenCount++;
|
1091
|
+
fullMessage += delta.content;
|
1092
|
+
// Track first meaningful content
|
1093
|
+
if (firstMeaningfulContentTime === 0 && fullMessage.trim().length > 0) {
|
1094
|
+
firstMeaningfulContentTime = currentTime - startTime;
|
1095
|
+
}
|
1096
|
+
onEvent({
|
1097
|
+
type: "message",
|
1098
|
+
message: fullMessage,
|
1099
|
+
});
|
1100
|
+
// Performance metrics tracking (internal only)
|
1101
|
+
if (tokenCount % 10 === 0) {
|
1102
|
+
const totalTokens = tokenCount + toolArgumentTokens;
|
1103
|
+
const tokensPerSecond = totalTokens > 0 ? totalTokens / ((currentTime - startTime) / 1000) : 0;
|
1104
|
+
const avgInterTokenDelay = interTokenDelays.length > 0
|
1105
|
+
? interTokenDelays.reduce((a, b) => a + b, 0) / interTokenDelays.length
|
1106
|
+
: 0;
|
1107
|
+
}
|
1108
|
+
}
|
1109
|
+
// Handle tool calls
|
1110
|
+
if (delta.tool_calls) {
|
1111
|
+
for (const toolCall of delta.tool_calls) {
|
1112
|
+
const index = toolCall.index;
|
1113
|
+
// Initialize tool call if it doesn't exist
|
1114
|
+
if (!toolCalls[index]) {
|
1115
|
+
toolCalls[index] = {
|
1116
|
+
id: toolCall.id || `tool_${index}`,
|
1117
|
+
name: toolCall.function?.name || "",
|
1118
|
+
arguments: "",
|
1119
|
+
};
|
1120
|
+
// Start tool timing
|
1121
|
+
toolMetrics.totalTools++;
|
1122
|
+
toolMetrics.currentToolStart = currentTime;
|
1123
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1124
|
+
console.log(`🔧 [Deepseek] Tool call started: ${toolCalls[index].name}`);
|
1125
|
+
}
|
1126
|
+
onEvent({
|
1127
|
+
type: "tool_call_parsed",
|
1128
|
+
toolCall: { ...toolCalls[index] },
|
1129
|
+
});
|
1130
|
+
}
|
1131
|
+
// Update tool call name if provided
|
1132
|
+
if (toolCall.function?.name) {
|
1133
|
+
toolCalls[index].name = toolCall.function.name;
|
1134
|
+
}
|
1135
|
+
// Accumulate arguments
|
1136
|
+
if (toolCall.function?.arguments) {
|
1137
|
+
toolCalls[index].arguments += toolCall.function.arguments;
|
1138
|
+
toolArgumentTokens++;
|
1139
|
+
}
|
1140
|
+
// Update with current state
|
1141
|
+
onEvent({
|
1142
|
+
type: "tool_call_delta",
|
1143
|
+
toolCallId: toolCalls[index].id,
|
1144
|
+
argumentDelta: toolCall.function?.arguments || "",
|
1145
|
+
});
|
1146
|
+
}
|
1147
|
+
}
|
1148
|
+
}
|
1149
|
+
// Process completed tool calls
|
1150
|
+
const validToolCalls = toolCalls.filter((tc, idx) => {
|
1151
|
+
if (!isValidJSON(tc.arguments)) {
|
1152
|
+
console.warn(`[Deepseek] Filtering out incomplete tool call ${idx} (${tc.name}) with INVALID JSON (${tc.arguments.length} chars)`);
|
1153
|
+
return false;
|
1154
|
+
}
|
1155
|
+
return true;
|
1156
|
+
});
|
1157
|
+
if (toolCalls.length !== validToolCalls.length) {
|
1158
|
+
console.log(`[Deepseek] Filtered out ${toolCalls.length - validToolCalls.length} incomplete tool calls`);
|
1159
|
+
}
|
1160
|
+
// Final performance metrics
|
1161
|
+
const totalTime = Date.now() - startTime;
|
1162
|
+
const totalTokens = tokenCount + toolArgumentTokens;
|
1163
|
+
const tokensPerSecond = totalTokens > 0 ? totalTokens / (totalTime / 1000) : 0;
|
1164
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_METRICS) {
|
1165
|
+
const metricsData = {
|
1166
|
+
totalTime: `${totalTime}ms`,
|
1167
|
+
ttft: `${firstTokenTime}ms`,
|
1168
|
+
ttfmc: firstMeaningfulContentTime > 0
|
1169
|
+
? `${firstMeaningfulContentTime}ms`
|
1170
|
+
: null,
|
1171
|
+
contentTokens: tokenCount,
|
1172
|
+
toolTokens: toolArgumentTokens,
|
1173
|
+
totalTokens: totalTokens,
|
1174
|
+
tps: tokensPerSecond.toFixed(2),
|
1175
|
+
};
|
1176
|
+
console.log(`📊 [Deepseek] 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}`);
|
1177
|
+
}
|
1178
|
+
// Send completion event
|
1179
|
+
onEvent({
|
1180
|
+
type: "complete",
|
1181
|
+
tokens: totalTokens,
|
1182
|
+
});
|
1183
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1184
|
+
console.log(`✅ [Deepseek] Stream completed: ${fullMessage.length} chars, ${validToolCalls.length} tools`);
|
1185
|
+
}
|
1186
|
+
onComplete(fullMessage, validToolCalls);
|
1187
|
+
}
|
1188
|
+
catch (error) {
|
1189
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1190
|
+
console.error(`❌ [Deepseek] Stream error:`, error);
|
1191
|
+
}
|
1192
|
+
onEvent({
|
1193
|
+
type: "error",
|
1194
|
+
error: `Deepseek streaming error: ${error}`,
|
1195
|
+
});
|
1196
|
+
throw error;
|
1197
|
+
}
|
1198
|
+
}
|
1199
|
+
/**
|
1200
|
+
* Stream with Cohere SDK
|
1201
|
+
*/
|
1202
|
+
export async function streamWithCohere(specification, messages, tools, cohereClient, // CohereClient instance
|
1203
|
+
onEvent, onComplete) {
|
1204
|
+
let fullMessage = "";
|
1205
|
+
let toolCalls = [];
|
1206
|
+
// Performance metrics
|
1207
|
+
const startTime = Date.now();
|
1208
|
+
let firstTokenTime = 0;
|
1209
|
+
let tokenCount = 0;
|
1210
|
+
try {
|
1211
|
+
const modelName = getModelName(specification);
|
1212
|
+
if (!modelName) {
|
1213
|
+
throw new Error(`No model name found for Cohere specification: ${specification.name}`);
|
1214
|
+
}
|
1215
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1216
|
+
console.log(`🤖 [Cohere] Model Config: Service=Cohere | Model=${modelName} | Temperature=${specification.cohere?.temperature} | Tools=${tools?.length || 0} | Spec="${specification.name}"`);
|
1217
|
+
}
|
1218
|
+
// Prepare the latest user message and chat history
|
1219
|
+
const lastMessage = messages[messages.length - 1];
|
1220
|
+
const chatHistory = messages.slice(0, -1);
|
1221
|
+
const streamConfig = {
|
1222
|
+
model: modelName,
|
1223
|
+
message: lastMessage.message,
|
1224
|
+
chatHistory: chatHistory,
|
1225
|
+
temperature: specification.cohere?.temperature,
|
1226
|
+
};
|
1227
|
+
// Add tools if provided
|
1228
|
+
if (tools && tools.length > 0) {
|
1229
|
+
streamConfig.tools = tools.map((tool) => {
|
1230
|
+
if (!tool.schema) {
|
1231
|
+
return {
|
1232
|
+
name: tool.name,
|
1233
|
+
description: tool.description,
|
1234
|
+
parameterDefinitions: {},
|
1235
|
+
};
|
1236
|
+
}
|
1237
|
+
// Parse the JSON schema
|
1238
|
+
const schema = JSON.parse(tool.schema);
|
1239
|
+
// Convert JSON Schema to Cohere's expected format
|
1240
|
+
const parameterDefinitions = {};
|
1241
|
+
if (schema.properties) {
|
1242
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
1243
|
+
const prop = value;
|
1244
|
+
parameterDefinitions[key] = {
|
1245
|
+
type: prop.type || "string",
|
1246
|
+
description: prop.description || "",
|
1247
|
+
required: schema.required?.includes(key) || false,
|
1248
|
+
};
|
1249
|
+
// Add additional properties that Cohere might expect
|
1250
|
+
if (prop.enum) {
|
1251
|
+
parameterDefinitions[key].options = prop.enum;
|
1252
|
+
}
|
1253
|
+
if (prop.default !== undefined) {
|
1254
|
+
parameterDefinitions[key].default = prop.default;
|
1255
|
+
}
|
1256
|
+
if (prop.items) {
|
1257
|
+
parameterDefinitions[key].items = prop.items;
|
1258
|
+
}
|
1259
|
+
}
|
1260
|
+
}
|
1261
|
+
return {
|
1262
|
+
name: tool.name,
|
1263
|
+
description: tool.description,
|
1264
|
+
parameterDefinitions,
|
1265
|
+
};
|
1266
|
+
});
|
1267
|
+
}
|
1268
|
+
const stream = await cohereClient.chatStream(streamConfig);
|
1269
|
+
for await (const chunk of stream) {
|
1270
|
+
if (chunk.eventType === "text-generation") {
|
1271
|
+
const text = chunk.text;
|
1272
|
+
if (text) {
|
1273
|
+
fullMessage += text;
|
1274
|
+
tokenCount++;
|
1275
|
+
if (firstTokenTime === 0) {
|
1276
|
+
firstTokenTime = Date.now() - startTime;
|
1277
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1278
|
+
console.log(`⚡ [Cohere] Time to First Token: ${firstTokenTime}ms`);
|
1279
|
+
}
|
1280
|
+
}
|
1281
|
+
onEvent({
|
1282
|
+
type: "token",
|
1283
|
+
token: text,
|
1284
|
+
});
|
1285
|
+
}
|
1286
|
+
}
|
1287
|
+
else if (chunk.eventType === "tool-calls-generation") {
|
1288
|
+
// Handle tool calls
|
1289
|
+
if (chunk.toolCalls) {
|
1290
|
+
for (const toolCall of chunk.toolCalls) {
|
1291
|
+
const id = `tool_${Date.now()}_${toolCalls.length}`;
|
1292
|
+
const formattedToolCall = {
|
1293
|
+
id,
|
1294
|
+
name: toolCall.name,
|
1295
|
+
arguments: JSON.stringify(toolCall.parameters),
|
1296
|
+
};
|
1297
|
+
toolCalls.push(formattedToolCall);
|
1298
|
+
onEvent({
|
1299
|
+
type: "tool_call_start",
|
1300
|
+
toolCall: { id, name: toolCall.name },
|
1301
|
+
});
|
1302
|
+
onEvent({
|
1303
|
+
type: "tool_call_parsed",
|
1304
|
+
toolCall: formattedToolCall,
|
1305
|
+
});
|
1306
|
+
}
|
1307
|
+
}
|
1308
|
+
}
|
1309
|
+
}
|
1310
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1311
|
+
console.log(`✅ [Cohere] Complete. Total tokens: ${tokenCount} | Message length: ${fullMessage.length}`);
|
1312
|
+
}
|
1313
|
+
onComplete(fullMessage, toolCalls);
|
1314
|
+
}
|
1315
|
+
catch (error) {
|
1316
|
+
throw error;
|
1317
|
+
}
|
1318
|
+
}
|
1319
|
+
/**
|
1320
|
+
* Stream with Mistral SDK
|
1321
|
+
*/
|
1322
|
+
export async function streamWithMistral(specification, messages, tools, mistralClient, // Mistral client instance
|
1323
|
+
onEvent, onComplete) {
|
1324
|
+
let fullMessage = "";
|
1325
|
+
let toolCalls = [];
|
1326
|
+
// Performance metrics
|
1327
|
+
const startTime = Date.now();
|
1328
|
+
let firstTokenTime = 0;
|
1329
|
+
let tokenCount = 0;
|
1330
|
+
try {
|
1331
|
+
const modelName = getModelName(specification);
|
1332
|
+
if (!modelName) {
|
1333
|
+
throw new Error(`No model name found for Mistral specification: ${specification.name}`);
|
1334
|
+
}
|
1335
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1336
|
+
console.log(`🤖 [Mistral] Model Config: Service=Mistral | Model=${modelName} | Temperature=${specification.mistral?.temperature} | Tools=${tools?.length || 0} | Spec="${specification.name}"`);
|
1337
|
+
}
|
1338
|
+
const streamConfig = {
|
1339
|
+
model: modelName,
|
1340
|
+
messages,
|
1341
|
+
temperature: specification.mistral?.temperature,
|
1342
|
+
};
|
1343
|
+
// Add tools if provided
|
1344
|
+
if (tools && tools.length > 0) {
|
1345
|
+
streamConfig.tools = tools.map((tool) => ({
|
1346
|
+
type: "function",
|
1347
|
+
function: {
|
1348
|
+
name: tool.name,
|
1349
|
+
description: tool.description,
|
1350
|
+
parameters: tool.schema ? JSON.parse(tool.schema) : {},
|
1351
|
+
},
|
1352
|
+
}));
|
1353
|
+
}
|
1354
|
+
const stream = await mistralClient.chat.stream(streamConfig);
|
1355
|
+
for await (const chunk of stream) {
|
1356
|
+
const delta = chunk.data.choices[0]?.delta;
|
1357
|
+
if (delta?.content) {
|
1358
|
+
fullMessage += delta.content;
|
1359
|
+
tokenCount++;
|
1360
|
+
if (firstTokenTime === 0) {
|
1361
|
+
firstTokenTime = Date.now() - startTime;
|
1362
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1363
|
+
console.log(`⚡ [Mistral] Time to First Token: ${firstTokenTime}ms`);
|
1364
|
+
}
|
1365
|
+
}
|
1366
|
+
onEvent({
|
1367
|
+
type: "token",
|
1368
|
+
token: delta.content,
|
1369
|
+
});
|
1370
|
+
}
|
1371
|
+
// Handle tool calls
|
1372
|
+
if (delta?.tool_calls) {
|
1373
|
+
for (const toolCallDelta of delta.tool_calls) {
|
1374
|
+
const index = toolCallDelta.index || 0;
|
1375
|
+
if (!toolCalls[index]) {
|
1376
|
+
toolCalls[index] = {
|
1377
|
+
id: toolCallDelta.id || `tool_${Date.now()}_${index}`,
|
1378
|
+
name: "",
|
1379
|
+
arguments: "",
|
1380
|
+
};
|
1381
|
+
onEvent({
|
1382
|
+
type: "tool_call_start",
|
1383
|
+
toolCall: {
|
1384
|
+
id: toolCalls[index].id,
|
1385
|
+
name: toolCallDelta.function?.name || "",
|
1386
|
+
},
|
1387
|
+
});
|
1388
|
+
}
|
1389
|
+
if (toolCallDelta.function?.name) {
|
1390
|
+
toolCalls[index].name = toolCallDelta.function.name;
|
1391
|
+
}
|
1392
|
+
if (toolCallDelta.function?.arguments) {
|
1393
|
+
toolCalls[index].arguments += toolCallDelta.function.arguments;
|
1394
|
+
onEvent({
|
1395
|
+
type: "tool_call_delta",
|
1396
|
+
toolCallId: toolCalls[index].id,
|
1397
|
+
argumentDelta: toolCallDelta.function.arguments,
|
1398
|
+
});
|
1399
|
+
}
|
1400
|
+
}
|
1401
|
+
}
|
1402
|
+
}
|
1403
|
+
// Emit complete events for tool calls
|
1404
|
+
for (const toolCall of toolCalls) {
|
1405
|
+
if (isValidJSON(toolCall.arguments)) {
|
1406
|
+
onEvent({
|
1407
|
+
type: "tool_call_parsed",
|
1408
|
+
toolCall,
|
1409
|
+
});
|
1410
|
+
}
|
1411
|
+
}
|
1412
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1413
|
+
console.log(`✅ [Mistral] Complete. Total tokens: ${tokenCount} | Message length: ${fullMessage.length}`);
|
1414
|
+
}
|
1415
|
+
onComplete(fullMessage, toolCalls);
|
1416
|
+
}
|
1417
|
+
catch (error) {
|
1418
|
+
throw error;
|
1419
|
+
}
|
1420
|
+
}
|
1421
|
+
/**
|
1422
|
+
* Stream with Bedrock SDK (for Claude models)
|
1423
|
+
*/
|
1424
|
+
export async function streamWithBedrock(specification, messages, systemPrompt, tools, bedrockClient, // BedrockRuntimeClient instance
|
1425
|
+
onEvent, onComplete) {
|
1426
|
+
let fullMessage = "";
|
1427
|
+
let toolCalls = [];
|
1428
|
+
// Map contentBlockIndex to tool calls for proper correlation
|
1429
|
+
const toolCallsByIndex = new Map();
|
1430
|
+
// Performance metrics
|
1431
|
+
const startTime = Date.now();
|
1432
|
+
let firstTokenTime = 0;
|
1433
|
+
let tokenCount = 0;
|
1434
|
+
try {
|
1435
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1436
|
+
console.log(`🔍 [Bedrock] Specification object:`, JSON.stringify(specification, null, 2));
|
1437
|
+
}
|
1438
|
+
const modelName = getModelName(specification);
|
1439
|
+
if (!modelName) {
|
1440
|
+
console.error(`❌ [Bedrock] Model resolution failed for specification:`, {
|
1441
|
+
name: specification.name,
|
1442
|
+
serviceType: specification.serviceType,
|
1443
|
+
bedrock: specification.bedrock,
|
1444
|
+
hasCustomModelName: !!specification.bedrock?.modelName
|
1445
|
+
});
|
1446
|
+
throw new Error(`No model name found for Bedrock specification: ${specification.name} (service: ${specification.serviceType}, bedrock.model: ${specification.bedrock?.model})`);
|
1447
|
+
}
|
1448
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1449
|
+
console.log(`🤖 [Bedrock] Model Config: Service=Bedrock | Model=${modelName} | Temperature=${specification.bedrock?.temperature} | Tools=${tools?.length || 0} | Spec="${specification.name}"`);
|
1450
|
+
}
|
1451
|
+
// Import the ConverseStreamCommand for unified API
|
1452
|
+
const { ConverseStreamCommand } = await import("@aws-sdk/client-bedrock-runtime");
|
1453
|
+
// Convert messages to Bedrock Converse format
|
1454
|
+
// The AWS SDK expects content as an array of content blocks
|
1455
|
+
const converseMessages = messages.map((msg) => ({
|
1456
|
+
role: msg.role,
|
1457
|
+
content: [{
|
1458
|
+
text: typeof msg.content === 'string' ? msg.content : msg.content.toString()
|
1459
|
+
}]
|
1460
|
+
}));
|
1461
|
+
// Prepare the request using Converse API format
|
1462
|
+
// Using 'any' type because:
|
1463
|
+
// 1. We're dynamically importing the SDK (can't import types at compile time)
|
1464
|
+
// 2. The ConverseStreamCommandInput type has complex union types for system/toolConfig
|
1465
|
+
// 3. The structure matches the AWS SDK expectations
|
1466
|
+
const request = {
|
1467
|
+
modelId: modelName,
|
1468
|
+
messages: converseMessages,
|
1469
|
+
inferenceConfig: {
|
1470
|
+
temperature: specification.bedrock?.temperature ?? undefined,
|
1471
|
+
topP: specification.bedrock?.probability ?? undefined,
|
1472
|
+
maxTokens: specification.bedrock?.completionTokenLimit || 1000,
|
1473
|
+
},
|
1474
|
+
};
|
1475
|
+
// Add system prompt if provided
|
1476
|
+
if (systemPrompt) {
|
1477
|
+
request.system = [{ text: systemPrompt }];
|
1478
|
+
}
|
1479
|
+
// Add tools if provided
|
1480
|
+
if (tools && tools.length > 0) {
|
1481
|
+
request.toolConfig = {
|
1482
|
+
tools: tools.map((tool) => ({
|
1483
|
+
toolSpec: {
|
1484
|
+
name: tool.name,
|
1485
|
+
description: tool.description,
|
1486
|
+
inputSchema: {
|
1487
|
+
json: tool.schema ? JSON.parse(tool.schema) : {},
|
1488
|
+
},
|
1489
|
+
},
|
1490
|
+
})),
|
1491
|
+
};
|
1492
|
+
}
|
1493
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1494
|
+
console.log(`🔍 [Bedrock] Converse request:`, JSON.stringify(request, null, 2));
|
1495
|
+
}
|
1496
|
+
const command = new ConverseStreamCommand(request);
|
1497
|
+
const response = await bedrockClient.send(command);
|
1498
|
+
if (response.stream) {
|
1499
|
+
for await (const event of response.stream) {
|
1500
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1501
|
+
console.log(`🔍 [Bedrock] Stream event:`, JSON.stringify(event));
|
1502
|
+
}
|
1503
|
+
// Handle different event types from Converse API
|
1504
|
+
if (event.contentBlockDelta) {
|
1505
|
+
const delta = event.contentBlockDelta.delta;
|
1506
|
+
const contentIndex = event.contentBlockDelta.contentBlockIndex;
|
1507
|
+
if (delta?.text) {
|
1508
|
+
const text = delta.text;
|
1509
|
+
fullMessage += text;
|
1510
|
+
tokenCount++;
|
1511
|
+
if (firstTokenTime === 0) {
|
1512
|
+
firstTokenTime = Date.now() - startTime;
|
1513
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1514
|
+
console.log(`⚡ [Bedrock] Time to First Token: ${firstTokenTime}ms`);
|
1515
|
+
}
|
1516
|
+
}
|
1517
|
+
onEvent({
|
1518
|
+
type: "token",
|
1519
|
+
token: text,
|
1520
|
+
});
|
1521
|
+
onEvent({
|
1522
|
+
type: "message",
|
1523
|
+
message: fullMessage,
|
1524
|
+
});
|
1525
|
+
}
|
1526
|
+
else if (delta?.toolUse) {
|
1527
|
+
// Handle tool use input delta
|
1528
|
+
if (delta.toolUse.input && contentIndex !== undefined) {
|
1529
|
+
// Find the corresponding tool call by index
|
1530
|
+
// Bedrock uses contentBlockIndex to correlate deltas with their starts
|
1531
|
+
const toolCall = toolCallsByIndex.get(contentIndex);
|
1532
|
+
if (toolCall) {
|
1533
|
+
toolCall.arguments += delta.toolUse.input;
|
1534
|
+
}
|
1535
|
+
}
|
1536
|
+
}
|
1537
|
+
}
|
1538
|
+
else if (event.contentBlockStart) {
|
1539
|
+
// Handle tool use start
|
1540
|
+
const start = event.contentBlockStart.start;
|
1541
|
+
const startIndex = event.contentBlockStart.contentBlockIndex;
|
1542
|
+
if (start?.toolUse && startIndex !== undefined) {
|
1543
|
+
const toolUse = start.toolUse;
|
1544
|
+
const id = toolUse.toolUseId || `tool_${Date.now()}_${toolCalls.length}`;
|
1545
|
+
// Initialize the tool call
|
1546
|
+
const toolCall = {
|
1547
|
+
id,
|
1548
|
+
name: toolUse.name || "",
|
1549
|
+
arguments: "",
|
1550
|
+
};
|
1551
|
+
// Store in both array and map
|
1552
|
+
toolCalls.push(toolCall);
|
1553
|
+
toolCallsByIndex.set(startIndex, toolCall);
|
1554
|
+
onEvent({
|
1555
|
+
type: "tool_call_start",
|
1556
|
+
toolCall: { id, name: toolUse.name || "" },
|
1557
|
+
});
|
1558
|
+
}
|
1559
|
+
}
|
1560
|
+
else if (event.contentBlockStop) {
|
1561
|
+
// Handle tool use completion
|
1562
|
+
const stopIndex = event.contentBlockStop.contentBlockIndex;
|
1563
|
+
if (stopIndex !== undefined) {
|
1564
|
+
const toolCall = toolCallsByIndex.get(stopIndex);
|
1565
|
+
if (toolCall) {
|
1566
|
+
// Emit tool_call_parsed event when tool arguments are complete
|
1567
|
+
onEvent({
|
1568
|
+
type: "tool_call_parsed",
|
1569
|
+
toolCall: toolCall,
|
1570
|
+
});
|
1571
|
+
}
|
1572
|
+
}
|
1573
|
+
}
|
1574
|
+
else if (event.metadata) {
|
1575
|
+
// Metadata events contain usage information
|
1576
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1577
|
+
console.log(`📊 [Bedrock] Metadata:`, event.metadata);
|
1578
|
+
}
|
1579
|
+
}
|
1580
|
+
}
|
1581
|
+
}
|
1582
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1583
|
+
console.log(`✅ [Bedrock] Complete. Total tokens: ${tokenCount} | Message length: ${fullMessage.length}`);
|
1584
|
+
}
|
1585
|
+
onEvent({
|
1586
|
+
type: "complete",
|
1587
|
+
tokens: tokenCount,
|
1588
|
+
});
|
1589
|
+
onComplete(fullMessage, toolCalls);
|
1590
|
+
}
|
1591
|
+
catch (error) {
|
1592
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
1593
|
+
console.error(`❌ [Bedrock] Stream error:`, error);
|
1594
|
+
}
|
1595
|
+
onEvent({
|
1596
|
+
type: "error",
|
1597
|
+
error: `Bedrock streaming error: ${error}`,
|
1598
|
+
});
|
1599
|
+
throw error;
|
1600
|
+
}
|
1601
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "graphlit-client",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.20250615001",
|
4
4
|
"description": "Graphlit API Client for TypeScript",
|
5
5
|
"type": "module",
|
6
6
|
"main": "./dist/client.js",
|
@@ -65,11 +65,27 @@
|
|
65
65
|
},
|
66
66
|
"@google/generative-ai": {
|
67
67
|
"optional": true
|
68
|
+
},
|
69
|
+
"groq-sdk": {
|
70
|
+
"optional": true
|
71
|
+
},
|
72
|
+
"cohere-ai": {
|
73
|
+
"optional": true
|
74
|
+
},
|
75
|
+
"@mistralai/mistralai": {
|
76
|
+
"optional": true
|
77
|
+
},
|
78
|
+
"@aws-sdk/client-bedrock-runtime": {
|
79
|
+
"optional": true
|
68
80
|
}
|
69
81
|
},
|
70
82
|
"optionalDependencies": {
|
71
83
|
"@anthropic-ai/sdk": "^0.53.0",
|
84
|
+
"@aws-sdk/client-bedrock-runtime": "^3.828.0",
|
72
85
|
"@google/generative-ai": "^0.24.1",
|
86
|
+
"@mistralai/mistralai": "^1.7.2",
|
87
|
+
"cohere-ai": "^7.17.1",
|
88
|
+
"groq-sdk": "^0.25.0",
|
73
89
|
"openai": "^5.3.0"
|
74
90
|
},
|
75
91
|
"devDependencies": {
|