graphlit-client 1.0.20250610006 → 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 CHANGED
@@ -1713,7 +1713,79 @@ class Graphlit {
1713
1713
  continue;
1714
1714
  }
1715
1715
  try {
1716
- const args = JSON.parse(toolCall.arguments);
1716
+ let args;
1717
+ try {
1718
+ args = JSON.parse(toolCall.arguments);
1719
+ }
1720
+ catch (parseError) {
1721
+ console.error(`Failed to parse tool arguments for ${toolCall.name}:`);
1722
+ console.error(`Arguments (${toolCall.arguments.length} chars):`, toolCall.arguments);
1723
+ console.error(`Parse error:`, parseError);
1724
+ // Check for common truncation patterns
1725
+ const lastChars = toolCall.arguments.slice(-20);
1726
+ let isTruncated = false;
1727
+ if (!toolCall.arguments.includes('}') || !lastChars.includes('}')) {
1728
+ console.error(`Possible truncation detected - arguments don't end with '}': ...${lastChars}`);
1729
+ isTruncated = true;
1730
+ }
1731
+ // Try to fix truncated JSON by adding missing closing braces
1732
+ if (isTruncated) {
1733
+ let fixedJson = toolCall.arguments.trim();
1734
+ // Count open braces vs close braces to determine how many we need
1735
+ const openBraces = (fixedJson.match(/\{/g) || []).length;
1736
+ const closeBraces = (fixedJson.match(/\}/g) || []).length;
1737
+ const missingBraces = openBraces - closeBraces;
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
+ }
1749
+ // Add missing closing quote if the string ends with an unfinished string
1750
+ else if (fixedJson.endsWith('"') === false && fixedJson.includes('"')) {
1751
+ const lastQuoteIndex = fixedJson.lastIndexOf('"');
1752
+ const afterLastQuote = fixedJson.slice(lastQuoteIndex + 1);
1753
+ if (!afterLastQuote.includes('"')) {
1754
+ fixedJson += '"';
1755
+ }
1756
+ }
1757
+ // Add missing closing braces
1758
+ fixedJson += '}'.repeat(missingBraces);
1759
+ console.log(`Attempting to fix truncated JSON by adding ${missingBraces} closing brace(s):`);
1760
+ console.log(fixedJson);
1761
+ try {
1762
+ args = JSON.parse(fixedJson);
1763
+ console.log(`✅ Successfully fixed truncated JSON for ${toolCall.name}`);
1764
+ }
1765
+ catch (fixError) {
1766
+ console.error(`❌ Failed to fix truncated JSON: ${fixError}`);
1767
+ // Fall through to error handling below
1768
+ }
1769
+ }
1770
+ }
1771
+ // If we couldn't parse or fix the JSON, log details and continue
1772
+ if (!args) {
1773
+ // Log position mentioned in error if available
1774
+ const errorMsg = parseError instanceof Error ? parseError.message : '';
1775
+ const posMatch = errorMsg.match(/position (\d+)/);
1776
+ if (posMatch) {
1777
+ const pos = parseInt(posMatch[1]);
1778
+ const context = toolCall.arguments.slice(Math.max(0, pos - 20), pos + 20);
1779
+ console.error(`Error context around position ${pos}: ...${context}...`);
1780
+ }
1781
+ // Update UI with error - use StreamEvent error type
1782
+ uiAdapter.handleEvent({
1783
+ type: "error",
1784
+ error: `Tool ${toolCall.name} failed: Invalid JSON arguments: ${parseError instanceof Error ? parseError.message : 'Unknown error'}`,
1785
+ });
1786
+ continue;
1787
+ }
1788
+ }
1717
1789
  // Update UI
