@usecrow/ui 0.1.6 → 0.1.7

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/index.d.cts CHANGED
@@ -390,6 +390,7 @@ interface WorkflowEvent {
390
390
  interface ToolCallEvent {
391
391
  type: "start" | "complete" | "client_call";
392
392
  toolName: string;
393
+ toolCallId?: string;
393
394
  arguments?: Record<string, unknown>;
394
395
  success?: boolean;
395
396
  }
@@ -417,6 +418,7 @@ declare function useChat({ productId, apiUrl, onVerificationStatus, onConversati
417
418
  stopGeneration: () => void;
418
419
  resetMessages: () => void;
419
420
  loadMessages: (historyMessages: Message[]) => void;
421
+ submitToolResult: (toolCallId: string, toolName: string, result: Record<string, unknown>) => Promise<void>;
420
422
  };
421
423
 
422
424
  /**
package/dist/index.d.ts CHANGED
@@ -390,6 +390,7 @@ interface WorkflowEvent {
390
390
  interface ToolCallEvent {
391
391
  type: "start" | "complete" | "client_call";
392
392
  toolName: string;
393
+ toolCallId?: string;
393
394
  arguments?: Record<string, unknown>;
394
395
  success?: boolean;
395
396
  }
@@ -417,6 +418,7 @@ declare function useChat({ productId, apiUrl, onVerificationStatus, onConversati
417
418
  stopGeneration: () => void;
418
419
  resetMessages: () => void;
419
420
  loadMessages: (historyMessages: Message[]) => void;
421
+ submitToolResult: (toolCallId: string, toolName: string, result: Record<string, unknown>) => Promise<void>;
420
422
  };
421
423
 
422
424
  /**
package/dist/index.js CHANGED
@@ -100,6 +100,7 @@ function useChat({
100
100
  async (message, botMsgId) => {
101
101
  let accumulatedText = "";
102
102
  let firstChunk = true;
103
+ const pendingClientTools = [];
103
104
  abortControllerRef.current = new AbortController();
104
105
  try {
105
106
  const identityToken = window.__crow_identity_token;
@@ -131,6 +132,14 @@ function useChat({
131
132
  const data = line.slice(6).trim();
132
133
  if (data === "[DONE]") {
133
134
  setIsLoading(false);
135
+ for (const tool of pendingClientTools) {
136
+ onToolCall?.({
137
+ type: "client_call",
138
+ toolName: tool.toolName,
139
+ toolCallId: tool.toolCallId,
140
+ arguments: tool.arguments
141
+ });
142
+ }
134
143
  return;
135
144
  }
136
145
  try {
@@ -227,9 +236,9 @@ function useChat({
227
236
  );
228
237
  break;
229
238
  case "client_tool_call":
230
- onToolCall?.({
231
- type: "client_call",
239
+ pendingClientTools.push({
232
240
  toolName: parsed.tool_name,
241
+ toolCallId: parsed.tool_call_id,
233
242
  arguments: parsed.arguments
234
243
  });
235
244
  break;
@@ -343,6 +352,102 @@ function useChat({
343
352
  const loadMessages = useCallback((historyMessages) => {
344
353
  setMessages(historyMessages);
345
354
  }, []);
355
+ const submitToolResult = useCallback(
356
+ async (toolCallId, toolName, result) => {
357
+ if (!conversationId) {
358
+ console.error("[Crow] Cannot submit tool result: no conversation ID");
359
+ return;
360
+ }
361
+ const botMsgId = generateMessageId("bot");
362
+ const pendingClientTools = [];
363
+ setMessages((prev) => [
364
+ ...prev,
365
+ {
366
+ id: botMsgId,
367
+ content: "",
368
+ isBot: true,
369
+ timestamp: /* @__PURE__ */ new Date()
370
+ }
371
+ ]);
372
+ setIsLoading(true);
373
+ try {
374
+ const identityToken = window.__crow_identity_token;
375
+ const response = await fetch(`${apiUrl}/api/chat/tool-result`, {
376
+ method: "POST",
377
+ headers: { "Content-Type": "application/json" },
378
+ body: JSON.stringify({
379
+ product_id: productId,
380
+ conversation_id: conversationId,
381
+ tool_call_id: toolCallId,
382
+ tool_name: toolName,
383
+ result,
384
+ identity_token: identityToken,
385
+ model: selectedModel
386
+ })
387
+ });
388
+ if (!response.ok) {
389
+ throw new Error(`HTTP error! status: ${response.status}`);
390
+ }
391
+ const reader = response.body?.getReader();
392
+ const decoder = new TextDecoder();
393
+ let accumulatedText = "";
394
+ if (reader) {
395
+ while (true) {
396
+ const { done, value } = await reader.read();
397
+ if (done) break;
398
+ const chunk = decoder.decode(value);
399
+ const lines = chunk.split("\n");
400
+ for (const line of lines) {
401
+ if (line.startsWith("data: ")) {
402
+ const data = line.slice(6).trim();
403
+ if (data === "[DONE]") {
404
+ setIsLoading(false);
405
+ for (const tool of pendingClientTools) {
406
+ onToolCall?.({
407
+ type: "client_call",
408
+ toolName: tool.toolName,
409
+ toolCallId: tool.toolCallId,
410
+ arguments: tool.arguments
411
+ });
412
+ }
413
+ return;
414
+ }
415
+ try {
416
+ const parsed = JSON.parse(data);
417
+ if (parsed.type === "content") {
418
+ accumulatedText += parsed.content;
419
+ setMessages(
420
+ (prev) => prev.map(
421
+ (msg) => msg.id === botMsgId ? { ...msg, content: accumulatedText } : msg
422
+ )
423
+ );
424
+ } else if (parsed.type === "client_tool_call") {
425
+ pendingClientTools.push({
426
+ toolName: parsed.tool_name,
427
+ toolCallId: parsed.tool_call_id,
428
+ arguments: parsed.arguments
429
+ });
430
+ }
431
+ } catch (e) {
432
+ console.error("[Crow] Parse error in tool result response:", e);
433
+ }
434
+ }
435
+ }
436
+ }
437
+ }
438
+ } catch (error) {
439
+ console.error("[Crow] Error submitting tool result:", error);
440
+ setMessages(
441
+ (prev) => prev.map(
442
+ (msg) => msg.id === botMsgId ? { ...msg, content: "Sorry, I encountered an error processing the tool result." } : msg
443
+ )
444
+ );
445
+ } finally {
446
+ setIsLoading(false);
447
+ }
448
+ },
449
+ [apiUrl, productId, conversationId, selectedModel]
450
+ );
346
451
  return {
347
452
  messages,
348
453
  isLoading,
@@ -354,7 +459,8 @@ function useChat({
354
459
  sendMessage,
355
460
  stopGeneration,
356
461
  resetMessages,
357
- loadMessages
462
+ loadMessages,
463
+ submitToolResult
358
464
  };
359
465
  }
360
466
  function useConversations({ productId, apiUrl = "" }) {
@@ -2088,6 +2194,7 @@ function CrowWidget({
2088
2194
  });
2089
2195
  const messagesContainerRef = useRef(null);
2090
2196
  const executeClientToolRef = useRef(null);
2197
+ const submitToolResultRef = useRef(null);
2091
2198
  const [isCollapsed, setIsCollapsed] = useState(variant === "floating");
2092
2199
  const [showConversationList, setShowConversationList] = useState(false);
2093
2200
  const [isVerifiedUser, setIsVerifiedUser] = useState(false);
@@ -2134,11 +2241,30 @@ function CrowWidget({
2134
2241
  break;
2135
2242
  }
2136
2243
  },
2137
- onToolCall: (event) => {
2138
- if (event.type === "client_call" && event.toolName) {
2139
- console.log("[Crow Widget] Executing client tool:", event.toolName, event.arguments);
2140
- const result = executeClientToolRef.current?.(event.toolName, event.arguments || {});
2141
- result?.then((r) => console.log("[Crow Widget] Tool result:", r)).catch((e) => console.error("[Crow Widget] Tool error:", e));
2244
+ onToolCall: async (event) => {
2245
+ if (event.type === "client_call" && event.toolName && event.toolCallId) {
2246
+ try {
2247
+ const result = await executeClientToolRef.current?.(
2248
+ event.toolName,
2249
+ event.arguments || {}
2250
+ );
2251
+ if (result && submitToolResultRef.current) {
2252
+ await submitToolResultRef.current(
2253
+ event.toolCallId,
2254
+ event.toolName,
2255
+ result
2256
+ );
2257
+ }
2258
+ } catch (e) {
2259
+ console.error("[Crow Widget] Tool error:", e);
2260
+ if (submitToolResultRef.current) {
2261
+ await submitToolResultRef.current(
2262
+ event.toolCallId,
2263
+ event.toolName,
2264
+ { success: false, error: String(e) }
2265
+ );
2266
+ }
2267
+ }
2142
2268
  }
2143
2269
  },
2144
2270
  onRestoredConversation: () => {
@@ -2184,6 +2310,7 @@ function CrowWidget({
2184
2310
  }
2185
2311
  });
2186
2312
  executeClientToolRef.current = executeClientTool;
2313
+ submitToolResultRef.current = chat.submitToolResult;
2187
2314
  useEffect(() => {
2188
2315
  if (!isLoadingStyles) {
2189
2316
  onReady?.();