llmz 0.6.0 → 0.6.2

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 (110) hide show
  1. package/DOCS.md +1 -3
  2. package/dist/_baseForOwn-CFhWe_W6.js +48 -0
  3. package/dist/_baseForOwn-buOZDFbK.cjs +103 -0
  4. package/dist/_baseIteratee-CF4qP2a8.cjs +2377 -0
  5. package/dist/_baseIteratee-P_firB7b.js +2138 -0
  6. package/dist/_getPrototype-CG7Dxrrz.js +6 -0
  7. package/dist/_getPrototype-DQDlNVVM.cjs +10 -0
  8. package/dist/_overArg-C5Hy7oCi.cjs +196 -0
  9. package/dist/_overArg-CRYbj1NH.js +155 -0
  10. package/dist/citations.d.ts +11 -11
  11. package/dist/component-BwixNVH8.cjs +73 -0
  12. package/dist/component-DGG7d_Mz.js +44 -0
  13. package/dist/context.d.ts +8 -8
  14. package/dist/custom-client-BPmEoWKu.cjs +2618 -0
  15. package/dist/custom-client-NUoxrFQh.js +2547 -0
  16. package/dist/dual-modes-D_p-jedw.js +2750 -0
  17. package/dist/dual-modes-wAOAlaBS.cjs +2840 -0
  18. package/dist/errors-C962uJdS.cjs +220 -0
  19. package/dist/errors-Cg1G0Awu.js +161 -0
  20. package/dist/execute-BEaq3jkV.cjs +1003 -0
  21. package/dist/execute-DYvFRs54.js +1001 -0
  22. package/dist/exit-BGi0Kk0y.cjs +428 -0
  23. package/dist/exit-CDdxDBRT.js +417 -0
  24. package/dist/index.cjs +1786 -1489
  25. package/dist/index.d.ts +10 -9
  26. package/dist/index.js +1721 -1479
  27. package/dist/index.workerd.js +17534 -16875
  28. package/dist/isEmpty-BhJRCqIF.js +52 -0
  29. package/dist/isEmpty-LY533Wmh.cjs +57 -0
  30. package/dist/isPlainObject-CSxXVKCk.cjs +56 -0
  31. package/dist/isPlainObject-D39AzPO2.js +51 -0
  32. package/dist/jsx-BWvT6ukg.cjs +75 -0
  33. package/dist/jsx-CIgdGGVs.js +52 -0
  34. package/dist/jsx.d.ts +2 -2
  35. package/dist/prompts/chat-mode/system.md.d.ts +1 -1
  36. package/dist/prompts/chat-mode/user.md.d.ts +1 -1
  37. package/dist/prompts/worker-mode/system.md.d.ts +1 -1
  38. package/dist/prompts/worker-mode/user.md.d.ts +1 -1
  39. package/dist/quickjs-variant-BYhMBpwU.js +45 -0
  40. package/dist/quickjs-variant-CsIwTVFC.cjs +56 -0
  41. package/dist/quickjs-variant.workerd.d.ts +1 -1
  42. package/dist/result.d.ts +12 -12
  43. package/dist/rolldown-runtime-EAsCxrDo.js +14 -0
  44. package/dist/runtime/interpret-result.d.ts +1 -1
  45. package/dist/snapshots.d.ts +5 -5
  46. package/dist/stack-traces-BIE_5Xwf.js +21 -0
  47. package/dist/stack-traces-ntw_qhoy.cjs +26 -0
  48. package/dist/tool-CAo-BQHs.js +643 -0
  49. package/dist/tool-DdPZb0JW.cjs +654 -0
  50. package/dist/tool.d.ts +3 -3
  51. package/dist/truncator-B6aaJaN1.cjs +276 -0
  52. package/dist/truncator-DZqEqnpD.js +253 -0
  53. package/dist/typings-BwAz_TM9.cjs +784 -0
  54. package/dist/typings-CrBa7Tyy.js +743 -0
  55. package/dist/uniq-BYQ7GWwb.js +183 -0
  56. package/dist/uniq-G872RWmE.cjs +200 -0
  57. package/dist/utils-UBeAFeEI.js +950 -0
  58. package/dist/utils-ZpJm00ed.cjs +1075 -0
  59. package/dist/vm/errors.d.ts +2 -2
  60. package/dist/vm-BjkJqSQH.cjs +3438 -0
  61. package/dist/vm-C5vf1LH3.js +3436 -0
  62. package/package.json +7 -7
  63. package/dist/chunk-34GJUS6U.js +0 -3266
  64. package/dist/chunk-3UZKLXAD.cjs +0 -3249
  65. package/dist/chunk-4POZJQXN.js +0 -1530
  66. package/dist/chunk-5WBNTV42.cjs +0 -1530
  67. package/dist/chunk-5ZF6NTYI.cjs +0 -33
  68. package/dist/chunk-72ZWBNZV.js +0 -173
  69. package/dist/chunk-DDSJTR6Q.cjs +0 -257
  70. package/dist/chunk-GDJTKI7J.js +0 -792
  71. package/dist/chunk-GLXADNTR.js +0 -199
  72. package/dist/chunk-GOWPACSK.cjs +0 -199
  73. package/dist/chunk-HB2G32AK.js +0 -257
  74. package/dist/chunk-HC2QZTVT.cjs +0 -3266
  75. package/dist/chunk-HEAQAEFZ.cjs +0 -173
  76. package/dist/chunk-HMMM32RG.cjs +0 -792
  77. package/dist/chunk-I56MPOVG.cjs +0 -62
  78. package/dist/chunk-KIN7Y247.cjs +0 -32
  79. package/dist/chunk-KTDBAJBB.cjs +0 -1685
  80. package/dist/chunk-KWQTYXAT.js +0 -62
  81. package/dist/chunk-LOX7GOR6.js +0 -1685
  82. package/dist/chunk-PK72FAKD.cjs +0 -175
  83. package/dist/chunk-PZE6PIPG.cjs +0 -621
  84. package/dist/chunk-RGUG3UMF.cjs +0 -26
  85. package/dist/chunk-TDALRNGL.js +0 -621
  86. package/dist/chunk-UGHFNCPD.js +0 -26
  87. package/dist/chunk-WSVDMGMR.js +0 -175
  88. package/dist/chunk-XTEHT2TK.js +0 -33
  89. package/dist/chunk-YEAWWJSJ.js +0 -32
  90. package/dist/chunk-YRS7KRKP.js +0 -3249
  91. package/dist/component-2NYFY6CW.cjs +0 -14
  92. package/dist/component-ASNZHQUR.js +0 -14
  93. package/dist/dual-modes-S5TJ5JVJ.js +0 -10
  94. package/dist/dual-modes-TCMWBZIH.cjs +0 -10
  95. package/dist/execute-FF7D46TY.js +0 -1109
  96. package/dist/execute-IK6O4SU2.cjs +0 -1109
  97. package/dist/exit-MX56VN2C.cjs +0 -8
  98. package/dist/exit-UQ3EUX5D.js +0 -8
  99. package/dist/jsx-DDZ63UUF.js +0 -13
  100. package/dist/jsx-ULPWX5TW.cjs +0 -13
  101. package/dist/tool-KD7FL45V.js +0 -11
  102. package/dist/tool-VE2YHIY2.cjs +0 -11
  103. package/dist/truncator-73RCL52Y.js +0 -12
  104. package/dist/truncator-FZNLYSDL.cjs +0 -12
  105. package/dist/typings-E2RROCLT.js +0 -10
  106. package/dist/typings-LQOBZXSO.cjs +0 -10
  107. package/dist/utils-IATCUNKF.js +0 -43
  108. package/dist/utils-ITB4NEL7.cjs +0 -43
  109. package/dist/vm-6D3IIIU7.cjs +0 -12
  110. package/dist/vm-QO5FSGPN.js +0 -12
