graphlit-client 1.0.20250610007 → 1.0.20250610008
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 +12 -2
- package/dist/streaming/providers.js +45 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
@@ -1730,14 +1730,24 @@ class Graphlit {
|
|
1730
1730
|
}
|
1731
1731
|
// Try to fix truncated JSON by adding missing closing braces
|
1732
1732
|
if (isTruncated) {
|
1733
|
-
let fixedJson = toolCall.arguments;
|
1733
|
+
let fixedJson = toolCall.arguments.trim();
|
1734
1734
|
// Count open braces vs close braces to determine how many we need
|
1735
1735
|
const openBraces = (fixedJson.match(/\{/g) || []).length;
|
1736
1736
|
const closeBraces = (fixedJson.match(/\}/g) || []).length;
|
1737
1737
|
const missingBraces = openBraces - closeBraces;
|
1738
1738
|
if (missingBraces > 0) {
|
1739
|
+
// Check if we're mid-value (ends with number or boolean)
|
1740
|
+
if (fixedJson.match(/:\s*\d+$/) || fixedJson.match(/:\s*(true|false)$/)) {
|
1741
|
+
// Complete the current property and close
|
1742
|
+
fixedJson += ', "content": ""'; // Add empty content field
|
1743
|
+
}
|
1744
|
+
// Check if we're after a value but missing comma
|
1745
|
+
else if (fixedJson.match(/"\s*:\s*[^,}\s]+$/)) {
|
1746
|
+
// We have a complete value but no comma, add empty content
|
1747
|
+
fixedJson += ', "content": ""';
|
1748
|
+
}
|
1739
1749
|
// Add missing closing quote if the string ends with an unfinished string
|
1740
|
-
if (fixedJson.endsWith('"') === false && fixedJson.includes('"')) {
|
1750
|
+
else if (fixedJson.endsWith('"') === false && fixedJson.includes('"')) {
|
1741
1751
|
const lastQuoteIndex = fixedJson.lastIndexOf('"');
|
1742
1752
|
const afterLastQuote = fixedJson.slice(lastQuoteIndex + 1);
|
1743
1753
|
if (!afterLastQuote.includes('"')) {
|
@@ -141,8 +141,14 @@ onEvent, onComplete) {
|
|
141
141
|
}));
|
142
142
|
}
|
143
143
|
const stream = await anthropicClient.messages.create(streamConfig);
|
144
|
+
let activeContentBlock = false;
|
144
145
|
for await (const chunk of stream) {
|
146
|
+
// Debug log all chunk types
|
147
|
+
if (process.env.DEBUG_STREAMING) {
|
148
|
+
console.log(`[Anthropic] Received chunk type: ${chunk.type}`);
|
149
|
+
}
|
145
150
|
if (chunk.type === "content_block_start") {
|
151
|
+
activeContentBlock = true;
|
146
152
|
if (chunk.content_block.type === "tool_use") {
|
147
153
|
const toolCall = {
|
148
154
|
id: chunk.content_block.id,
|
@@ -186,6 +192,7 @@ onEvent, onComplete) {
|
|
186
192
|
}
|
187
193
|
}
|
188
194
|
else if (chunk.type === "content_block_stop") {
|
195
|
+
activeContentBlock = false;
|
189
196
|
// Tool call complete
|
190
197
|
const currentTool = toolCalls[toolCalls.length - 1];
|
191
198
|
if (currentTool) {
|
@@ -221,8 +228,45 @@ onEvent, onComplete) {
|
|
221
228
|
});
|
222
229
|
}
|
223
230
|
}
|
231
|
+
else if (chunk.type === "message_stop" && activeContentBlock) {
|
232
|
+
// Handle Anthropic bug: message_stop without content_block_stop
|
233
|
+
console.warn(`[Anthropic] Received message_stop without content_block_stop - handling as implicit block stop`);
|
234
|
+
activeContentBlock = false;
|
235
|
+
// Emit synthetic content_block_stop for the current tool
|
236
|
+
const currentTool = toolCalls[toolCalls.length - 1];
|
237
|
+
if (currentTool) {
|
238
|
+
// Log the incomplete tool
|
239
|
+
console.warn(`[Anthropic] Synthetic content_block_stop for incomplete tool ${currentTool.name} (${currentTool.arguments.length} chars)`);
|
240
|
+
// Only emit tool_call_complete if we have valid JSON
|
241
|
+
if (isValidJSON(currentTool.arguments)) {
|
242
|
+
onEvent({
|
243
|
+
type: "tool_call_complete",
|
244
|
+
toolCall: {
|
245
|
+
id: currentTool.id,
|
246
|
+
name: currentTool.name,
|
247
|
+
arguments: currentTool.arguments,
|
248
|
+
},
|
249
|
+
});
|
250
|
+
}
|
251
|
+
else {
|
252
|
+
console.error(`[Anthropic] Tool ${currentTool.name} has incomplete JSON, skipping tool_call_complete event`);
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
224
256
|
}
|
225
|
-
|
257
|
+
// Final check: filter out any remaining incomplete tool calls
|
258
|
+
const validToolCalls = toolCalls.filter((tc, idx) => {
|
259
|
+
if (!isValidJSON(tc.arguments)) {
|
260
|
+
console.warn(`[Anthropic] Filtering out incomplete tool call ${idx} (${tc.name}) with INVALID JSON (${tc.arguments.length} chars)`);
|
261
|
+
return false;
|
262
|
+
}
|
263
|
+
return true;
|
264
|
+
});
|
265
|
+
if (toolCalls.length !== validToolCalls.length) {
|
266
|
+
console.log(`[Anthropic] Filtered out ${toolCalls.length - validToolCalls.length} incomplete tool calls`);
|
267
|
+
console.log(`[Anthropic] Successfully processed ${validToolCalls.length} valid tool calls`);
|
268
|
+
}
|
269
|
+
onComplete(fullMessage, validToolCalls);
|
226
270
|
}
|
227
271
|
catch (error) {
|
228
272
|
onEvent({
|