@devicai/ui 0.62.3 → 0.62.4

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.
Files changed (27) hide show
  1. package/README.md +14 -1
  2. package/dist/cjs/api/types.js.map +1 -1
  3. package/dist/cjs/components/AICommandBar/useAICommandBar.js +5 -5
  4. package/dist/cjs/components/AICommandBar/useAICommandBar.js.map +1 -1
  5. package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js +5 -5
  6. package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
  7. package/dist/cjs/components/AIGenerationButton/useAIGenerationButton.js +5 -5
  8. package/dist/cjs/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
  9. package/dist/cjs/hooks/useDevicChat.js +7 -6
  10. package/dist/cjs/hooks/useDevicChat.js.map +1 -1
  11. package/dist/cjs/hooks/useModelInterface.js +109 -33
  12. package/dist/cjs/hooks/useModelInterface.js.map +1 -1
  13. package/dist/esm/api/types.d.ts +16 -0
  14. package/dist/esm/api/types.js.map +1 -1
  15. package/dist/esm/components/AICommandBar/useAICommandBar.js +5 -5
  16. package/dist/esm/components/AICommandBar/useAICommandBar.js.map +1 -1
  17. package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js +5 -5
  18. package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
  19. package/dist/esm/components/AIGenerationButton/useAIGenerationButton.js +5 -5
  20. package/dist/esm/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
  21. package/dist/esm/hooks/useDevicChat.js +7 -6
  22. package/dist/esm/hooks/useDevicChat.js.map +1 -1
  23. package/dist/esm/hooks/useModelInterface.d.ts +54 -1
  24. package/dist/esm/hooks/useModelInterface.js +108 -35
  25. package/dist/esm/hooks/useModelInterface.js.map +1 -1
  26. package/dist/esm/index.d.ts +1 -1
  27. package/package.json +1 -1
@@ -1,5 +1,75 @@
1
- import { useMemo, useCallback } from 'react';
1
+ import { useMemo, useRef, useCallback } from 'react';
2
2
 
3
+ /**
4
+ * How long a call to a tool this client does not have is given to show up
5
+ * before it is answered as unavailable. Tools come and go with the screen the
6
+ * user is on, and the screen the model has just navigated to may not have
7
+ * registered its own yet.
8
+ */
9
+ const UNAVAILABLE_TOOL_GRACE_MS = 5000;
10
+ const UNAVAILABLE_TOOL_CHECK_MS = 250;
11
+ /**
12
+ * The answer to a call for a client-side tool this client does not have loaded
13
+ * — typically one registered by a screen the user has since left.
14
+ *
15
+ * Answering is the point. The API holds the conversation in
16
+ * `waiting_for_tool_response` until every call it handed to the client is
17
+ * answered, and nothing else will answer this one: the run never resumes, and
18
+ * every later message is refused or parked behind it.
19
+ */
20
+ function unavailableToolResponse(toolCall) {
21
+ return {
22
+ tool_call_id: toolCall.id,
23
+ content: {
24
+ error: `Tool "${toolCall.function.name}" is not available: the application no longer offers it in its current context (the user may have moved to another screen). Do not call it again unless it is offered again.`,
25
+ errorType: 'TOOL_UNAVAILABLE',
26
+ },
27
+ role: 'tool',
28
+ };
29
+ }
30
+ /**
31
+ * The calls still unanswered in the most recent assistant message that made
32
+ * any, narrowed to the ones `include` accepts.
33
+ */
34
+ function unansweredToolCalls(messages, include) {
35
+ for (let i = messages.length - 1; i >= 0; i--) {
36
+ const message = messages[i];
37
+ if (message.role !== 'assistant' || !message.tool_calls?.length)
38
+ continue;
39
+ const answered = new Set(messages
40
+ .slice(i + 1)
41
+ .filter((m) => m.role === 'tool')
42
+ .map((m) => m.tool_call_id));
43
+ return message.tool_calls.filter((toolCall) => include(toolCall) && !answered.has(toolCall.id));
44
+ }
45
+ return [];
46
+ }
47
+ /**
48
+ * The tool calls a client owes an answer to, given a realtime snapshot.
49
+ *
50
+ * While a run is going, only the calls to the client's own tools are its
51
+ * business: anything else in the same message is a backend tool the run is
52
+ * still executing. Once the API reports `waiting_for_tool_response` the backend
53
+ * has done its part — it runs its own tools before pausing — so every call
54
+ * still unanswered is waiting on the client, including one to a tool that is
55
+ * no longer loaded. Those are returned too, so they can be answered as
56
+ * unavailable instead of blocking the conversation.
57
+ *
58
+ * The exception is a backend tool that answers asynchronously: the
59
+ * conversation waits on it as well, but the answer comes from an external
60
+ * system, and the API lists it in `pendingAsyncToolCalls`.
61
+ */
62
+ function resolvePendingToolCalls(data, isClientTool) {
63
+ if (data.pendingToolCalls)
64
+ return data.pendingToolCalls;
65
+ const messages = data.chatHistory ?? [];
66
+ if (data.status !== 'waiting_for_tool_response') {
67
+ return unansweredToolCalls(messages, (toolCall) => isClientTool(toolCall.function.name));
68
+ }
69
+ const answeredElsewhere = new Set((data.pendingAsyncToolCalls ?? []).map((call) => call.toolCallId));
70
+ return unansweredToolCalls(messages, (toolCall) => !answeredElsewhere.has(toolCall.id));
71
+ }
72
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3
73
  /**
4
74
  * Hook for implementing the Model Interface Protocol
5
75
  *
@@ -31,7 +101,7 @@ import { useMemo, useCallback } from 'react';
31
101
  * ```
32
102
  */