@@ -0,0 +1,643 @@
1
+ import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.js";
2
+ import { t as uniq } from "./uniq-BYQ7GWwb.js";
3
+ import { c as isJsonSchema, l as isValidIdentifier, r as fromJSONSchemaCompat, u as isZuiSchema } from "./utils-UBeAFeEI.js";
4
+ import { t as isEmpty } from "./isEmpty-BhJRCqIF.js";
5
+ import { t as getTypings } from "./typings-CrBa7Tyy.js";
6
+ import { transforms, z } from "@bpinternal/zui";
7
+ //#region src/convert.ts
8
+ function convertObjectToZuiLiterals(obj, nested = false) {
9
+ if (typeof obj === "string") return z.literal(obj).catch(() => obj);
10
+ if (typeof obj === "number") return z.literal(obj).catch(() => obj);
11
+ if (typeof obj === "boolean") return z.literal(obj).catch(() => obj);
12
+ if (Array.isArray(obj)) {
13
+ if (obj.length === 0) return z.tuple([]).catch(() => obj);
14
+ const items = obj.map((child) => convertObjectToZuiLiterals(child, true));
15
+ return z.tuple(items).catch(() => obj);
16
+ }
17
+ if (obj !== null && typeof obj === "object") {
18
+ const shape = {};
19
+ for (const [key, value] of Object.entries(obj)) shape[key] = convertObjectToZuiLiterals(value, true);
20
+ if (nested) return z.object(shape).catch(() => obj);
21
+ if (Object.keys(shape).length === 0) return z.object({}).catch(() => obj);
22
+ return shape;
23
+ }
24
+ if (obj === void 0) return z.undefined().catch(() => void 0);
25
+ if (obj === null) return z.null().catch(() => null);
26
+ throw new Error(`Unsupported object type ${typeof obj}, ${obj})`);
27
+ }
28
+ //#endregion
29
+ //#region src/tool.ts
30
+ var tool_exports = /* @__PURE__ */ __exportAll({ Tool: () => Tool });
31
+ /**
32
+ * Tool represents a callable function that agents can use to interact with external systems.
33
+ *
34
+ * Tools are the core building blocks of LLMz agents, providing type-safe interfaces between
35
+ * the generated TypeScript code and external APIs, databases, file systems, or any other
36
+ * service your agent needs to interact with.
37
+ *
38
+ * ## Key Features
39
+ * - **Type Safety**: Full TypeScript inference with Zui/Zod schema validation
40
+ * - **Input/Output Validation**: Automatic validation of inputs and outputs against schemas
41
+ * - **Retry Logic**: Built-in retry mechanisms with custom retry functions
42
+ * - **Static Values**: Pre-configure parameters that won't change between calls
43
+ * - **Tool Cloning**: Create variations of existing tools with modified behavior
44
+ * - **TypeScript Generation**: Generate type definitions for LLM context
45
+ * - **Error Handling**: Comprehensive error handling with detailed validation messages
46
+ *
47
+ * ## Basic Usage
48
+ *
49
+ * ### Simple Tool
50
+ * ```typescript
51
+ * const weatherTool = new Tool({
52
+ * name: 'getCurrentWeather',
53
+ * description: 'Gets current weather conditions for a city',
54
+ * input: z.object({
55
+ * city: z.string().describe('City name'),
56
+ * units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
57
+ * }),
58
+ * output: z.object({
59
+ * temperature: z.number(),
60
+ * condition: z.string(),
61
+ * humidity: z.number(),
62
+ * }),
63
+ * async handler({ city, units }) {
64
+ * const response = await fetch(`/api/weather?city=${city}&units=${units}`)
65
+ * return response.json()
66
+ * },
67
+ * })
68
+ * ```
69
+ *
70
+ * ### Using Tools in Execute
71
+ * ```typescript
72
+ * const result = await execute({
73
+ * instructions: 'Get the weather for San Francisco and recommend clothing',
74
+ * tools: [weatherTool],
75
+ * client,
76
+ * })
77
+ * ```
78
+ *
79
+ * ## Advanced Patterns
80
+ *
81
+ * ### Static Input Values
82
+ * Pre-configure parameters that shouldn't change:
83
+ * ```typescript
84
+ * const restrictedWeatherTool = weatherTool.setStaticInputValues({
85
+ * units: 'celsius' // Always use celsius, agent can't override
86
+ * })
87
+ * ```
88
+ *
89
+ * ### Retry Logic
90
+ * Add resilience with custom retry logic:
91
+ * ```typescript
92
+ * const resilientTool = new Tool({
93
+ * name: 'apiCall',
94
+ * // ... other properties
95
+ * async handler({ endpoint }) {
96
+ * const response = await fetch(endpoint)
97
+ * if (!response.ok) throw new Error(`API error: ${response.status}`)
98
+ * return response.json()
99
+ * },
100
+ * retry: ({ attempt, error }) => {
101
+ * // Retry up to 3 times for network errors, but not auth errors
102
+ * if (attempt >= 3) return false
103
+ * if (error.message.includes('401') || error.message.includes('403')) return false
104
+ * return true
105
+ * },
106
+ * })
107
+ * ```
108
+ *
109
+ * ### Tool Cloning
110
+ * Create variations of existing tools:
111
+ * ```typescript
112
+ * const enhancedWeatherTool = weatherTool.clone({
113
+ * name: 'getEnhancedWeather',
114
+ * description: 'Gets weather with additional forecasting',
115
+ * output: (original) => original!.extend({
116
+ * forecast: z.array(z.object({
117
+ * day: z.string(),
118
+ * temperature: z.number(),
119
+ * condition: z.string(),
120
+ * })),
121
+ * }),
122
+ * async handler(input) {
123
+ * const basicWeather = await weatherTool.execute(input, ctx)
124
+ * const forecast = await getForecast(input.city)
125
+ * return { ...basicWeather, forecast }
126
+ * },
127
+ * })
128
+ * ```
129
+ *
130
+ * ## Schema Design Best Practices
131
+ *
132
+ * ### Rich Descriptions
133
+ * Use detailed descriptions to help the LLM understand when and how to use tools:
134
+ * ```typescript
135
+ * input: z.object({
136
+ * query: z.string()
137
+ * .min(1)
138
+ * .max(500)
139
+ * .describe('Search query - be specific and include relevant keywords'),
140
+ * maxResults: z.number()
141
+ * .min(1)
142
+ * .max(50)
143
+ * .default(10)
144
+ * .describe('Maximum number of results to return (1-50, default: 10)'),
145
+ * includeSnippets: z.boolean()
146
+ * .default(true)
147
+ * .describe('Whether to include content snippets in results'),
148
+ * })
149
+ * ```
150
+ *
151
+ * ### Enums for Controlled Values
152
+ * Use enums to restrict inputs to valid options:
153
+ * ```typescript
154
+ * input: z.object({
155
+ * operation: z.enum(['create', 'read', 'update', 'delete'])
156
+ * .describe('CRUD operation to perform'),
157
+ * format: z.enum(['json', 'csv', 'xml'])
158
+ * .default('json')
159
+ * .describe('Output format for the response'),
160
+ * })
161
+ * ```
162
+ *
163
+ * ## Error Handling
164
+ *
165
+ * Tools automatically handle:
166
+ * - **Input validation**: Invalid inputs throw descriptive errors
167
+ * - **Output validation**: Outputs are validated but invalid outputs are still returned
168
+ * - **Handler errors**: Exceptions in handlers are caught and can trigger retries
169
+ * - **Type coercion**: Basic type coercion where possible
170
+ *
171
+ */
172
+ var Tool = class Tool {
173
+ _staticInputValues;
174
+ name;
175
+ aliases = [];
176
+ description;
177
+ metadata;
178
+ input;
179
+ output;
180
+ retry;
181
+ MAX_RETRIES = 1e3;
182
+ /**
183
+ * Sets static input values that will be automatically applied to all tool calls.
184
+ *
185
+ * Static values allow you to pre-configure certain parameters that shouldn't change
186
+ * between calls, effectively removing them from the LLM's control while ensuring
187
+ * they're always present when the tool executes.
188
+ *
189
+ * @param values - Partial input values to apply statically. Set to null/undefined to clear.
190
+ * @returns The tool instance for method chaining
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * // Create a tool with configurable parameters
195
+ * const searchTool = new Tool({
196
+ * name: 'search',
197
+ * input: z.object({
198
+ * query: z.string(),
199
+ * maxResults: z.number().default(10),
200
+ * includeSnippets: z.boolean().default(true),
201
+ * }),
202
+ * handler: async ({ query, maxResults, includeSnippets }) => {
203
+ * // Implementation
204
+ * },
205
+ * })
206
+ *
207
+ * // Create a restricted version with static values
208
+ * const restrictedSearch = searchTool.setStaticInputValues({
209
+ * maxResults: 5, // Always limit to 5 results
210
+ * includeSnippets: false, // Never include snippets
211
+ * })
212
+ *
213
+ * // LLM can only control 'query' parameter now
214
+ * // The tool will always use maxResults: 5, includeSnippets: false
215
+ * ```
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * // Clear static values
220
+ * const unrestricted = restrictedTool.setStaticInputValues(null)
221
+ * ```
222
+ */
223
+ setStaticInputValues(values) {
224
+ if (values === null || values === void 0) {
225
+ this._staticInputValues = void 0;
226
+ return this;
227
+ }
228
+ const input = this.input ? fromJSONSchemaCompat(this.input) : z.any();
229
+ if (z.is.zuiObject(input) && typeof values !== "object") throw new Error(`Invalid static input values for tool ${this.name}. Expected an object, but got type "${typeof values}"`);
230
+ if (z.is.zuiArray(input) && !Array.isArray(values)) throw new Error(`Invalid static input values for tool ${this.name}. Expected an array, but got type "${typeof values}"`);
231
+ this._staticInputValues = values;
232
+ return this;
233
+ }
234
+ /**
235
+ * Gets the computed input schema with static values applied.
236
+ *
237
+ * This property returns the final input schema that will be used for validation,
238
+ * including any static input values that have been set via setStaticInputValues().
239
+ *
240
+ * @returns The Zui schema for input validation
241
+ * @internal
242
+ */
243
+ get zInput() {
244
+ let input;
245
+ if (this.input) try {
246
+ input = transforms.fromJSONSchema(this.input);
247
+ } catch {
248
+ input = transforms.fromJSONSchemaLegacy(this.input);
249
+ }
250
+ else input = z.any();
251
+ if (!isEmpty(this._staticInputValues)) {
252
+ if (z.is.zuiObject(input)) {
253
+ const inputExtensions = convertObjectToZuiLiterals(this._staticInputValues);
254
+ input = input.extend(inputExtensions);
255
+ } else if (z.is.zuiArray(input)) {
256
+ const inputExtensions = convertObjectToZuiLiterals(this._staticInputValues);
257
+ input = z.array(input.element.extend(inputExtensions));
258
+ } else input = convertObjectToZuiLiterals(this._staticInputValues);
259
+ }
260
+ return input;
261
+ }
262
+ /**
263
+ * Gets the output schema for validation.
264
+ *
265
+ * @returns The Zui schema for output validation
266
+ * @internal
267
+ */
268
+ get zOutput() {
269
+ let output = z.void();
270
+ if (this.output) try {
271
+ output = transforms.fromJSONSchema(this.output);
272
+ } catch {
273
+ output = transforms.fromJSONSchemaLegacy(this.output);
274
+ }
275
+ return z.promise(output);
276
+ }
277
+ /**
278
+ * Renames the tool and updates its aliases.
279
+ *
280
+ * @param name - New name for the tool (must be a valid identifier)
281
+ * @returns The tool instance for method chaining
282
+ * @throws Error if the name is not a valid identifier
283
+ *
284
+ * @example
285
+ * ```typescript
286
+ * const weatherTool = new Tool({ name: 'getWeather', ... })
287
+ * weatherTool.rename('getCurrentWeather')
288
+ * console.log(weatherTool.name) // 'getCurrentWeather'
289
+ * ```
290
+ */
291
+ rename(name) {
292
+ const before = this.name;
293
+ if (!isValidIdentifier(name)) throw new Error(`Invalid name for tool ${name}. A tool name must start with a letter and contain only letters, numbers, and underscores. It must be 1-50 characters long.`);
294
+ this.name = name;
295
+ this.aliases = uniq([name, ...this.aliases.map((alias) => alias === before ? name : alias)]);
296
+ return this;
297
+ }
298
+ /**
299
+ * Creates a new tool based on this one with modified properties.
300
+ *
301
+ * Clone allows you to create variations of existing tools with different names,
302
+ * schemas, handlers, or other configuration. This is useful for creating specialized
303
+ * versions of tools or adding additional functionality.
304
+ *
305
+ * @param props - Properties to override in the cloned tool
306
+ * @returns A new Tool instance with the specified modifications
307
+ * @throws Error if cloning fails due to invalid configuration
308
+ *
309
+ * @example
310
+ * ```typescript
311
+ * // Create a basic search tool
312
+ * const basicSearch = new Tool({
313
+ * name: 'search',
314
+ * input: z.object({ query: z.string() }),
315
+ * output: z.object({ results: z.array(z.string()) }),
316
+ * handler: async ({ query }) => ({ results: await search(query) }),
317
+ * })
318
+ *
319
+ * // Clone with enhanced output schema
320
+ * const enhancedSearch = basicSearch.clone({
321
+ * name: 'enhancedSearch',
322
+ * description: 'Search with additional metadata',
323
+ * output: (original) => original!.extend({
324
+ * totalResults: z.number(),
325
+ * searchTime: z.number(),
326
+ * }),
327
+ * handler: async ({ query }) => {
328
+ * const startTime = Date.now()
329
+ * const results = await search(query)
330
+ * return {
331
+ * results: results.items,
332
+ * totalResults: results.total,
333
+ * searchTime: Date.now() - startTime,
334
+ * }
335
+ * },
336
+ * })
337
+ * ```
338
+ *
339
+ * @example
340
+ * ```typescript
341
+ * // Clone with restricted access
342
+ * const restrictedTool = adminTool.clone({
343
+ * name: 'userTool',
344
+ * handler: async (input, ctx) => {
345
+ * // Add authorization check
346
+ * if (!isAuthorized(ctx.callId)) {
347
+ * throw new Error('Unauthorized access')
348
+ * }
349
+ * return adminTool.execute(input, ctx)
350
+ * },
351
+ * })
352
+ * ```
353
+ */
354
+ clone(props = {}) {
355
+ try {
356
+ var _props$input, _props$output;
357
+ let zInput;
358
+ if (this.input) try {
359
+ zInput = transforms.fromJSONSchema(this.input);
360
+ } catch {
361
+ zInput = transforms.fromJSONSchemaLegacy(this.input);
362
+ }
363
+ let zOutput;
364
+ if (this.output) try {
365
+ zOutput = transforms.fromJSONSchema(this.output);
366
+ } catch {
367
+ zOutput = transforms.fromJSONSchemaLegacy(this.output);
368
+ }
369
+ return new Tool({
370
+ name: props.name ?? this.name,
371
+ aliases: props.aliases ?? [...this.aliases],
372
+ description: props.description ?? this.description,
373
+ metadata: JSON.parse(JSON.stringify(props.metadata ?? this.metadata)),
374
+ input: typeof props.input === "function" ? (_props$input = props.input) === null || _props$input === void 0 ? void 0 : _props$input.call(props, zInput) : z.is.zuiType(props.input) ? props.input : zInput,
375
+ output: typeof props.output === "function" ? (_props$output = props.output) === null || _props$output === void 0 ? void 0 : _props$output.call(props, zOutput) : z.is.zuiType(props.output) ? props.output : zOutput,
376
+ handler: props.handler ?? this._handler,
377
+ retry: props.retry ?? this.retry
378
+ }).setStaticInputValues(props.staticInputValues ?? this._staticInputValues);
379
+ } catch (e) {
380
+ throw new Error(`Failed to clone tool "${this.name}": ${e}`);
381
+ }
382
+ }
383
+ _handler;
384
+ /**
385
+ * Creates a new Tool instance.
386
+ *
387
+ * @param props - Tool configuration properties
388
+ * @param props.name - Unique tool name (must be valid TypeScript identifier)
389
+ * @param props.description - Human-readable description for the LLM
390
+ * @param props.input - Zui/Zod schema for input validation (optional)
391
+ * @param props.output - Zui/Zod schema for output validation (optional)
392
+ * @param props.handler - Async function that implements the tool logic
393
+ * @param props.aliases - Alternative names for the tool (optional)
394
+ * @param props.metadata - Additional metadata for the tool (optional)
395
+ * @param props.staticInputValues - Default input values (optional)
396
+ * @param props.retry - Custom retry logic function (optional)
397
+ *
398
+ * @throws Error if name is not a valid identifier
399
+ * @throws Error if description is not a string
400
+ * @throws Error if metadata is not an object
401
+ * @throws Error if handler is not a function
402
+ * @throws Error if aliases contains invalid identifiers
403
+ * @throws Error if input/output schemas are invalid
404
+ *
405
+ * @example
406
+ * ```typescript
407
+ * const weatherTool = new Tool({
408
+ * name: 'getCurrentWeather',
409
+ * description: 'Fetches current weather data for a given city',
410
+ * aliases: ['weather', 'getWeather'], // Alternative names
411
+ *
412
+ * input: z.object({
413
+ * city: z.string().min(1).describe('City name to get weather for'),
414
+ * units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
415
+ * includeHourly: z.boolean().default(false),
416
+ * }),
417
+ *
418
+ * output: z.object({
419
+ * temperature: z.number(),
420
+ * description: z.string(),
421
+ * humidity: z.number().min(0).max(100),
422
+ * hourlyForecast: z.array(z.object({
423
+ * hour: z.number(),
424
+ * temp: z.number(),
425
+ * condition: z.string(),
426
+ * })).optional(),
427
+ * }),
428
+ *
429
+ * async handler({ city, units, includeHourly }) {
430
+ * const response = await fetch(`/api/weather?city=${encodeURIComponent(city)}&units=${units}`)
431
+ * const data = await response.json()
432
+ *
433
+ * const result = {
434
+ * temperature: data.main.temp,
435
+ * description: data.weather[0].description,
436
+ * humidity: data.main.humidity,
437
+ * }
438
+ *
439
+ * if (includeHourly) {
440
+ * result.hourlyForecast = data.hourly?.slice(0, 24) || []
441
+ * }
442
+ *
443
+ * return result
444
+ * },
445
+ *
446
+ * // Optional: Add retry logic for network errors
447
+ * retry: ({ attempt, error }) => {
448
+ * return attempt < 3 && error.message.includes('network')
449
+ * },
450
+ *
451
+ * // Optional: Add metadata
452
+ * metadata: {
453
+ * category: 'weather',
454
+ * rateLimit: 100,
455
+ * cacheable: true,
456
+ * },
457
+ * })
458
+ * ```
459
+ */
460
+ constructor(props) {
461
+ if (!isValidIdentifier(props.name)) throw new Error(`Invalid name for tool ${props.name}. A tool name must start with a letter and contain only letters, numbers, and underscores. It must be 1-50 characters long.`);
462
+ if (props.description !== void 0 && typeof props.description !== "string") throw new Error(`Invalid description for tool ${props.name}. Expected a string, but got type "${typeof props.description}"`);
463
+ if (props.metadata !== void 0 && typeof props.metadata !== "object") throw new Error(`Invalid metadata for tool ${props.name}. Expected an object, but got type "${typeof props.metadata}"`);
464
+ if (typeof props.handler !== "function") throw new Error(`Invalid handler for tool ${props.name}. Expected a function, but got type "${typeof props.handler}"`);
465
+ if (props.aliases !== void 0 && !Array.isArray(props.aliases)) throw new Error(`Invalid aliases for tool ${props.name}. Expected an array, but got type "${typeof props.aliases}"`);
466
+ if (props.aliases && props.aliases.some((alias) => !isValidIdentifier(alias))) throw new Error(`Invalid aliases for tool ${props.name}. Expected an array of valid identifiers.`);
467
+ if (typeof props.input !== "undefined") {
468
+ if (isZuiSchema(props.input)) try {
469
+ this.input = transforms.toJSONSchema(props.input);
470
+ } catch {
471
+ this.input = transforms.toJSONSchemaLegacy(props.input);
472
+ }
473
+ else if (isJsonSchema(props.input)) this.input = props.input;
474
+ else throw new Error(`Invalid input schema for tool ${props.name}. Expected a ZodType or JSONSchema, but got type "${typeof props.input}"`);
475
+ }
476
+ if (typeof props.output !== "undefined") {
477
+ if (isZuiSchema(props.output)) try {
478
+ this.output = transforms.toJSONSchema(props.output);
479
+ } catch {
480
+ this.output = transforms.toJSONSchemaLegacy(props.output);
481
+ }
482
+ else if (isJsonSchema(props.output)) this.output = props.output;
483
+ else throw new Error(`Invalid output schema for tool ${props.name}. Expected a ZodType or JSONSchema, but got type "${typeof props.output}"`);
484
+ }
485
+ this.name = props.name;
486
+ this.aliases = uniq([props.name, ...props.aliases ?? []]);
487
+ this.description = props.description;
488
+ this.metadata = props.metadata ?? {};
489
+ this._handler = props.handler;
490
+ this.setStaticInputValues(props.staticInputValues);
491
+ this.retry = props.retry;
492
+ }
493
+ async _executeHandler(input, ctx, chat, yieldedCount, setYieldedCount) {
494
+ const handler = this._handler(input, ctx);
495
+ if (!(typeof handler === "object" && handler !== null && "next" in handler)) return handler;
496
+ let yieldIndex = 0;
497
+ while (true) {
498
+ const { value, done } = await handler.next();
499
+ if (done) return value;
500
+ if (yieldIndex >= yieldedCount) {
501
+ var _chat$handler;
502
+ setYieldedCount(yieldIndex + 1);
503
+ await (chat === null || chat === void 0 || (_chat$handler = chat.handler) === null || _chat$handler === void 0 ? void 0 : _chat$handler.call(chat, value));
504
+ }
505
+ yieldIndex++;
506
+ }
507
+ }
508
+ /**
509
+ * Executes the tool with the given input and context.
510
+ *
511
+ * This method handles input validation, retry logic, output validation, and error handling.
512
+ * It's called internally by the LLMz execution engine when generated code calls the tool.
513
+ *
514
+ * @param input - Input data to pass to the tool handler
515
+ * @param ctx - Tool call context containing call ID and other metadata
516
+ * @returns Promise resolving to the tool's output
517
+ * @throws Error if input validation fails
518
+ * @throws Error if tool execution fails after all retries
519
+ *
520
+ * @example
521
+ * ```typescript
522
+ * // Direct tool execution (usually done by LLMz internally)
523
+ * const result = await weatherTool.execute(
524
+ * { city: 'San Francisco', units: 'fahrenheit' },
525
+ * { callId: 'call_123' }
526
+ * )
527
+ * console.log(result.temperature) // 72
528
+ * ```
529
+ *
530
+ * @internal This method is primarily used internally by the LLMz execution engine
531
+ */
532
+ async execute(rawInput, ctx, chat) {
533
+ const input = this.zInput._def.typeName === "ZodObject" ? rawInput ?? {} : rawInput;
534
+ const pInput = this.zInput.safeParse(input);
535
+ if (!pInput.success) throw new Error(`Tool "${this.name}" received invalid input: ${pInput.error.message}`);
536
+ let attempt = 0;
537
+ let yieldedCount = 0;
538
+ while (attempt < this.MAX_RETRIES) try {
539
+ const result = await this._executeHandler(pInput.data, ctx, chat, yieldedCount, (n) => {
540
+ yieldedCount = n;
541
+ });
542
+ const pOutput = this.zOutput.safeParse(result);
543
+ return pOutput.success ? pOutput.data : result;
544
+ } catch (err) {
545
+ var _this$retry;
546
+ if (!await ((_this$retry = this.retry) === null || _this$retry === void 0 ? void 0 : _this$retry.call(this, {
547
+ input: pInput.data,
548
+ attempt: ++attempt,
549
+ error: err
550
+ }))) throw err;
551
+ }
552
+ throw new Error(`Tool "${this.name}" failed after ${this.MAX_RETRIES} attempts. Last error: ${JSON.stringify(input)}`);
553
+ }
554
+ /**
555
+ * Generates TypeScript type definitions for this tool.
556
+ *
557
+ * This method creates TypeScript function declarations that are included in the
558
+ * LLM's context to help it understand how to call the tool correctly. The generated
559
+ * types include parameter types, return types, and JSDoc comments with descriptions.
560
+ *
561
+ * @returns Promise resolving to TypeScript declaration string
562
+ */
563
+ async getTypings() {
564
+ let input;
565
+ if (this.input) try {
566
+ input = transforms.fromJSONSchema(this.input);
567
+ } catch {
568
+ input = transforms.fromJSONSchemaLegacy(this.input);
569
+ }
570
+ let output;
571
+ if (!this.output || Object.keys(this.output).length === 0) output = z.void();
572
+ else try {
573
+ output = transforms.fromJSONSchema(this.output);
574
+ } catch {
575
+ output = transforms.fromJSONSchemaLegacy(this.output);
576
+ }
577
+ if (input && z.is.zuiObject(input.naked()) && typeof this._staticInputValues === "object" && !isEmpty(this._staticInputValues)) {
578
+ const inputExtensions = convertObjectToZuiLiterals(this._staticInputValues);
579
+ input = input.extend(inputExtensions);
580
+ } else if (this._staticInputValues !== void 0) input = convertObjectToZuiLiterals(this._staticInputValues);
581
+ const fnType = z.function(input, z.promise(output)).title(this.name).describe(this.description ?? "");
582
+ return getTypings(fnType, { declaration: true });
583
+ }
584
+ /**
585
+ * Ensures all tools in an array have unique names by appending numbers to duplicates.
586
+ *
587
+ * When multiple tools have the same name, this method renames them by appending
588
+ * incrementing numbers (tool1, tool2, etc.) to avoid conflicts. Tools with already
589
+ * unique names are left unchanged.
590
+ *
591
+ * You usually don't need to call this method directly, as LLMz will automatically detect and rename tools with conflicting names.
592
+ *
593
+ * @param tools - Array of tools to process for unique names
594
+ * @returns Array of tools with guaranteed unique names
595
+ *
596
+ * @example
597
+ * ```typescript
598
+ * const tools = [
599
+ * new Tool({ name: 'search', handler: async () => {} }),
600
+ * new Tool({ name: 'search', handler: async () => {} }),
601
+ * new Tool({ name: 'unique', handler: async () => {} }),
602
+ * new Tool({ name: 'search', handler: async () => {} }),
603
+ * ]
604
+ *
605
+ * const uniqueTools = Tool.withUniqueNames(tools)
606
+ * console.log(uniqueTools.map(t => t.name))
607
+ * // Output: ['search1', 'search1', 'unique', 'search']
608
+ * // Note: The last occurrence keeps the original name
609
+ * ```
610
+ *
611
+ * @static
612
+ */
613
+ static withUniqueNames = (tools) => {
614
+ const names = /* @__PURE__ */ new Set();
615
+ return tools.map((tool) => {
616
+ if (tools.filter((t) => t.name === tool.name).length === 1) return tool;
617
+ let counter = 1;
618
+ let toolName = tool.name + counter;
619
+ while (names.has(toolName)) toolName = `${tool.name}${++counter}`;
620
+ names.add(toolName);
621
+ return tool.rename(toolName);
622
+ });
623
+ };
624
+ /**
625
+ * Converts the tool to its JSON representation.
626
+ *
627
+ * @returns JSON representation of the Tool instance
628
+ */
629
+ toJSON() {
630
+ return {
631
+ name: this.name,
632
+ aliases: [...this.aliases],
633
+ description: this.description,
634
+ metadata: this.metadata,
635
+ input: this.input,
636
+ output: this.output,
637
+ staticInputValues: this._staticInputValues,
638
+ maxRetries: this.MAX_RETRIES
639
+ };
640
+ }
641
+ };
642
+ //#endregion
643
+ export { tool_exports as n, Tool as t };