1718
1790
  uiAdapter.handleEvent({
1719
1791
  type: "tool_call_start",
@@ -1,4 +1,16 @@
1
1
  import { getModelName } from "../model-mapping.js";
2
+ /**
3
+ * Helper to check if a string is valid JSON
4
+ */
5
+ function isValidJSON(str) {
6
+ try {
7
+ JSON.parse(str);
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
2
14
  /**
3
15
  * Stream with OpenAI SDK
4
16
  */
@@ -115,7 +127,7 @@ onEvent, onComplete) {
115
127
  stream: true,
116
128
  temperature: specification.anthropic?.temperature,
117
129
  //top_p: specification.anthropic?.probability,
118
- max_tokens: specification.anthropic?.completionTokenLimit || 1024, // required
130
+ max_tokens: specification.anthropic?.completionTokenLimit || 8192, // required
119
131
  };
120
132
  if (systemPrompt) {
121
133
  streamConfig.system = systemPrompt;
@@ -129,8 +141,14 @@ onEvent, onComplete) {
129
141
  }));
130
142
  }
131
143
  const stream = await anthropicClient.messages.create(streamConfig);
144
+ let activeContentBlock = false;
132
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
+ }
133
150
  if (chunk.type === "content_block_start") {
151
+ activeContentBlock = true;
134
152
  if (chunk.content_block.type === "tool_use") {
135
153
  const toolCall = {
136
154
  id: chunk.content_block.id,
@@ -160,6 +178,11 @@ onEvent, onComplete) {
160
178
  const currentTool = toolCalls[toolCalls.length - 1];
161
179
  if (currentTool) {
162
180
  currentTool.arguments += chunk.delta.partial_json;
181
+ // Debug logging for partial JSON accumulation
182
+ if (process.env.DEBUG_STREAMING) {
183
+ console.log(`[Anthropic] Tool ${currentTool.name} - Partial JSON chunk: "${chunk.delta.partial_json}"`);
184
+ console.log(`[Anthropic] Tool ${currentTool.name} - Total accumulated: ${currentTool.arguments.length} chars`);
185
+ }
163
186
  onEvent({
164
187
  type: "tool_call_delta",
165
188
  toolCallId: currentTool.id,
@@ -169,9 +192,32 @@ onEvent, onComplete) {
169
192
  }
170
193
  }
171
194
  else if (chunk.type === "content_block_stop") {
195
+ activeContentBlock = false;
172
196
  // Tool call complete
173
197
  const currentTool = toolCalls[toolCalls.length - 1];
174
198
  if (currentTool) {
199
+ // Log the final JSON for debugging
200
+ if (process.env.DEBUG_STREAMING ||
201
+ !isValidJSON(currentTool.arguments)) {
202
+ console.log(`[Anthropic] Tool ${currentTool.name} complete with arguments (${currentTool.arguments.length} chars):`);
203
+ console.log(currentTool.arguments);
204
+ // Check if JSON appears truncated
205
+ const lastChars = currentTool.arguments.slice(-10);
206
+ if (!lastChars.includes("}") &&
207
+ currentTool.arguments.length > 100) {
208
+ console.warn(`[Anthropic] WARNING: JSON may be truncated - doesn't end with '}': ...${lastChars}`);
209
+ }
210
+ // Validate JSON
211
+ try {
212
+ JSON.parse(currentTool.arguments);
213
+ if (process.env.DEBUG_STREAMING) {
214
+ console.log(`[Anthropic] ✅ Valid JSON for ${currentTool.name}`);
215
+ }
216
+ }
217
+ catch (e) {
218
+ console.error(`[Anthropic] ❌ Invalid JSON for ${currentTool.name}: ${e}`);
219
+ }
220
+ }
175
221
  onEvent({
176
222
  type: "tool_call_complete",
177
223
  toolCall: {
@@ -182,8 +228,45 @@ onEvent, onComplete) {
182
228
  });
183
229
  }
184
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
+ }
185
256
  }
186
- onComplete(fullMessage, toolCalls);
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);
187
270
  }
188
271
  catch (error) {
189
272
  onEvent({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphlit-client",
3
- "version": "1.0.20250610006",
3
+ "version": "1.0.20250610008",
4
4
  "description": "Graphlit API Client for TypeScript",
5
5
  "main": "dist/client.js",
6
6
  "types": "dist/client.d.ts",