33
103
  function useModelInterface(options) {
34
- const { tools, onToolExecute, onToolComplete, onToolError } = options;
104
+ const { tools, onToolExecute, onToolComplete, onToolError, unavailableToolGraceMs = UNAVAILABLE_TOOL_GRACE_MS, } = options;
35
105
  // Extract tool schemas for API
36
106
  const toolSchemas = useMemo(() => {
37
107
  return tools.map((tool) => tool.schema);
@@ -40,6 +110,12 @@ function useModelInterface(options) {
40
110
  const toolMap = useMemo(() => {
41
111
  return new Map(tools.map((tool) => [tool.toolName, tool]));
42
112
  }, [tools]);
113
+ // The tools as of the latest render: a call waiting for a tool to appear
114
+ // has to see the ones registered after it started.
115
+ const toolMapRef = useRef(toolMap);
116
+ toolMapRef.current = toolMap;
117
+ const toolSchemasRef = useRef(toolSchemas);
118
+ toolSchemasRef.current = toolSchemas;
43
119
  // Check if a tool is a client-side tool
44
120
  const isClientTool = useCallback((toolName) => {
45
121
  return toolMap.has(toolName);
@@ -50,11 +126,8 @@ function useModelInterface(options) {
50
126
  const handleToolCalls = useCallback(async (toolCalls) => {
51
127
  const responses = [];
52
128
  const widgetCalls = [];
53
- for (const toolCall of toolCalls) {
129
+ const handle = async (toolCall, tool) => {
54
130
  const toolName = toolCall.function.name;
55
- const tool = toolMap.get(toolName);
56
- if (!tool)
57
- continue;
58
131
  let params = {};
59
132
  try {
60
133
  params = JSON.parse(toolCall.function.arguments || '{}');
@@ -70,7 +143,7 @@ function useModelInterface(options) {
70
143
  widget: tool.responseWidget,
71
144
  toolName,
72
145
  });
73
- continue;
146
+ return;
74
147
  }
75
148
  if (!tool.callback) {
76
149
  // Neither callback nor widget — respond with error so the model can continue
@@ -79,7 +152,7 @@ function useModelInterface(options) {
79
152
  content: { error: `Tool "${toolName}" has no callback or responseWidget` },
80
153
  role: 'tool',
81
154
  });
82
- continue;
155
+ return;
83
156
  }
84
157
  try {
85
158
  const result = await tool.callback(params);
@@ -99,35 +172,34 @@ function useModelInterface(options) {
99
172
  role: 'tool',
100
173
  });
101
174
  }
175
+ };
176
+ const missing = [];
177
+ for (const toolCall of toolCalls) {
178
+ const tool = toolMapRef.current.get(toolCall.function.name);
179
+ if (tool)
180
+ await handle(toolCall, tool);
181
+ else
182
+ missing.push(toolCall);
102
183
  }
103
- return { responses, widgetCalls };
104
- }, [toolMap, onToolExecute, onToolComplete, onToolError]);
105
- // Extract pending tool calls from messages that need client handling
106
- const extractPendingToolCalls = useCallback((messages) => {
107
- const pendingCalls = [];
108
- // Look at the last assistant message
109
- for (let i = messages.length - 1; i >= 0; i--) {
110
- const message = messages[i];
111
- if (message.role === 'assistant' && message.tool_calls?.length) {
112
- // Filter for client-side tools only
113
- const clientToolCalls = message.tool_calls.filter((tc) => isClientTool(tc.function.name));
114
- // Check if these tool calls have been responded to
115
- const respondedToolIds = new Set(messages
116
- .slice(i + 1)
117
- .filter((m) => m.role === 'tool')
118
- .map((m) => m.tool_call_id));
119
- // Get unresponded tool calls
120
- for (const tc of clientToolCalls) {
121
- if (!respondedToolIds.has(tc.id)) {
122
- pendingCalls.push(tc);
123
- }
124
- }
125
- // Only check the most recent assistant message with tool calls
126
- break;
184
+ if (missing.length > 0) {
185
+ const allLoaded = () => missing.every((toolCall) => toolMapRef.current.has(toolCall.function.name));
186
+ const deadline = Date.now() + unavailableToolGraceMs;
187
+ while (!allLoaded() && Date.now() < deadline) {
188
+ await sleep(Math.min(UNAVAILABLE_TOOL_CHECK_MS, deadline - Date.now()));
189
+ }
190
+ for (const toolCall of missing) {
191
+ const tool = toolMapRef.current.get(toolCall.function.name);
192
+ if (tool)
193
+ await handle(toolCall, tool);
194
+ else
195
+ responses.push(unavailableToolResponse(toolCall));
127
196
  }
128
197
  }
129
- return pendingCalls;
130
- }, [isClientTool]);
198
+ return { responses, widgetCalls, toolSchemas: toolSchemasRef.current };
199
+ }, [onToolExecute, onToolComplete, onToolError, unavailableToolGraceMs]);
200
+ // Extract pending tool calls from messages that need client handling
201
+ const extractPendingToolCalls = useCallback((messages) => unansweredToolCalls(messages, (toolCall) => isClientTool(toolCall.function.name)), [isClientTool]);
202
+ const resolvePending = useCallback((data) => resolvePendingToolCalls(data, isClientTool), [isClientTool]);
131
203
  return {
132
204
  toolSchemas,
133
205
  isClientTool,
@@ -135,8 +207,9 @@ function useModelInterface(options) {
135
207
  getTool,
136
208
  handleToolCalls,
137
209
  extractPendingToolCalls,
210
+ resolvePendingToolCalls: resolvePending,
138
211
  };
139
212
  }
140
213
 
141
- export { useModelInterface };
214
+ export { UNAVAILABLE_TOOL_GRACE_MS, resolvePendingToolCalls, unavailableToolResponse, useModelInterface };
142
215
  //# sourceMappingURL=useModelInterface.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModelInterface.js","sources":["../../../src/hooks/useModelInterface.ts"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport type {\n ModelInterfaceTool,\n ModelInterfaceToolSchema,\n ToolCall,\n ToolCallResponse,\n ChatMessage,\n ResponseWidgetConfig,\n} from '../api/types';\n\nexport interface PendingWidgetCall {\n toolCall: ToolCall;\n params: any;\n widget: ResponseWidgetConfig;\n toolName: string;\n}\n\nexport interface HandleToolCallsResult {\n /** Responses ready to send back to the API (from callback-based tools) */\n responses: ToolCallResponse[];\n /** Tool calls that require user interaction via a response widget */\n widgetCalls: PendingWidgetCall[];\n}\n\nexport interface UseModelInterfaceOptions {\n /**\n * Client-side tools available for model interface protocol\n */\n tools: ModelInterfaceTool[];\n\n /**\n * Callback when a tool is being executed\n */\n onToolExecute?: (toolName: string, params: any) => void;\n\n /**\n * Callback when a tool execution completes\n */\n onToolComplete?: (toolName: string, result: any) => void;\n\n /**\n * Callback when a tool execution fails\n */\n onToolError?: (toolName: string, error: Error) => void;\n}\n\nexport interface UseModelInterfaceResult {\n /**\n * Tool schemas to send to the API\n */\n toolSchemas: ModelInterfaceToolSchema[];\n\n /**\n * Check if a tool call should be handled client-side\n */\n isClientTool: (toolName: string) => boolean;\n\n /**\n * Check if a tool has a response widget (user-driven) instead of a callback\n */\n hasResponseWidget: (toolName: string) => boolean;\n\n /**\n * Get the tool definition by name\n */\n getTool: (toolName: string) => ModelInterfaceTool | undefined;\n\n /**\n * Handle tool calls from the model.\n * Callback-based tools are executed immediately and their responses returned.\n * Widget-based tools are returned as pending widget calls for user interaction.\n */\n handleToolCalls: (toolCalls: ToolCall[]) => Promise<HandleToolCallsResult>;\n\n /**\n * Process messages and extract pending tool calls that need client handling\n */\n extractPendingToolCalls: (messages: ChatMessage[]) => ToolCall[];\n}\n\n/**\n * Hook for implementing the Model Interface Protocol\n *\n * The Model Interface Protocol allows client-side tools to be executed\n * during an assistant conversation. When the model calls a client-side tool,\n * this hook handles executing the tool and preparing the response.\n *\n * @example\n * ```tsx\n * const { toolSchemas, handleToolCalls } = useModelInterface({\n * tools: [\n * {\n * toolName: 'get_user_location',\n * schema: {\n * type: 'function',\n * function: {\n * name: 'get_user_location',\n * description: 'Get user current location',\n * parameters: { type: 'object', properties: {} }\n * }\n * },\n * callback: async () => {\n * const pos = await getCurrentPosition();\n * return { lat: pos.coords.latitude, lng: pos.coords.longitude };\n * }\n * }\n * ]\n * });\n * ```\n */\nexport function useModelInterface(\n options: UseModelInterfaceOptions\n): UseModelInterfaceResult {\n const { tools, onToolExecute, onToolComplete, onToolError } = options;\n\n // Extract tool schemas for API\n const toolSchemas = useMemo(() => {\n return tools.map((tool) => tool.schema);\n }, [tools]);\n\n // Map of tool name to tool definition\n const toolMap = useMemo(() => {\n return new Map(tools.map((tool) => [tool.toolName, tool]));\n }, [tools]);\n\n // Check if a tool is a client-side tool\n const isClientTool = useCallback(\n (toolName: string): boolean => {\n return toolMap.has(toolName);\n },\n [toolMap]\n );\n\n const hasResponseWidget = useCallback(\n (toolName: string): boolean => Boolean(toolMap.get(toolName)?.responseWidget),\n [toolMap]\n );\n\n const getTool = useCallback(\n (toolName: string): ModelInterfaceTool | undefined => toolMap.get(toolName),\n [toolMap]\n );\n\n // Handle tool calls: execute callback tools, queue widget tools for user input\n const handleToolCalls = useCallback(\n async (toolCalls: ToolCall[]): Promise<HandleToolCallsResult> => {\n const responses: ToolCallResponse[] = [];\n const widgetCalls: PendingWidgetCall[] = [];\n\n for (const toolCall of toolCalls) {\n const toolName = toolCall.function.name;\n const tool = toolMap.get(toolName);\n\n if (!tool) continue;\n\n let params: any = {};\n try {\n params = JSON.parse(toolCall.function.arguments || '{}');\n } catch {\n // Keep empty params if parsing fails\n }\n\n onToolExecute?.(toolName, params);\n\n if (tool.responseWidget) {\n widgetCalls.push({\n toolCall,\n params,\n widget: tool.responseWidget,\n toolName,\n });\n continue;\n }\n\n if (!tool.callback) {\n // Neither callback nor widget — respond with error so the model can continue\n responses.push({\n tool_call_id: toolCall.id,\n content: { error: `Tool \"${toolName}\" has no callback or responseWidget` },\n role: 'tool',\n });\n continue;\n }\n\n try {\n const result = await tool.callback(params);\n onToolComplete?.(toolName, result);\n responses.push({\n tool_call_id: toolCall.id,\n content: result,\n role: 'tool',\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onToolError?.(toolName, error);\n responses.push({\n tool_call_id: toolCall.id,\n content: { error: error.message },\n role: 'tool',\n });\n }\n }\n\n return { responses, widgetCalls };\n },\n [toolMap, onToolExecute, onToolComplete, onToolError]\n );\n\n // Extract pending tool calls from messages that need client handling\n const extractPendingToolCalls = useCallback(\n (messages: ChatMessage[]): ToolCall[] => {\n const pendingCalls: ToolCall[] = [];\n\n // Look at the last assistant message\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i];\n\n if (message.role === 'assistant' && message.tool_calls?.length) {\n // Filter for client-side tools only\n const clientToolCalls = message.tool_calls.filter((tc) =>\n isClientTool(tc.function.name)\n );\n\n // Check if these tool calls have been responded to\n const respondedToolIds = new Set(\n messages\n .slice(i + 1)\n .filter((m) => m.role === 'tool')\n .map((m) => m.tool_call_id)\n );\n\n // Get unresponded tool calls\n for (const tc of clientToolCalls) {\n if (!respondedToolIds.has(tc.id)) {\n pendingCalls.push(tc);\n }\n }\n\n // Only check the most recent assistant message with tool calls\n break;\n }\n }\n\n return pendingCalls;\n },\n [isClientTool]\n );\n\n return {\n toolSchemas,\n isClientTool,\n hasResponseWidget,\n getTool,\n handleToolCalls,\n extractPendingToolCalls,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useModelInterface.js","sources":["../../../src/hooks/useModelInterface.ts"],"sourcesContent":["import { useCallback, useMemo, useRef } from 'react';\nimport type {\n ModelInterfaceTool,\n ModelInterfaceToolSchema,\n ToolCall,\n ToolCallResponse,\n ChatMessage,\n RealtimeChatHistory,\n ResponseWidgetConfig,\n} from '../api/types';\n\n/**\n * How long a call to a tool this client does not have is given to show up\n * before it is answered as unavailable. Tools come and go with the screen the\n * user is on, and the screen the model has just navigated to may not have\n * registered its own yet.\n */\nexport const UNAVAILABLE_TOOL_GRACE_MS = 5_000;\nconst UNAVAILABLE_TOOL_CHECK_MS = 250;\n\n/**\n * The answer to a call for a client-side tool this client does not have loaded\n * — typically one registered by a screen the user has since left.\n *\n * Answering is the point. The API holds the conversation in\n * `waiting_for_tool_response` until every call it handed to the client is\n * answered, and nothing else will answer this one: the run never resumes, and\n * every later message is refused or parked behind it.\n */\nexport function unavailableToolResponse(toolCall: ToolCall): ToolCallResponse {\n return {\n tool_call_id: toolCall.id,\n content: {\n error: `Tool \"${toolCall.function.name}\" is not available: the application no longer offers it in its current context (the user may have moved to another screen). Do not call it again unless it is offered again.`,\n errorType: 'TOOL_UNAVAILABLE',\n },\n role: 'tool',\n };\n}\n\n/**\n * The calls still unanswered in the most recent assistant message that made\n * any, narrowed to the ones `include` accepts.\n */\nfunction unansweredToolCalls(\n messages: ChatMessage[],\n include: (toolCall: ToolCall) => boolean\n): ToolCall[] {\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i];\n if (message.role !== 'assistant' || !message.tool_calls?.length) continue;\n\n const answered = new Set(\n messages\n .slice(i + 1)\n .filter((m) => m.role === 'tool')\n .map((m) => m.tool_call_id)\n );\n return message.tool_calls.filter(\n (toolCall) => include(toolCall) && !answered.has(toolCall.id)\n );\n }\n return [];\n}\n\n/**\n * The tool calls a client owes an answer to, given a realtime snapshot.\n *\n * While a run is going, only the calls to the client's own tools are its\n * business: anything else in the same message is a backend tool the run is\n * still executing. Once the API reports `waiting_for_tool_response` the backend\n * has done its part — it runs its own tools before pausing — so every call\n * still unanswered is waiting on the client, including one to a tool that is\n * no longer loaded. Those are returned too, so they can be answered as\n * unavailable instead of blocking the conversation.\n *\n * The exception is a backend tool that answers asynchronously: the\n * conversation waits on it as well, but the answer comes from an external\n * system, and the API lists it in `pendingAsyncToolCalls`.\n */\nexport function resolvePendingToolCalls(\n data: Pick<\n RealtimeChatHistory,\n 'status' | 'chatHistory' | 'pendingToolCalls' | 'pendingAsyncToolCalls'\n >,\n isClientTool: (toolName: string) => boolean\n): ToolCall[] {\n if (data.pendingToolCalls) return data.pendingToolCalls;\n\n const messages = data.chatHistory ?? [];\n if (data.status !== 'waiting_for_tool_response') {\n return unansweredToolCalls(messages, (toolCall) =>\n isClientTool(toolCall.function.name)\n );\n }\n\n const answeredElsewhere = new Set(\n (data.pendingAsyncToolCalls ?? []).map((call) => call.toolCallId)\n );\n return unansweredToolCalls(\n messages,\n (toolCall) => !answeredElsewhere.has(toolCall.id)\n );\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport interface PendingWidgetCall {\n toolCall: ToolCall;\n params: any;\n widget: ResponseWidgetConfig;\n toolName: string;\n}\n\nexport interface HandleToolCallsResult {\n /** Responses ready to send back to the API (from callback-based tools) */\n responses: ToolCallResponse[];\n /** Tool calls that require user interaction via a response widget */\n widgetCalls: PendingWidgetCall[];\n /**\n * The tool schemas on offer once the calls were handled — what to restate\n * alongside the responses. It can differ from the ones at the start of the\n * call when the tools changed while an unavailable one was being waited for.\n */\n toolSchemas: ModelInterfaceToolSchema[];\n}\n\nexport interface UseModelInterfaceOptions {\n /**\n * Client-side tools available for model interface protocol\n */\n tools: ModelInterfaceTool[];\n\n /**\n * Callback when a tool is being executed\n */\n onToolExecute?: (toolName: string, params: any) => void;\n\n /**\n * Callback when a tool execution completes\n */\n onToolComplete?: (toolName: string, result: any) => void;\n\n /**\n * Callback when a tool execution fails\n */\n onToolError?: (toolName: string, error: Error) => void;\n\n /**\n * How long (ms) a call to a tool that is not loaded waits for it to appear\n * before it is answered as unavailable.\n * @default 5000\n */\n unavailableToolGraceMs?: number;\n}\n\nexport interface UseModelInterfaceResult {\n /**\n * Tool schemas to send to the API\n */\n toolSchemas: ModelInterfaceToolSchema[];\n\n /**\n * Check if a tool call should be handled client-side\n */\n isClientTool: (toolName: string) => boolean;\n\n /**\n * Check if a tool has a response widget (user-driven) instead of a callback\n */\n hasResponseWidget: (toolName: string) => boolean;\n\n /**\n * Get the tool definition by name\n */\n getTool: (toolName: string) => ModelInterfaceTool | undefined;\n\n /**\n * Handle tool calls from the model.\n * Callback-based tools are executed immediately and their responses returned.\n * Widget-based tools are returned as pending widget calls for user interaction.\n * A call to a tool that is not loaded waits `unavailableToolGraceMs` for it to\n * appear, and is otherwise answered as unavailable.\n */\n handleToolCalls: (toolCalls: ToolCall[]) => Promise<HandleToolCallsResult>;\n\n /**\n * Process messages and extract pending tool calls that need client handling\n */\n extractPendingToolCalls: (messages: ChatMessage[]) => ToolCall[];\n\n /**\n * The tool calls this client owes an answer to in a realtime snapshot: its\n * own pending calls, plus — while the API waits for a tool response — calls\n * to tools it does not have loaded, which must still be answered.\n */\n resolvePendingToolCalls: (data: RealtimeChatHistory) => ToolCall[];\n}\n\n/**\n * Hook for implementing the Model Interface Protocol\n *\n * The Model Interface Protocol allows client-side tools to be executed\n * during an assistant conversation. When the model calls a client-side tool,\n * this hook handles executing the tool and preparing the response.\n *\n * @example\n * ```tsx\n * const { toolSchemas, handleToolCalls } = useModelInterface({\n * tools: [\n * {\n * toolName: 'get_user_location',\n * schema: {\n * type: 'function',\n * function: {\n * name: 'get_user_location',\n * description: 'Get user current location',\n * parameters: { type: 'object', properties: {} }\n * }\n * },\n * callback: async () => {\n * const pos = await getCurrentPosition();\n * return { lat: pos.coords.latitude, lng: pos.coords.longitude };\n * }\n * }\n * ]\n * });\n * ```\n */\nexport function useModelInterface(\n options: UseModelInterfaceOptions\n): UseModelInterfaceResult {\n const {\n tools,\n onToolExecute,\n onToolComplete,\n onToolError,\n unavailableToolGraceMs = UNAVAILABLE_TOOL_GRACE_MS,\n } = options;\n\n // Extract tool schemas for API\n const toolSchemas = useMemo(() => {\n return tools.map((tool) => tool.schema);\n }, [tools]);\n\n // Map of tool name to tool definition\n const toolMap = useMemo(() => {\n return new Map(tools.map((tool) => [tool.toolName, tool]));\n }, [tools]);\n\n // The tools as of the latest render: a call waiting for a tool to appear\n // has to see the ones registered after it started.\n const toolMapRef = useRef(toolMap);\n toolMapRef.current = toolMap;\n const toolSchemasRef = useRef(toolSchemas);\n toolSchemasRef.current = toolSchemas;\n\n // Check if a tool is a client-side tool\n const isClientTool = useCallback(\n (toolName: string): boolean => {\n return toolMap.has(toolName);\n },\n [toolMap]\n );\n\n const hasResponseWidget = useCallback(\n (toolName: string): boolean => Boolean(toolMap.get(toolName)?.responseWidget),\n [toolMap]\n );\n\n const getTool = useCallback(\n (toolName: string): ModelInterfaceTool | undefined => toolMap.get(toolName),\n [toolMap]\n );\n\n // Handle tool calls: execute callback tools, queue widget tools for user input\n const handleToolCalls = useCallback(\n async (toolCalls: ToolCall[]): Promise<HandleToolCallsResult> => {\n const responses: ToolCallResponse[] = [];\n const widgetCalls: PendingWidgetCall[] = [];\n\n const handle = async (toolCall: ToolCall, tool: ModelInterfaceTool) => {\n const toolName = toolCall.function.name;\n\n let params: any = {};\n try {\n params = JSON.parse(toolCall.function.arguments || '{}');\n } catch {\n // Keep empty params if parsing fails\n }\n\n onToolExecute?.(toolName, params);\n\n if (tool.responseWidget) {\n widgetCalls.push({\n toolCall,\n params,\n widget: tool.responseWidget,\n toolName,\n });\n return;\n }\n\n if (!tool.callback) {\n // Neither callback nor widget — respond with error so the model can continue\n responses.push({\n tool_call_id: toolCall.id,\n content: { error: `Tool \"${toolName}\" has no callback or responseWidget` },\n role: 'tool',\n });\n return;\n }\n\n try {\n const result = await tool.callback(params);\n onToolComplete?.(toolName, result);\n responses.push({\n tool_call_id: toolCall.id,\n content: result,\n role: 'tool',\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onToolError?.(toolName, error);\n responses.push({\n tool_call_id: toolCall.id,\n content: { error: error.message },\n role: 'tool',\n });\n }\n };\n\n const missing: ToolCall[] = [];\n for (const toolCall of toolCalls) {\n const tool = toolMapRef.current.get(toolCall.function.name);\n if (tool) await handle(toolCall, tool);\n else missing.push(toolCall);\n }\n\n if (missing.length > 0) {\n const allLoaded = () =>\n missing.every((toolCall) => toolMapRef.current.has(toolCall.function.name));\n const deadline = Date.now() + unavailableToolGraceMs;\n while (!allLoaded() && Date.now() < deadline) {\n await sleep(Math.min(UNAVAILABLE_TOOL_CHECK_MS, deadline - Date.now()));\n }\n\n for (const toolCall of missing) {\n const tool = toolMapRef.current.get(toolCall.function.name);\n if (tool) await handle(toolCall, tool);\n else responses.push(unavailableToolResponse(toolCall));\n }\n }\n\n return { responses, widgetCalls, toolSchemas: toolSchemasRef.current };\n },\n [onToolExecute, onToolComplete, onToolError, unavailableToolGraceMs]\n );\n\n // Extract pending tool calls from messages that need client handling\n const extractPendingToolCalls = useCallback(\n (messages: ChatMessage[]): ToolCall[] =>\n unansweredToolCalls(messages, (toolCall) => isClientTool(toolCall.function.name)),\n [isClientTool]\n );\n\n const resolvePending = useCallback(\n (data: RealtimeChatHistory): ToolCall[] => resolvePendingToolCalls(data, isClientTool),\n [isClientTool]\n );\n\n return {\n toolSchemas,\n isClientTool,\n hasResponseWidget,\n getTool,\n handleToolCalls,\n extractPendingToolCalls,\n resolvePendingToolCalls: resolvePending,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -30,7 +30,7 @@ export type { UseDevicChatOptions, UseDevicChatResult, SendMessageResult, StopRe
30
30
  export { DevicApiClient, DevicApiError } from './api/client';
31
31
  export type { DevicApiClientConfig, TenantSessionToken } from './api/client';
32
32
  export { AgentThreadState, } from './api/types';
33
- export type { ChatMessage, ChatFile, MessageContent, ToolCall, ToolCallResponse, ProcessMessageDto, AssistantResponse, AsyncResponse, QueueDisposition, StopChatResponse, RealtimeChatHistory, RealtimeStatus, ChatHistory, AssistantSpecialization, ModelInterfaceTool, ModelInterfaceToolSchema, ResponseWidgetProps, ResponseWidgetConfig, PreviousMessage, ApiError, ConversationSummary, FeedbackSubmission, FeedbackEntry, AgentThreadDto, AgentTaskDto, AgentDto, HandOffToolResponse, ToolGroupCall, ToolGroupConfig, WhisperTranscriptionResponse, TenantLimitExceeded, TenantUsage, TenantUsageRule, TenantUsageHistoryRow, TenantUsageHistoryQuery, RecalledMemoryRecord, RecalledMemoryFact, RecalledMemoryEntity, RecalledMemoryTurn, CompactionCheckpoint, CompactionSummary, CompactionFact, CompactionActivity, CoreMemorySnapshot, CoreMemoryEntry, CoreMemoryLimits, CoreMemoryList, Integration, IntegrationAccount, IntegrationAuthField, IntegrationAuthScheme, IntegrationSetupRequired, TenantMcpAuthMode, TenantMcpConnection, TenantMcpServer, TenantMcpListing, TenantMcpAuthInput, TenantMcpConnectResult, } from './api/types';
33
+ export type { ChatMessage, ChatFile, MessageContent, ToolCall, ToolCallResponse, ProcessMessageDto, AssistantResponse, AsyncResponse, QueueDisposition, StopChatResponse, RealtimeChatHistory, RealtimeStatus, PendingAsyncToolCall, ChatHistory, AssistantSpecialization, ModelInterfaceTool, ModelInterfaceToolSchema, ResponseWidgetProps, ResponseWidgetConfig, PreviousMessage, ApiError, ConversationSummary, FeedbackSubmission, FeedbackEntry, AgentThreadDto, AgentTaskDto, AgentDto, HandOffToolResponse, ToolGroupCall, ToolGroupConfig, WhisperTranscriptionResponse, TenantLimitExceeded, TenantUsage, TenantUsageRule, TenantUsageHistoryRow, TenantUsageHistoryQuery, RecalledMemoryRecord, RecalledMemoryFact, RecalledMemoryEntity, RecalledMemoryTurn, CompactionCheckpoint, CompactionSummary, CompactionFact, CompactionActivity, CoreMemorySnapshot, CoreMemoryEntry, CoreMemoryLimits, CoreMemoryList, Integration, IntegrationAccount, IntegrationAuthField, IntegrationAuthScheme, IntegrationSetupRequired, TenantMcpAuthMode, TenantMcpConnection, TenantMcpServer, TenantMcpListing, TenantMcpAuthInput, TenantMcpConnectResult, } from './api/types';
34
34
  export { MessageActions, FeedbackModal } from './components/Feedback';
35
35
  export type { MessageActionsProps, FeedbackModalProps, FeedbackState, FeedbackTheme } from './components/Feedback';
36
36
  export { generateId, deepMerge, debounce, throttle, formatFileSize, storage, segmentToolCalls } from './utils';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devicai/ui",
3
- "version": "0.62.3",
3
+ "version": "0.62.4",
4
4
  "type": "module",
5
5
  "description": "React component library for Devic AI assistants",
6
6
  "engines": {