@tambo-ai/client 0.0.1

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 (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -0
  3. package/dist/index.d.ts +43 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +78 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/mcp/elicitation.d.ts +59 -0
  8. package/dist/mcp/elicitation.d.ts.map +1 -0
  9. package/dist/mcp/elicitation.js +27 -0
  10. package/dist/mcp/elicitation.js.map +1 -0
  11. package/dist/mcp/index.d.ts +6 -0
  12. package/dist/mcp/index.d.ts.map +1 -0
  13. package/dist/mcp/index.js +14 -0
  14. package/dist/mcp/index.js.map +1 -0
  15. package/dist/mcp/mcp-client.d.ts +185 -0
  16. package/dist/mcp/mcp-client.d.ts.map +1 -0
  17. package/dist/mcp/mcp-client.js +219 -0
  18. package/dist/mcp/mcp-client.js.map +1 -0
  19. package/dist/mcp/mcp-constants.d.ts +19 -0
  20. package/dist/mcp/mcp-constants.d.ts.map +1 -0
  21. package/dist/mcp/mcp-constants.js +21 -0
  22. package/dist/mcp/mcp-constants.js.map +1 -0
  23. package/dist/model/component-metadata.d.ts +390 -0
  24. package/dist/model/component-metadata.d.ts.map +1 -0
  25. package/dist/model/component-metadata.js +3 -0
  26. package/dist/model/component-metadata.js.map +1 -0
  27. package/dist/model/mcp-server-info.d.ts +72 -0
  28. package/dist/model/mcp-server-info.d.ts.map +1 -0
  29. package/dist/model/mcp-server-info.js +29 -0
  30. package/dist/model/mcp-server-info.js.map +1 -0
  31. package/dist/schema/index.d.ts +5 -0
  32. package/dist/schema/index.d.ts.map +1 -0
  33. package/dist/schema/index.js +15 -0
  34. package/dist/schema/index.js.map +1 -0
  35. package/dist/schema/json-schema.d.ts +42 -0
  36. package/dist/schema/json-schema.d.ts.map +1 -0
  37. package/dist/schema/json-schema.js +114 -0
  38. package/dist/schema/json-schema.js.map +1 -0
  39. package/dist/schema/schema.d.ts +49 -0
  40. package/dist/schema/schema.d.ts.map +1 -0
  41. package/dist/schema/schema.js +129 -0
  42. package/dist/schema/schema.js.map +1 -0
  43. package/dist/schema/standard-schema.d.ts +22 -0
  44. package/dist/schema/standard-schema.d.ts.map +1 -0
  45. package/dist/schema/standard-schema.js +42 -0
  46. package/dist/schema/standard-schema.js.map +1 -0
  47. package/dist/schema/validate.d.ts +14 -0
  48. package/dist/schema/validate.d.ts.map +1 -0
  49. package/dist/schema/validate.js +148 -0
  50. package/dist/schema/validate.js.map +1 -0
  51. package/dist/tambo-client.d.ts +292 -0
  52. package/dist/tambo-client.d.ts.map +1 -0
  53. package/dist/tambo-client.js +508 -0
  54. package/dist/tambo-client.js.map +1 -0
  55. package/dist/tambo-stream.d.ts +112 -0
  56. package/dist/tambo-stream.d.ts.map +1 -0
  57. package/dist/tambo-stream.js +345 -0
  58. package/dist/tambo-stream.js.map +1 -0
  59. package/dist/types/auth.d.ts +24 -0
  60. package/dist/types/auth.d.ts.map +1 -0
  61. package/dist/types/auth.js +3 -0
  62. package/dist/types/auth.js.map +1 -0
  63. package/dist/types/event.d.ts +89 -0
  64. package/dist/types/event.d.ts.map +1 -0
  65. package/dist/types/event.js +57 -0
  66. package/dist/types/event.js.map +1 -0
  67. package/dist/types/message.d.ts +122 -0
  68. package/dist/types/message.d.ts.map +1 -0
  69. package/dist/types/message.js +10 -0
  70. package/dist/types/message.js.map +1 -0
  71. package/dist/types/thread.d.ts +58 -0
  72. package/dist/types/thread.d.ts.map +1 -0
  73. package/dist/types/thread.js +9 -0
  74. package/dist/types/thread.js.map +1 -0
  75. package/dist/types/tool-choice.d.ts +8 -0
  76. package/dist/types/tool-choice.d.ts.map +1 -0
  77. package/dist/types/tool-choice.js +3 -0
  78. package/dist/types/tool-choice.js.map +1 -0
  79. package/dist/utils/event-accumulator.d.ts +165 -0
  80. package/dist/utils/event-accumulator.d.ts.map +1 -0
  81. package/dist/utils/event-accumulator.js +1278 -0
  82. package/dist/utils/event-accumulator.js.map +1 -0
  83. package/dist/utils/json-patch.d.ts +18 -0
  84. package/dist/utils/json-patch.d.ts.map +1 -0
  85. package/dist/utils/json-patch.js +35 -0
  86. package/dist/utils/json-patch.js.map +1 -0
  87. package/dist/utils/keyed-throttle.d.ts +42 -0
  88. package/dist/utils/keyed-throttle.d.ts.map +1 -0
  89. package/dist/utils/keyed-throttle.js +86 -0
  90. package/dist/utils/keyed-throttle.js.map +1 -0
  91. package/dist/utils/registry-conversion.d.ts +53 -0
  92. package/dist/utils/registry-conversion.d.ts.map +1 -0
  93. package/dist/utils/registry-conversion.js +115 -0
  94. package/dist/utils/registry-conversion.js.map +1 -0
  95. package/dist/utils/send-message.d.ts +140 -0
  96. package/dist/utils/send-message.d.ts.map +1 -0
  97. package/dist/utils/send-message.js +183 -0
  98. package/dist/utils/send-message.js.map +1 -0
  99. package/dist/utils/stream-handler.d.ts +45 -0
  100. package/dist/utils/stream-handler.d.ts.map +1 -0
  101. package/dist/utils/stream-handler.js +47 -0
  102. package/dist/utils/stream-handler.js.map +1 -0
  103. package/dist/utils/thread-utils.d.ts +16 -0
  104. package/dist/utils/thread-utils.d.ts.map +1 -0
  105. package/dist/utils/thread-utils.js +34 -0
  106. package/dist/utils/thread-utils.js.map +1 -0
  107. package/dist/utils/tool-call-tracker.d.ts +74 -0
  108. package/dist/utils/tool-call-tracker.d.ts.map +1 -0
  109. package/dist/utils/tool-call-tracker.js +181 -0
  110. package/dist/utils/tool-call-tracker.js.map +1 -0
  111. package/dist/utils/tool-executor.d.ts +67 -0
  112. package/dist/utils/tool-executor.d.ts.map +1 -0
  113. package/dist/utils/tool-executor.js +160 -0
  114. package/dist/utils/tool-executor.js.map +1 -0
  115. package/dist/utils/unstrictify.d.ts +32 -0
  116. package/dist/utils/unstrictify.d.ts.map +1 -0
  117. package/dist/utils/unstrictify.js +160 -0
  118. package/dist/utils/unstrictify.js.map +1 -0
  119. package/esm/index.d.ts +43 -0
  120. package/esm/index.d.ts.map +1 -0
  121. package/esm/index.js +78 -0
  122. package/esm/index.js.map +1 -0
  123. package/esm/mcp/elicitation.d.ts +59 -0
  124. package/esm/mcp/elicitation.d.ts.map +1 -0
  125. package/esm/mcp/elicitation.js +27 -0
  126. package/esm/mcp/elicitation.js.map +1 -0
  127. package/esm/mcp/index.d.ts +6 -0
  128. package/esm/mcp/index.d.ts.map +1 -0
  129. package/esm/mcp/index.js +14 -0
  130. package/esm/mcp/index.js.map +1 -0
  131. package/esm/mcp/mcp-client.d.ts +185 -0
  132. package/esm/mcp/mcp-client.d.ts.map +1 -0
  133. package/esm/mcp/mcp-client.js +219 -0
  134. package/esm/mcp/mcp-client.js.map +1 -0
  135. package/esm/mcp/mcp-constants.d.ts +19 -0
  136. package/esm/mcp/mcp-constants.d.ts.map +1 -0
  137. package/esm/mcp/mcp-constants.js +21 -0
  138. package/esm/mcp/mcp-constants.js.map +1 -0
  139. package/esm/model/component-metadata.d.ts +390 -0
  140. package/esm/model/component-metadata.d.ts.map +1 -0
  141. package/esm/model/component-metadata.js +3 -0
  142. package/esm/model/component-metadata.js.map +1 -0
  143. package/esm/model/mcp-server-info.d.ts +72 -0
  144. package/esm/model/mcp-server-info.d.ts.map +1 -0
  145. package/esm/model/mcp-server-info.js +29 -0
  146. package/esm/model/mcp-server-info.js.map +1 -0
  147. package/esm/schema/index.d.ts +5 -0
  148. package/esm/schema/index.d.ts.map +1 -0
  149. package/esm/schema/index.js +15 -0
  150. package/esm/schema/index.js.map +1 -0
  151. package/esm/schema/json-schema.d.ts +42 -0
  152. package/esm/schema/json-schema.d.ts.map +1 -0
  153. package/esm/schema/json-schema.js +114 -0
  154. package/esm/schema/json-schema.js.map +1 -0
  155. package/esm/schema/schema.d.ts +49 -0
  156. package/esm/schema/schema.d.ts.map +1 -0
  157. package/esm/schema/schema.js +129 -0
  158. package/esm/schema/schema.js.map +1 -0
  159. package/esm/schema/standard-schema.d.ts +22 -0
  160. package/esm/schema/standard-schema.d.ts.map +1 -0
  161. package/esm/schema/standard-schema.js +42 -0
  162. package/esm/schema/standard-schema.js.map +1 -0
  163. package/esm/schema/validate.d.ts +14 -0
  164. package/esm/schema/validate.d.ts.map +1 -0
  165. package/esm/schema/validate.js +148 -0
  166. package/esm/schema/validate.js.map +1 -0
  167. package/esm/tambo-client.d.ts +292 -0
  168. package/esm/tambo-client.d.ts.map +1 -0
  169. package/esm/tambo-client.js +508 -0
  170. package/esm/tambo-client.js.map +1 -0
  171. package/esm/tambo-stream.d.ts +112 -0
  172. package/esm/tambo-stream.d.ts.map +1 -0
  173. package/esm/tambo-stream.js +345 -0
  174. package/esm/tambo-stream.js.map +1 -0
  175. package/esm/types/auth.d.ts +24 -0
  176. package/esm/types/auth.d.ts.map +1 -0
  177. package/esm/types/auth.js +3 -0
  178. package/esm/types/auth.js.map +1 -0
  179. package/esm/types/event.d.ts +89 -0
  180. package/esm/types/event.d.ts.map +1 -0
  181. package/esm/types/event.js +57 -0
  182. package/esm/types/event.js.map +1 -0
  183. package/esm/types/message.d.ts +122 -0
  184. package/esm/types/message.d.ts.map +1 -0
  185. package/esm/types/message.js +10 -0
  186. package/esm/types/message.js.map +1 -0
  187. package/esm/types/thread.d.ts +58 -0
  188. package/esm/types/thread.d.ts.map +1 -0
  189. package/esm/types/thread.js +9 -0
  190. package/esm/types/thread.js.map +1 -0
  191. package/esm/types/tool-choice.d.ts +8 -0
  192. package/esm/types/tool-choice.d.ts.map +1 -0
  193. package/esm/types/tool-choice.js +3 -0
  194. package/esm/types/tool-choice.js.map +1 -0
  195. package/esm/utils/event-accumulator.d.ts +165 -0
  196. package/esm/utils/event-accumulator.d.ts.map +1 -0
  197. package/esm/utils/event-accumulator.js +1278 -0
  198. package/esm/utils/event-accumulator.js.map +1 -0
  199. package/esm/utils/json-patch.d.ts +18 -0
  200. package/esm/utils/json-patch.d.ts.map +1 -0
  201. package/esm/utils/json-patch.js +35 -0
  202. package/esm/utils/json-patch.js.map +1 -0
  203. package/esm/utils/keyed-throttle.d.ts +42 -0
  204. package/esm/utils/keyed-throttle.d.ts.map +1 -0
  205. package/esm/utils/keyed-throttle.js +86 -0
  206. package/esm/utils/keyed-throttle.js.map +1 -0
  207. package/esm/utils/registry-conversion.d.ts +53 -0
  208. package/esm/utils/registry-conversion.d.ts.map +1 -0
  209. package/esm/utils/registry-conversion.js +115 -0
  210. package/esm/utils/registry-conversion.js.map +1 -0
  211. package/esm/utils/send-message.d.ts +140 -0
  212. package/esm/utils/send-message.d.ts.map +1 -0
  213. package/esm/utils/send-message.js +183 -0
  214. package/esm/utils/send-message.js.map +1 -0
  215. package/esm/utils/stream-handler.d.ts +45 -0
  216. package/esm/utils/stream-handler.d.ts.map +1 -0
  217. package/esm/utils/stream-handler.js +47 -0
  218. package/esm/utils/stream-handler.js.map +1 -0
  219. package/esm/utils/thread-utils.d.ts +16 -0
  220. package/esm/utils/thread-utils.d.ts.map +1 -0
  221. package/esm/utils/thread-utils.js +34 -0
  222. package/esm/utils/thread-utils.js.map +1 -0
  223. package/esm/utils/tool-call-tracker.d.ts +74 -0
  224. package/esm/utils/tool-call-tracker.d.ts.map +1 -0
  225. package/esm/utils/tool-call-tracker.js +181 -0
  226. package/esm/utils/tool-call-tracker.js.map +1 -0
  227. package/esm/utils/tool-executor.d.ts +67 -0
  228. package/esm/utils/tool-executor.d.ts.map +1 -0
  229. package/esm/utils/tool-executor.js +160 -0
  230. package/esm/utils/tool-executor.js.map +1 -0
  231. package/esm/utils/unstrictify.d.ts +32 -0
  232. package/esm/utils/unstrictify.d.ts.map +1 -0
  233. package/esm/utils/unstrictify.js +160 -0
  234. package/esm/utils/unstrictify.js.map +1 -0
  235. package/package.json +90 -0
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ /**
3
+ * Tool Call Tracker
4
+ *
5
+ * Tracks tool calls during streaming, accumulating arguments until complete.
6
+ * Owns the tool name → JSON Schema mapping and handles unstrictification
7
+ * so callers don't need to know about schema conversion.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.ToolCallTracker = void 0;
11
+ const core_1 = require("@ag-ui/core");
12
+ const partial_json_1 = require("partial-json");
13
+ const schema_1 = require("../schema/schema.js");
14
+ const unstrictify_1 = require("./unstrictify.js");
15
+ /**
16
+ * Build a tool-name → JSONSchema7 map from the tool registry.
17
+ * Handles both modern `inputSchema` and deprecated `toolSchema` formats.
18
+ * Tools whose schema can't be converted are silently skipped.
19
+ * @param toolRegistry - Record of tool name → tool definition
20
+ * @returns Map of tool name → JSON Schema
21
+ */
22
+ function buildToolSchemas(toolRegistry) {
23
+ const schemas = new Map();
24
+ for (const tool of Object.values(toolRegistry)) {
25
+ try {
26
+ if ("inputSchema" in tool && tool.inputSchema) {
27
+ schemas.set(tool.name, (0, schema_1.schemaToJsonSchema)(tool.inputSchema));
28
+ }
29
+ else if ("toolSchema" in tool && tool.toolSchema) {
30
+ schemas.set(tool.name, (0, schema_1.schemaToJsonSchema)(tool.toolSchema));
31
+ }
32
+ }
33
+ catch {
34
+ // Schema conversion failed — tool still works, just without unstrictification
35
+ }
36
+ }
37
+ return schemas;
38
+ }
39
+ /**
40
+ * Tracks tool calls during streaming, accumulating arguments until complete.
41
+ *
42
+ * Tool calls arrive as a sequence of events:
43
+ * 1. TOOL_CALL_START - initializes the tool call with name
44
+ * 2. TOOL_CALL_ARGS (multiple) - streams JSON argument fragments
45
+ * 3. TOOL_CALL_END - marks the tool call as complete, triggers JSON parsing
46
+ *
47
+ * When constructed with a tool registry, the tracker unstrictifies parsed
48
+ * args (both partial and final) using the original JSON Schemas.
49
+ */
50
+ class ToolCallTracker {
51
+ pendingToolCalls = new Map();
52
+ accumulatingArgs = new Map();
53
+ _toolSchemas;
54
+ constructor(toolRegistry) {
55
+ this._toolSchemas = toolRegistry
56
+ ? buildToolSchemas(toolRegistry)
57
+ : new Map();
58
+ }
59
+ /**
60
+ * The tool-name → JSONSchema7 map, for passing to the reducer.
61
+ * @returns The tool schemas map
62
+ */
63
+ get toolSchemas() {
64
+ return this._toolSchemas;
65
+ }
66
+ /**
67
+ * Handles a streaming event, tracking tool call state as needed.
68
+ * @param event - The streaming event to process
69
+ * @throws {Error} If JSON parsing fails on TOOL_CALL_END (fail-fast, no silent fallback)
70
+ */
71
+ handleEvent(event) {
72
+ switch (event.type) {
73
+ case core_1.EventType.TOOL_CALL_START:
74
+ this.pendingToolCalls.set(event.toolCallId, {
75
+ name: event.toolCallName,
76
+ input: {},
77
+ });
78
+ this.accumulatingArgs.set(event.toolCallId, "");
79
+ break;
80
+ case core_1.EventType.TOOL_CALL_ARGS: {
81
+ const current = this.accumulatingArgs.get(event.toolCallId);
82
+ this.accumulatingArgs.set(event.toolCallId, (current ?? "") + event.delta);
83
+ break;
84
+ }
85
+ case core_1.EventType.TOOL_CALL_END: {
86
+ const jsonStr = this.accumulatingArgs.get(event.toolCallId);
87
+ const toolCall = this.pendingToolCalls.get(event.toolCallId);
88
+ if (toolCall && jsonStr) {
89
+ let parsedInput;
90
+ try {
91
+ parsedInput = JSON.parse(jsonStr);
92
+ }
93
+ catch (error) {
94
+ // Fail-fast: don't silently continue with empty input
95
+ throw new Error(`Failed to parse tool call arguments for ${event.toolCallId}: ${error instanceof Error ? error.message : "Unknown error"}. JSON: ${jsonStr.slice(0, 100)}${jsonStr.length > 100 ? "..." : ""}`);
96
+ }
97
+ parsedInput = this.unstrictify(toolCall.name, parsedInput);
98
+ toolCall.input = parsedInput;
99
+ }
100
+ break;
101
+ }
102
+ default:
103
+ // Other event types are ignored - only tool call events are tracked
104
+ break;
105
+ }
106
+ }
107
+ /**
108
+ * Parses partial JSON from the accumulated args for a tool call and
109
+ * unstrictifies the result. Used during streaming to get the current
110
+ * best-effort parsed args.
111
+ * @param toolCallId - ID of the tool call to parse
112
+ * @returns Parsed and unstrictified args, or undefined if not parseable yet
113
+ */
114
+ parsePartialArgs(toolCallId) {
115
+ const accToolCall = this.getAccumulatingToolCall(toolCallId);
116
+ if (!accToolCall)
117
+ return undefined;
118
+ try {
119
+ const parsed = (0, partial_json_1.parse)(accToolCall.accumulatedArgs);
120
+ if (typeof parsed === "object" &&
121
+ parsed !== null &&
122
+ !Array.isArray(parsed)) {
123
+ return this.unstrictify(accToolCall.name, parsed);
124
+ }
125
+ }
126
+ catch {
127
+ /* not parseable yet */
128
+ }
129
+ return undefined;
130
+ }
131
+ /**
132
+ * Gets the name and accumulated args for a tool call that is still accumulating.
133
+ * @param toolCallId - ID of the tool call to look up
134
+ * @returns The tool name and raw accumulated args string, or undefined if not found
135
+ */
136
+ getAccumulatingToolCall(toolCallId) {
137
+ const toolCall = this.pendingToolCalls.get(toolCallId);
138
+ const args = this.accumulatingArgs.get(toolCallId);
139
+ if (!toolCall || args === undefined)
140
+ return undefined;
141
+ return { name: toolCall.name, accumulatedArgs: args };
142
+ }
143
+ /**
144
+ * Gets tool calls for the given IDs, filtered to only those that exist.
145
+ * @param toolCallIds - IDs of tool calls to retrieve
146
+ * @returns Map of tool call ID to pending tool call
147
+ */
148
+ getToolCallsById(toolCallIds) {
149
+ const result = new Map();
150
+ for (const id of toolCallIds) {
151
+ const toolCall = this.pendingToolCalls.get(id);
152
+ if (toolCall) {
153
+ result.set(id, toolCall);
154
+ }
155
+ }
156
+ return result;
157
+ }
158
+ /**
159
+ * Clears tracked tool calls for the given IDs.
160
+ * @param toolCallIds - IDs of tool calls to clear
161
+ */
162
+ clearToolCalls(toolCallIds) {
163
+ for (const id of toolCallIds) {
164
+ this.pendingToolCalls.delete(id);
165
+ this.accumulatingArgs.delete(id);
166
+ }
167
+ }
168
+ /**
169
+ * Unstrictify params using the schema for the given tool name.
170
+ * Returns params unchanged if no schema is available.
171
+ * @returns The unstrictified params.
172
+ */
173
+ unstrictify(toolName, params) {
174
+ const schema = this._toolSchemas.get(toolName);
175
+ if (!schema)
176
+ return params;
177
+ return (0, unstrictify_1.unstrictifyToolCallParamsFromSchema)(schema, params);
178
+ }
179
+ }
180
+ exports.ToolCallTracker = ToolCallTracker;
181
+ //# sourceMappingURL=tool-call-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-call-tracker.js","sourceRoot":"","sources":["../../src/utils/tool-call-tracker.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Tool Call Tracker\n *\n * Tracks tool calls during streaming, accumulating arguments until complete.\n * Owns the tool name → JSON Schema mapping and handles unstrictification\n * so callers don't need to know about schema conversion.\n */\n\nimport { EventType, type AGUIEvent } from \"@ag-ui/core\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { parse as parsePartialJson } from \"partial-json\";\nimport type { TamboTool } from \"../model/component-metadata\";\nimport { schemaToJsonSchema } from \"../schema/schema\";\nimport type { PendingToolCall } from \"./tool-executor\";\nimport { unstrictifyToolCallParamsFromSchema } from \"./unstrictify\";\n\n/**\n * Build a tool-name → JSONSchema7 map from the tool registry.\n * Handles both modern `inputSchema` and deprecated `toolSchema` formats.\n * Tools whose schema can't be converted are silently skipped.\n * @param toolRegistry - Record of tool name → tool definition\n * @returns Map of tool name → JSON Schema\n */\nfunction buildToolSchemas(\n toolRegistry: Record<string, TamboTool>,\n): Map<string, JSONSchema7> {\n const schemas = new Map<string, JSONSchema7>();\n for (const tool of Object.values(toolRegistry)) {\n try {\n if (\"inputSchema\" in tool && tool.inputSchema) {\n schemas.set(tool.name, schemaToJsonSchema(tool.inputSchema));\n } else if (\"toolSchema\" in tool && tool.toolSchema) {\n schemas.set(tool.name, schemaToJsonSchema(tool.toolSchema));\n }\n } catch {\n // Schema conversion failed — tool still works, just without unstrictification\n }\n }\n return schemas;\n}\n\n/**\n * Tracks tool calls during streaming, accumulating arguments until complete.\n *\n * Tool calls arrive as a sequence of events:\n * 1. TOOL_CALL_START - initializes the tool call with name\n * 2. TOOL_CALL_ARGS (multiple) - streams JSON argument fragments\n * 3. TOOL_CALL_END - marks the tool call as complete, triggers JSON parsing\n *\n * When constructed with a tool registry, the tracker unstrictifies parsed\n * args (both partial and final) using the original JSON Schemas.\n */\nexport class ToolCallTracker {\n private pendingToolCalls = new Map<string, PendingToolCall>();\n private accumulatingArgs = new Map<string, string>();\n private _toolSchemas: Map<string, JSONSchema7>;\n\n constructor(toolRegistry?: Record<string, TamboTool>) {\n this._toolSchemas = toolRegistry\n ? buildToolSchemas(toolRegistry)\n : new Map();\n }\n\n /**\n * The tool-name → JSONSchema7 map, for passing to the reducer.\n * @returns The tool schemas map\n */\n get toolSchemas(): Map<string, JSONSchema7> {\n return this._toolSchemas;\n }\n\n /**\n * Handles a streaming event, tracking tool call state as needed.\n * @param event - The streaming event to process\n * @throws {Error} If JSON parsing fails on TOOL_CALL_END (fail-fast, no silent fallback)\n */\n handleEvent(event: AGUIEvent): void {\n switch (event.type) {\n case EventType.TOOL_CALL_START:\n this.pendingToolCalls.set(event.toolCallId, {\n name: event.toolCallName,\n input: {},\n });\n this.accumulatingArgs.set(event.toolCallId, \"\");\n break;\n\n case EventType.TOOL_CALL_ARGS: {\n const current = this.accumulatingArgs.get(event.toolCallId);\n this.accumulatingArgs.set(\n event.toolCallId,\n (current ?? \"\") + event.delta,\n );\n break;\n }\n\n case EventType.TOOL_CALL_END: {\n const jsonStr = this.accumulatingArgs.get(event.toolCallId);\n const toolCall = this.pendingToolCalls.get(event.toolCallId);\n if (toolCall && jsonStr) {\n let parsedInput: Record<string, unknown>;\n try {\n parsedInput = JSON.parse(jsonStr) as Record<string, unknown>;\n } catch (error) {\n // Fail-fast: don't silently continue with empty input\n throw new Error(\n `Failed to parse tool call arguments for ${event.toolCallId}: ${error instanceof Error ? error.message : \"Unknown error\"}. JSON: ${jsonStr.slice(0, 100)}${jsonStr.length > 100 ? \"...\" : \"\"}`,\n );\n }\n\n parsedInput = this.unstrictify(toolCall.name, parsedInput);\n toolCall.input = parsedInput;\n }\n break;\n }\n\n default:\n // Other event types are ignored - only tool call events are tracked\n break;\n }\n }\n\n /**\n * Parses partial JSON from the accumulated args for a tool call and\n * unstrictifies the result. Used during streaming to get the current\n * best-effort parsed args.\n * @param toolCallId - ID of the tool call to parse\n * @returns Parsed and unstrictified args, or undefined if not parseable yet\n */\n parsePartialArgs(toolCallId: string): Record<string, unknown> | undefined {\n const accToolCall = this.getAccumulatingToolCall(toolCallId);\n if (!accToolCall) return undefined;\n\n try {\n const parsed: unknown = parsePartialJson(accToolCall.accumulatedArgs);\n if (\n typeof parsed === \"object\" &&\n parsed !== null &&\n !Array.isArray(parsed)\n ) {\n return this.unstrictify(\n accToolCall.name,\n parsed as Record<string, unknown>,\n );\n }\n } catch {\n /* not parseable yet */\n }\n return undefined;\n }\n\n /**\n * Gets the name and accumulated args for a tool call that is still accumulating.\n * @param toolCallId - ID of the tool call to look up\n * @returns The tool name and raw accumulated args string, or undefined if not found\n */\n getAccumulatingToolCall(\n toolCallId: string,\n ): { name: string; accumulatedArgs: string } | undefined {\n const toolCall = this.pendingToolCalls.get(toolCallId);\n const args = this.accumulatingArgs.get(toolCallId);\n if (!toolCall || args === undefined) return undefined;\n return { name: toolCall.name, accumulatedArgs: args };\n }\n\n /**\n * Gets tool calls for the given IDs, filtered to only those that exist.\n * @param toolCallIds - IDs of tool calls to retrieve\n * @returns Map of tool call ID to pending tool call\n */\n getToolCallsById(toolCallIds: string[]): Map<string, PendingToolCall> {\n const result = new Map<string, PendingToolCall>();\n for (const id of toolCallIds) {\n const toolCall = this.pendingToolCalls.get(id);\n if (toolCall) {\n result.set(id, toolCall);\n }\n }\n return result;\n }\n\n /**\n * Clears tracked tool calls for the given IDs.\n * @param toolCallIds - IDs of tool calls to clear\n */\n clearToolCalls(toolCallIds: string[]): void {\n for (const id of toolCallIds) {\n this.pendingToolCalls.delete(id);\n this.accumulatingArgs.delete(id);\n }\n }\n\n /**\n * Unstrictify params using the schema for the given tool name.\n * Returns params unchanged if no schema is available.\n * @returns The unstrictified params.\n */\n private unstrictify(\n toolName: string,\n params: Record<string, unknown>,\n ): Record<string, unknown> {\n const schema = this._toolSchemas.get(toolName);\n if (!schema) return params;\n return unstrictifyToolCallParamsFromSchema(schema, params);\n }\n}\n"]}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Tool Executor
3
+ *
4
+ * Handles automatic execution of client-side tools when the model
5
+ * requests them via `tambo.run.awaiting_input` events.
6
+ */
7
+ import type { TamboTool } from "../model/component-metadata.js";
8
+ import type { ToolResultContent } from "@tambo-ai/typescript-sdk/resources/threads/threads";
9
+ import type { ToolCallTracker } from "./tool-call-tracker.js";
10
+ import { type KeyedThrottle } from "./keyed-throttle.js";
11
+ /**
12
+ * Pending tool call from the stream accumulator
13
+ */
14
+ export interface PendingToolCall {
15
+ name: string;
16
+ input: Record<string, unknown>;
17
+ }
18
+ /**
19
+ * Execute a streamable tool call during streaming with pre-parsed partial args.
20
+ *
21
+ * Called on each TOOL_CALL_ARGS event for tools annotated with
22
+ * `tamboStreamableHint: true`. Enables incremental UI updates while
23
+ * the model is still generating arguments.
24
+ *
25
+ * Errors are caught silently — streaming tool execution is non-fatal since
26
+ * the final execution via `awaiting_input` is what matters.
27
+ * @param toolCallId - The tool call ID being accumulated
28
+ * @param parsedArgs - Pre-parsed partial JSON args
29
+ * @param toolTracker - Tracker holding pending tool call state
30
+ * @param toolRegistry - Record of tool name to tool definition
31
+ */
32
+ export declare function executeStreamableToolCall(toolCallId: string, parsedArgs: Record<string, unknown>, toolTracker: ToolCallTracker, toolRegistry: Record<string, TamboTool>): Promise<void>;
33
+ /**
34
+ * Creates a throttled wrapper around executeStreamableToolCall.
35
+ *
36
+ * Each tool call ID gets its own independent leading+trailing throttle via
37
+ * {@link createKeyedThrottle}. The first call for a tool ID fires immediately
38
+ * (leading edge). Subsequent calls during the cooldown window update the
39
+ * stored args. After `delay` ms, if new args arrived, the tool re-executes
40
+ * with the latest args (trailing edge). This repeats as long as new args
41
+ * keep arriving — roughly one execution per `delay` ms during streaming.
42
+ *
43
+ * Call `flush()` to force-execute all pending trailing calls and reset to idle.
44
+ * @param toolTracker - Tracker holding pending tool call state
45
+ * @param toolRegistry - Record of tool name to tool definition
46
+ * @param delay - Throttle interval in milliseconds
47
+ * @returns Keyed throttle controller (schedule / flush)
48
+ */
49
+ export declare function createThrottledStreamableExecutor(toolTracker: ToolCallTracker, toolRegistry: Record<string, TamboTool>, delay?: number): KeyedThrottle<Record<string, unknown>>;
50
+ /**
51
+ * Execute a single client-side tool and return the result.
52
+ * @param tool - The tool definition from the registry
53
+ * @param toolCallId - The ID of the tool call to respond to
54
+ * @param args - The parsed arguments for the tool
55
+ * @returns ToolResultContent with the execution result or error
56
+ */
57
+ export declare function executeClientTool(tool: TamboTool, toolCallId: string, args: Record<string, unknown>): Promise<ToolResultContent>;
58
+ /**
59
+ * Execute all pending tool calls and return their results.
60
+ * Tools are executed sequentially to avoid race conditions when
61
+ * tools may have side effects that depend on each other.
62
+ * @param toolCalls - Map of tool call IDs to their call details
63
+ * @param registry - Registry of tool names to their definitions (Map or Record)
64
+ * @returns Array of ToolResultContent for all executed tools
65
+ */
66
+ export declare function executeAllPendingTools(toolCalls: Map<string, PendingToolCall>, registry: Map<string, TamboTool> | Record<string, TamboTool>): Promise<ToolResultContent[]>;
67
+ //# sourceMappingURL=tool-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-executor.d.ts","sourceRoot":"","sources":["../../src/utils/tool-executor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,oDAAoD,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE3E;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,WAAW,EAAE,eAAe,EAC5B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAiBf;AAID;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iCAAiC,CAC/C,WAAW,EAAE,eAAe,EAC5B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EACvC,KAAK,SAAiC,GACrC,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAIxC;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CAiD5B;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EACvC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAC3D,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAiC9B"}
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ /**
3
+ * Tool Executor
4
+ *
5
+ * Handles automatic execution of client-side tools when the model
6
+ * requests them via `tambo.run.awaiting_input` events.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.executeStreamableToolCall = executeStreamableToolCall;
10
+ exports.createThrottledStreamableExecutor = createThrottledStreamableExecutor;
11
+ exports.executeClientTool = executeClientTool;
12
+ exports.executeAllPendingTools = executeAllPendingTools;
13
+ const keyed_throttle_1 = require("./keyed-throttle.js");
14
+ /**
15
+ * Execute a streamable tool call during streaming with pre-parsed partial args.
16
+ *
17
+ * Called on each TOOL_CALL_ARGS event for tools annotated with
18
+ * `tamboStreamableHint: true`. Enables incremental UI updates while
19
+ * the model is still generating arguments.
20
+ *
21
+ * Errors are caught silently — streaming tool execution is non-fatal since
22
+ * the final execution via `awaiting_input` is what matters.
23
+ * @param toolCallId - The tool call ID being accumulated
24
+ * @param parsedArgs - Pre-parsed partial JSON args
25
+ * @param toolTracker - Tracker holding pending tool call state
26
+ * @param toolRegistry - Record of tool name to tool definition
27
+ */
28
+ async function executeStreamableToolCall(toolCallId, parsedArgs, toolTracker, toolRegistry) {
29
+ const accumulating = toolTracker.getAccumulatingToolCall(toolCallId);
30
+ if (!accumulating)
31
+ return;
32
+ const tool = toolRegistry[accumulating.name];
33
+ if (!tool?.annotations?.tamboStreamableHint)
34
+ return;
35
+ try {
36
+ await tool.tool(parsedArgs);
37
+ }
38
+ catch (error) {
39
+ console.warn(`[ToolExecutor] Non-fatal error in streamable tool "${accumulating.name}" ` +
40
+ `(toolCallId: ${toolCallId}). This likely indicates a bug in the tool ` +
41
+ `implementation; fix the tool to avoid repeated warnings.`, error);
42
+ }
43
+ }
44
+ const DEFAULT_STREAMABLE_THROTTLE_MS = 100;
45
+ /**
46
+ * Creates a throttled wrapper around executeStreamableToolCall.
47
+ *
48
+ * Each tool call ID gets its own independent leading+trailing throttle via
49
+ * {@link createKeyedThrottle}. The first call for a tool ID fires immediately
50
+ * (leading edge). Subsequent calls during the cooldown window update the
51
+ * stored args. After `delay` ms, if new args arrived, the tool re-executes
52
+ * with the latest args (trailing edge). This repeats as long as new args
53
+ * keep arriving — roughly one execution per `delay` ms during streaming.
54
+ *
55
+ * Call `flush()` to force-execute all pending trailing calls and reset to idle.
56
+ * @param toolTracker - Tracker holding pending tool call state
57
+ * @param toolRegistry - Record of tool name to tool definition
58
+ * @param delay - Throttle interval in milliseconds
59
+ * @returns Keyed throttle controller (schedule / flush)
60
+ */
61
+ function createThrottledStreamableExecutor(toolTracker, toolRegistry, delay = DEFAULT_STREAMABLE_THROTTLE_MS) {
62
+ return (0, keyed_throttle_1.createKeyedThrottle)((toolCallId, args) => {
63
+ void executeStreamableToolCall(toolCallId, args, toolTracker, toolRegistry);
64
+ }, delay);
65
+ }
66
+ /**
67
+ * Execute a single client-side tool and return the result.
68
+ * @param tool - The tool definition from the registry
69
+ * @param toolCallId - The ID of the tool call to respond to
70
+ * @param args - The parsed arguments for the tool
71
+ * @returns ToolResultContent with the execution result or error
72
+ */
73
+ async function executeClientTool(tool, toolCallId, args) {
74
+ try {
75
+ const result = await tool.tool(args);
76
+ // Transform result to content if transformer provided
77
+ let content;
78
+ if (tool.transformToContent) {
79
+ // transformToContent may return content parts in beta format
80
+ // Convert to content format (TextContent | ResourceContent)
81
+ const transformed = await tool.transformToContent(result);
82
+ content = transformed.map((part) => {
83
+ if (part.type === "text" && "text" in part && part.text) {
84
+ return { type: "text", text: part.text };
85
+ }
86
+ // For other types, stringify as text
87
+ return {
88
+ type: "text",
89
+ text: JSON.stringify(part),
90
+ };
91
+ });
92
+ }
93
+ else {
94
+ // Default: stringify result as text
95
+ content = [
96
+ {
97
+ type: "text",
98
+ text: typeof result === "string" ? result : JSON.stringify(result),
99
+ },
100
+ ];
101
+ }
102
+ return {
103
+ type: "tool_result",
104
+ toolUseId: toolCallId,
105
+ content,
106
+ };
107
+ }
108
+ catch (error) {
109
+ return {
110
+ type: "tool_result",
111
+ toolUseId: toolCallId,
112
+ isError: true,
113
+ content: [
114
+ {
115
+ type: "text",
116
+ text: error instanceof Error ? error.message : "Tool execution failed",
117
+ },
118
+ ],
119
+ };
120
+ }
121
+ }
122
+ /**
123
+ * Execute all pending tool calls and return their results.
124
+ * Tools are executed sequentially to avoid race conditions when
125
+ * tools may have side effects that depend on each other.
126
+ * @param toolCalls - Map of tool call IDs to their call details
127
+ * @param registry - Registry of tool names to their definitions (Map or Record)
128
+ * @returns Array of ToolResultContent for all executed tools
129
+ */
130
+ async function executeAllPendingTools(toolCalls, registry) {
131
+ const results = [];
132
+ // Normalize registry to allow lookup regardless of Map or Record
133
+ const getTool = (name) => {
134
+ if (registry instanceof Map) {
135
+ return registry.get(name);
136
+ }
137
+ return registry[name];
138
+ };
139
+ for (const [toolCallId, { name, input }] of toolCalls) {
140
+ const tool = getTool(name);
141
+ if (!tool) {
142
+ results.push({
143
+ type: "tool_result",
144
+ toolUseId: toolCallId,
145
+ isError: true,
146
+ content: [
147
+ {
148
+ type: "text",
149
+ text: `Tool "${name}" not found in registry`,
150
+ },
151
+ ],
152
+ });
153
+ continue;
154
+ }
155
+ const result = await executeClientTool(tool, toolCallId, input);
156
+ results.push(result);
157
+ }
158
+ return results;
159
+ }
160
+ //# sourceMappingURL=tool-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-executor.js","sourceRoot":"","sources":["../../src/utils/tool-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Tool Executor\n *\n * Handles automatic execution of client-side tools when the model\n * requests them via `tambo.run.awaiting_input` events.\n */\n\nimport type { TamboTool } from \"../model/component-metadata\";\nimport type {\n ToolResultContent,\n TextContent,\n ResourceContent,\n} from \"@tambo-ai/typescript-sdk/resources/threads/threads\";\nimport type { ToolCallTracker } from \"./tool-call-tracker\";\nimport { createKeyedThrottle, type KeyedThrottle } from \"./keyed-throttle\";\n\n/**\n * Pending tool call from the stream accumulator\n */\nexport interface PendingToolCall {\n name: string;\n input: Record<string, unknown>;\n}\n\n/**\n * Execute a streamable tool call during streaming with pre-parsed partial args.\n *\n * Called on each TOOL_CALL_ARGS event for tools annotated with\n * `tamboStreamableHint: true`. Enables incremental UI updates while\n * the model is still generating arguments.\n *\n * Errors are caught silently — streaming tool execution is non-fatal since\n * the final execution via `awaiting_input` is what matters.\n * @param toolCallId - The tool call ID being accumulated\n * @param parsedArgs - Pre-parsed partial JSON args\n * @param toolTracker - Tracker holding pending tool call state\n * @param toolRegistry - Record of tool name to tool definition\n */\nexport async function executeStreamableToolCall(\n toolCallId: string,\n parsedArgs: Record<string, unknown>,\n toolTracker: ToolCallTracker,\n toolRegistry: Record<string, TamboTool>,\n): Promise<void> {\n const accumulating = toolTracker.getAccumulatingToolCall(toolCallId);\n if (!accumulating) return;\n\n const tool = toolRegistry[accumulating.name];\n if (!tool?.annotations?.tamboStreamableHint) return;\n\n try {\n await tool.tool(parsedArgs);\n } catch (error) {\n console.warn(\n `[ToolExecutor] Non-fatal error in streamable tool \"${accumulating.name}\" ` +\n `(toolCallId: ${toolCallId}). This likely indicates a bug in the tool ` +\n `implementation; fix the tool to avoid repeated warnings.`,\n error,\n );\n }\n}\n\nconst DEFAULT_STREAMABLE_THROTTLE_MS = 100;\n\n/**\n * Creates a throttled wrapper around executeStreamableToolCall.\n *\n * Each tool call ID gets its own independent leading+trailing throttle via\n * {@link createKeyedThrottle}. The first call for a tool ID fires immediately\n * (leading edge). Subsequent calls during the cooldown window update the\n * stored args. After `delay` ms, if new args arrived, the tool re-executes\n * with the latest args (trailing edge). This repeats as long as new args\n * keep arriving — roughly one execution per `delay` ms during streaming.\n *\n * Call `flush()` to force-execute all pending trailing calls and reset to idle.\n * @param toolTracker - Tracker holding pending tool call state\n * @param toolRegistry - Record of tool name to tool definition\n * @param delay - Throttle interval in milliseconds\n * @returns Keyed throttle controller (schedule / flush)\n */\nexport function createThrottledStreamableExecutor(\n toolTracker: ToolCallTracker,\n toolRegistry: Record<string, TamboTool>,\n delay = DEFAULT_STREAMABLE_THROTTLE_MS,\n): KeyedThrottle<Record<string, unknown>> {\n return createKeyedThrottle<Record<string, unknown>>((toolCallId, args) => {\n void executeStreamableToolCall(toolCallId, args, toolTracker, toolRegistry);\n }, delay);\n}\n\n/**\n * Execute a single client-side tool and return the result.\n * @param tool - The tool definition from the registry\n * @param toolCallId - The ID of the tool call to respond to\n * @param args - The parsed arguments for the tool\n * @returns ToolResultContent with the execution result or error\n */\nexport async function executeClientTool(\n tool: TamboTool,\n toolCallId: string,\n args: Record<string, unknown>,\n): Promise<ToolResultContent> {\n try {\n const result = await tool.tool(args);\n\n // Transform result to content if transformer provided\n let content: (TextContent | ResourceContent)[];\n if (tool.transformToContent) {\n // transformToContent may return content parts in beta format\n // Convert to content format (TextContent | ResourceContent)\n const transformed = await tool.transformToContent(result);\n content = transformed.map((part) => {\n if (part.type === \"text\" && \"text\" in part && part.text) {\n return { type: \"text\" as const, text: part.text };\n }\n // For other types, stringify as text\n return {\n type: \"text\" as const,\n text: JSON.stringify(part),\n };\n });\n } else {\n // Default: stringify result as text\n content = [\n {\n type: \"text\" as const,\n text: typeof result === \"string\" ? result : JSON.stringify(result),\n },\n ];\n }\n\n return {\n type: \"tool_result\",\n toolUseId: toolCallId,\n content,\n };\n } catch (error) {\n return {\n type: \"tool_result\",\n toolUseId: toolCallId,\n isError: true,\n content: [\n {\n type: \"text\" as const,\n text:\n error instanceof Error ? error.message : \"Tool execution failed\",\n },\n ],\n };\n }\n}\n\n/**\n * Execute all pending tool calls and return their results.\n * Tools are executed sequentially to avoid race conditions when\n * tools may have side effects that depend on each other.\n * @param toolCalls - Map of tool call IDs to their call details\n * @param registry - Registry of tool names to their definitions (Map or Record)\n * @returns Array of ToolResultContent for all executed tools\n */\nexport async function executeAllPendingTools(\n toolCalls: Map<string, PendingToolCall>,\n registry: Map<string, TamboTool> | Record<string, TamboTool>,\n): Promise<ToolResultContent[]> {\n const results: ToolResultContent[] = [];\n\n // Normalize registry to allow lookup regardless of Map or Record\n const getTool = (name: string): TamboTool | undefined => {\n if (registry instanceof Map) {\n return registry.get(name);\n }\n return registry[name];\n };\n\n for (const [toolCallId, { name, input }] of toolCalls) {\n const tool = getTool(name);\n if (!tool) {\n results.push({\n type: \"tool_result\",\n toolUseId: toolCallId,\n isError: true,\n content: [\n {\n type: \"text\" as const,\n text: `Tool \"${name}\" not found in registry`,\n },\n ],\n });\n continue;\n }\n\n const result = await executeClientTool(tool, toolCallId, input);\n results.push(result);\n }\n\n return results;\n}\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Unstrictify tool call parameters using the original JSON Schema.
3
+ *
4
+ * When OpenAI's structured outputs mode is enabled, all optional parameters
5
+ * become required-and-nullable. The LLM then sends `null` for parameters the
6
+ * user didn't specify. This module reverses that transformation by comparing
7
+ * the LLM's output against the original schema and stripping nulls for
8
+ * parameters that were originally optional and non-nullable.
9
+ *
10
+ * Copied from packages/core/src/strictness/tool-call-strict.ts (minus the
11
+ * OpenAI-specific `unstrictifyToolCallRequest` wrapper).
12
+ */
13
+ import type { JSONSchema7, JSONSchema7Definition } from "json-schema";
14
+ /**
15
+ * Unstrictify tool call params using the original JSON Schema.
16
+ *
17
+ * Unlike the private `unstrictifyToolCallParams` which throws on unknown params,
18
+ * this function separates params into schema-defined vs `_tambo_*` pass-through
19
+ * (server-injected params not in the original schema), unstrictifies only the
20
+ * schema-defined ones, and merges pass-through params back. Unknown keys that
21
+ * aren't in the schema and don't have the `_tambo_` prefix are dropped.
22
+ * @returns The params with strictification-induced nulls stripped for optional
23
+ * non-nullable properties, and pass-through params preserved as-is.
24
+ */
25
+ export declare function unstrictifyToolCallParamsFromSchema(originalSchema: JSONSchema7, params: Record<string, unknown>): Record<string, unknown>;
26
+ /**
27
+ * Check if a JSON Schema definition allows null values.
28
+ * @param originalSchema - The schema definition to check
29
+ * @returns True if the schema allows null values
30
+ */
31
+ export declare function canBeNull(originalSchema: JSONSchema7Definition): boolean;
32
+ //# sourceMappingURL=unstrictify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unstrictify.d.ts","sourceRoot":"","sources":["../../src/utils/unstrictify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAuItE;;;;;;;;;;GAUG;AACH,wBAAgB,mCAAmC,CACjD,cAAc,EAAE,WAAW,EAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyBzB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,cAAc,EAAE,qBAAqB,GAAG,OAAO,CAaxE"}
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ /**
3
+ * Unstrictify tool call parameters using the original JSON Schema.
4
+ *
5
+ * When OpenAI's structured outputs mode is enabled, all optional parameters
6
+ * become required-and-nullable. The LLM then sends `null` for parameters the
7
+ * user didn't specify. This module reverses that transformation by comparing
8
+ * the LLM's output against the original schema and stripping nulls for
9
+ * parameters that were originally optional and non-nullable.
10
+ *
11
+ * Copied from packages/core/src/strictness/tool-call-strict.ts (minus the
12
+ * OpenAI-specific `unstrictifyToolCallRequest` wrapper).
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.unstrictifyToolCallParamsFromSchema = unstrictifyToolCallParamsFromSchema;
16
+ exports.canBeNull = canBeNull;
17
+ /**
18
+ * Unstrictify the parameters of a tool call request.
19
+ *
20
+ * This effectively reverses the process of strictifyToolCallParams, for a
21
+ * tool call request that was built from a strict JSON Schema, by returning a
22
+ * updated tool call request with the parameter values unstrictified.
23
+ * @returns The params with strictification-induced nulls stripped.
24
+ */
25
+ function unstrictifyToolCallParams(originalToolParamSchema, toolCallRequestParams) {
26
+ if (originalToolParamSchema.type !== "object") {
27
+ throw new Error(`tool call parameter schema must be an object, instead got ${originalToolParamSchema.type} / ${typeof originalToolParamSchema}`);
28
+ }
29
+ const newParams = Object.entries(toolCallRequestParams)
30
+ .map(([parameterName, parameterValue]) => {
31
+ const isRequired = originalToolParamSchema.required?.includes(parameterName);
32
+ // find the param in the original tool schema
33
+ const originalParamSchema = parameterName in (originalToolParamSchema.properties ?? {})
34
+ ? originalToolParamSchema.properties?.[parameterName]
35
+ : undefined;
36
+ // This should never happen, because the strict schema was derived from
37
+ // the original schema, so the parameter should always be present.
38
+ if (!originalParamSchema) {
39
+ throw new Error(`Tool call request parameter ${parameterName} not found in original tool`);
40
+ }
41
+ if (parameterValue === null &&
42
+ !canBeNull(originalParamSchema) &&
43
+ !isRequired) {
44
+ // This is the meat of this function. In the strict schema, this is
45
+ // "required and can be null", but in the original schema, the param was
46
+ // not required.
47
+ if (typeof originalParamSchema === "object" &&
48
+ "default" in originalParamSchema) {
49
+ return [parameterName, originalParamSchema.default];
50
+ }
51
+ return undefined;
52
+ }
53
+ // recurse into arrays
54
+ if (typeof originalParamSchema === "object" &&
55
+ originalParamSchema.type === "array") {
56
+ const arrayValue = parameterValue;
57
+ const itemSchema = originalParamSchema.items;
58
+ if (Array.isArray(arrayValue) &&
59
+ itemSchema &&
60
+ typeof itemSchema === "object" &&
61
+ !Array.isArray(itemSchema)) {
62
+ const newArrayValue = arrayValue.map((item) => {
63
+ if (itemSchema.type === "object" &&
64
+ typeof item === "object" &&
65
+ item !== null) {
66
+ // recurse into each object in the array
67
+ return unstrictifyToolCallParams(itemSchema, item);
68
+ }
69
+ return item;
70
+ });
71
+ return [parameterName, newArrayValue];
72
+ }
73
+ return [parameterName, parameterValue];
74
+ }
75
+ // recurse into the parameter value, passing along the matching original schema
76
+ if (typeof originalParamSchema === "object" &&
77
+ originalParamSchema.type === "object") {
78
+ // If the LLM sent a JSON string instead of an object (common with z.any() schemas),
79
+ // try to parse it
80
+ let objectValue = parameterValue;
81
+ if (typeof parameterValue === "string") {
82
+ try {
83
+ const parsed = JSON.parse(parameterValue);
84
+ if (typeof parsed === "object" && parsed !== null) {
85
+ objectValue = parsed;
86
+ }
87
+ }
88
+ catch {
89
+ // Not valid JSON, keep original value
90
+ }
91
+ }
92
+ // Only recurse if we have an actual object AND the schema has properties defined.
93
+ // If the schema has no properties (e.g., z.any() which produces {type: 'object', anyOf: [...]}),
94
+ // just return the value as-is without recursing.
95
+ const hasProperties = originalParamSchema.properties &&
96
+ Object.keys(originalParamSchema.properties).length > 0;
97
+ if (hasProperties &&
98
+ typeof objectValue === "object" &&
99
+ objectValue !== null &&
100
+ !Array.isArray(objectValue)) {
101
+ const newParamValue = unstrictifyToolCallParams(originalParamSchema, objectValue);
102
+ return [parameterName, newParamValue];
103
+ }
104
+ // Return the (possibly parsed) object value without recursing
105
+ return [parameterName, objectValue];
106
+ }
107
+ return [parameterName, parameterValue];
108
+ })
109
+ .filter((param) => param !== undefined);
110
+ return Object.fromEntries(newParams);
111
+ }
112
+ /**
113
+ * Unstrictify tool call params using the original JSON Schema.
114
+ *
115
+ * Unlike the private `unstrictifyToolCallParams` which throws on unknown params,
116
+ * this function separates params into schema-defined vs `_tambo_*` pass-through
117
+ * (server-injected params not in the original schema), unstrictifies only the
118
+ * schema-defined ones, and merges pass-through params back. Unknown keys that
119
+ * aren't in the schema and don't have the `_tambo_` prefix are dropped.
120
+ * @returns The params with strictification-induced nulls stripped for optional
121
+ * non-nullable properties, and pass-through params preserved as-is.
122
+ */
123
+ function unstrictifyToolCallParamsFromSchema(originalSchema, params) {
124
+ if (originalSchema.type !== "object") {
125
+ return params;
126
+ }
127
+ const schemaProperties = originalSchema.properties ?? {};
128
+ const schemaDefinedParams = {};
129
+ const passThroughParams = {};
130
+ for (const [key, value] of Object.entries(params)) {
131
+ if (key in schemaProperties) {
132
+ schemaDefinedParams[key] = value;
133
+ }
134
+ else if (key.startsWith("_tambo_")) {
135
+ passThroughParams[key] = value;
136
+ }
137
+ // Unknown keys not in schema and not _tambo_* are dropped —
138
+ // they're likely hallucinated by the model.
139
+ }
140
+ const unstrictified = unstrictifyToolCallParams(originalSchema, schemaDefinedParams);
141
+ return { ...unstrictified, ...passThroughParams };
142
+ }
143
+ /**
144
+ * Check if a JSON Schema definition allows null values.
145
+ * @param originalSchema - The schema definition to check
146
+ * @returns True if the schema allows null values
147
+ */
148
+ function canBeNull(originalSchema) {
149
+ if (typeof originalSchema !== "object") {
150
+ return false;
151
+ }
152
+ if (originalSchema.type === "null") {
153
+ return true;
154
+ }
155
+ if (originalSchema.anyOf?.some((anyOf) => canBeNull(anyOf))) {
156
+ return true;
157
+ }
158
+ return false;
159
+ }
160
+ //# sourceMappingURL=unstrictify.js.map