llmz 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +363 -0
- package/README.md +61 -34
- package/dist/abort-signal.d.ts +40 -0
- package/dist/chat.d.ts +325 -0
- package/dist/{chunk-PRVFVXT4.js → chunk-2Z5SFF6R.js} +302 -2
- package/dist/{chunk-HJKOSEH2.cjs → chunk-GOJY4GRL.cjs} +307 -7
- package/dist/chunk-KG7DT7WD.cjs +476 -0
- package/dist/chunk-OKTHMXRT.js +476 -0
- package/dist/chunk-WL7ZIMYD.cjs +231 -0
- package/dist/chunk-XAN7HQP5.js +231 -0
- package/dist/context.d.ts +212 -0
- package/dist/{exit-YORW76T3.js → exit-7HDRH27N.js} +1 -1
- package/dist/{exit-TRXEU4OU.cjs → exit-O2WZUEFS.cjs} +2 -2
- package/dist/exit.d.ts +333 -0
- package/dist/index.cjs +206 -9
- package/dist/index.d.ts +62 -0
- package/dist/index.js +204 -7
- package/dist/{llmz-ROOX7RYI.js → llmz-MCHRHRTD.js} +109 -35
- package/dist/{llmz-QLZBDG2Z.cjs → llmz-TR4CQK4F.cjs} +116 -42
- package/dist/llmz.d.ts +142 -5
- package/dist/objects.d.ts +314 -0
- package/dist/result.d.ts +430 -0
- package/dist/snapshots.d.ts +169 -0
- package/dist/{tool-N6ODRRGH.js → tool-4AJIJ3QB.js} +1 -1
- package/dist/{tool-QP4MVRWI.cjs → tool-NS7EGK7Z.cjs} +2 -2
- package/dist/tool.d.ts +441 -0
- package/docs/TODO.md +919 -0
- package/package.json +5 -5
- package/dist/chunk-C6WNNTEV.cjs +0 -212
- package/dist/chunk-GWFYZDUR.cjs +0 -105
- package/dist/chunk-JAGB2AOU.js +0 -212
- package/dist/chunk-JMSZKB4T.js +0 -105
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getTypings
|
|
3
|
+
} from "./chunk-IH2WQFO5.js";
|
|
4
|
+
import {
|
|
5
|
+
convertObjectToZuiLiterals,
|
|
6
|
+
isJsonSchema,
|
|
7
|
+
isValidIdentifier,
|
|
8
|
+
isZuiSchema
|
|
9
|
+
} from "./chunk-4MNIJGK6.js";
|
|
10
|
+
import {
|
|
11
|
+
isEmpty_default,
|
|
12
|
+
uniq_default
|
|
13
|
+
} from "./chunk-7WRN4E42.js";
|
|
14
|
+
|
|
15
|
+
// src/tool.ts
|
|
16
|
+
import { z, transforms, ZodObject, ZodType } from "@bpinternal/zui";
|
|
17
|
+
var Tool = class _Tool {
|
|
18
|
+
_staticInputValues;
|
|
19
|
+
name;
|
|
20
|
+
aliases = [];
|
|
21
|
+
description;
|
|
22
|
+
metadata;
|
|
23
|
+
input;
|
|
24
|
+
output;
|
|
25
|
+
retry;
|
|
26
|
+
MAX_RETRIES = 1e3;
|
|
27
|
+
/**
|
|
28
|
+
* Sets static input values that will be automatically applied to all tool calls.
|
|
29
|
+
*
|
|
30
|
+
* Static values allow you to pre-configure certain parameters that shouldn't change
|
|
31
|
+
* between calls, effectively removing them from the LLM's control while ensuring
|
|
32
|
+
* they're always present when the tool executes.
|
|
33
|
+
*
|
|
34
|
+
* @param values - Partial input values to apply statically. Set to null/undefined to clear.
|
|
35
|
+
* @returns The tool instance for method chaining
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* // Create a tool with configurable parameters
|
|
40
|
+
* const searchTool = new Tool({
|
|
41
|
+
* name: 'search',
|
|
42
|
+
* input: z.object({
|
|
43
|
+
* query: z.string(),
|
|
44
|
+
* maxResults: z.number().default(10),
|
|
45
|
+
* includeSnippets: z.boolean().default(true),
|
|
46
|
+
* }),
|
|
47
|
+
* handler: async ({ query, maxResults, includeSnippets }) => {
|
|
48
|
+
* // Implementation
|
|
49
|
+
* },
|
|
50
|
+
* })
|
|
51
|
+
*
|
|
52
|
+
* // Create a restricted version with static values
|
|
53
|
+
* const restrictedSearch = searchTool.setStaticInputValues({
|
|
54
|
+
* maxResults: 5, // Always limit to 5 results
|
|
55
|
+
* includeSnippets: false, // Never include snippets
|
|
56
|
+
* })
|
|
57
|
+
*
|
|
58
|
+
* // LLM can only control 'query' parameter now
|
|
59
|
+
* // The tool will always use maxResults: 5, includeSnippets: false
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // Clear static values
|
|
65
|
+
* const unrestricted = restrictedTool.setStaticInputValues(null)
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
setStaticInputValues(values) {
|
|
69
|
+
if (values === null || values === void 0) {
|
|
70
|
+
this._staticInputValues = void 0;
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
const input = this.input ? transforms.fromJSONSchemaLegacy(this.input) : z.any();
|
|
74
|
+
if (input instanceof z.ZodObject && typeof values !== "object") {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Invalid static input values for tool ${this.name}. Expected an object, but got type "${typeof values}"`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (input instanceof z.ZodArray && !Array.isArray(values)) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Invalid static input values for tool ${this.name}. Expected an array, but got type "${typeof values}"`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
this._staticInputValues = values;
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Gets the computed input schema with static values applied.
|
|
89
|
+
*
|
|
90
|
+
* This property returns the final input schema that will be used for validation,
|
|
91
|
+
* including any static input values that have been set via setStaticInputValues().
|
|
92
|
+
*
|
|
93
|
+
* @returns The Zui schema for input validation
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
96
|
+
get zInput() {
|
|
97
|
+
let input = this.input ? transforms.fromJSONSchemaLegacy(this.input) : z.any();
|
|
98
|
+
if (!isEmpty_default(this._staticInputValues)) {
|
|
99
|
+
const inputExtensions = convertObjectToZuiLiterals(this._staticInputValues);
|
|
100
|
+
if (input instanceof z.ZodObject) {
|
|
101
|
+
input = input.extend(inputExtensions);
|
|
102
|
+
} else if (input instanceof z.ZodArray) {
|
|
103
|
+
input = z.array(input.element.extend(inputExtensions));
|
|
104
|
+
} else {
|
|
105
|
+
input = inputExtensions;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return input;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Gets the output schema for validation.
|
|
112
|
+
*
|
|
113
|
+
* @returns The Zui schema for output validation
|
|
114
|
+
* @internal
|
|
115
|
+
*/
|
|
116
|
+
get zOutput() {
|
|
117
|
+
return this.output ? transforms.fromJSONSchemaLegacy(this.output) : z.void();
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Renames the tool and updates its aliases.
|
|
121
|
+
*
|
|
122
|
+
* @param name - New name for the tool (must be a valid identifier)
|
|
123
|
+
* @returns The tool instance for method chaining
|
|
124
|
+
* @throws Error if the name is not a valid identifier
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const weatherTool = new Tool({ name: 'getWeather', ... })
|
|
129
|
+
* weatherTool.rename('getCurrentWeather')
|
|
130
|
+
* console.log(weatherTool.name) // 'getCurrentWeather'
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
rename(name) {
|
|
134
|
+
const before = this.name;
|
|
135
|
+
if (!isValidIdentifier(name)) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
`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.`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
this.name = name;
|
|
141
|
+
this.aliases = uniq_default([name, ...this.aliases.map((alias) => alias === before ? name : alias)]);
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Creates a new tool based on this one with modified properties.
|
|
146
|
+
*
|
|
147
|
+
* Clone allows you to create variations of existing tools with different names,
|
|
148
|
+
* schemas, handlers, or other configuration. This is useful for creating specialized
|
|
149
|
+
* versions of tools or adding additional functionality.
|
|
150
|
+
*
|
|
151
|
+
* @param props - Properties to override in the cloned tool
|
|
152
|
+
* @returns A new Tool instance with the specified modifications
|
|
153
|
+
* @throws Error if cloning fails due to invalid configuration
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* // Create a basic search tool
|
|
158
|
+
* const basicSearch = new Tool({
|
|
159
|
+
* name: 'search',
|
|
160
|
+
* input: z.object({ query: z.string() }),
|
|
161
|
+
* output: z.object({ results: z.array(z.string()) }),
|
|
162
|
+
* handler: async ({ query }) => ({ results: await search(query) }),
|
|
163
|
+
* })
|
|
164
|
+
*
|
|
165
|
+
* // Clone with enhanced output schema
|
|
166
|
+
* const enhancedSearch = basicSearch.clone({
|
|
167
|
+
* name: 'enhancedSearch',
|
|
168
|
+
* description: 'Search with additional metadata',
|
|
169
|
+
* output: (original) => original!.extend({
|
|
170
|
+
* totalResults: z.number(),
|
|
171
|
+
* searchTime: z.number(),
|
|
172
|
+
* }),
|
|
173
|
+
* handler: async ({ query }) => {
|
|
174
|
+
* const startTime = Date.now()
|
|
175
|
+
* const results = await search(query)
|
|
176
|
+
* return {
|
|
177
|
+
* results: results.items,
|
|
178
|
+
* totalResults: results.total,
|
|
179
|
+
* searchTime: Date.now() - startTime,
|
|
180
|
+
* }
|
|
181
|
+
* },
|
|
182
|
+
* })
|
|
183
|
+
* ```
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* // Clone with restricted access
|
|
188
|
+
* const restrictedTool = adminTool.clone({
|
|
189
|
+
* name: 'userTool',
|
|
190
|
+
* handler: async (input, ctx) => {
|
|
191
|
+
* // Add authorization check
|
|
192
|
+
* if (!isAuthorized(ctx.callId)) {
|
|
193
|
+
* throw new Error('Unauthorized access')
|
|
194
|
+
* }
|
|
195
|
+
* return adminTool.execute(input, ctx)
|
|
196
|
+
* },
|
|
197
|
+
* })
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
clone(props = {}) {
|
|
201
|
+
var _a, _b;
|
|
202
|
+
try {
|
|
203
|
+
const zInput = this.input ? transforms.fromJSONSchemaLegacy(this.input) : void 0;
|
|
204
|
+
const zOutput = this.output ? transforms.fromJSONSchemaLegacy(this.output) : void 0;
|
|
205
|
+
return new _Tool({
|
|
206
|
+
name: props.name ?? this.name,
|
|
207
|
+
aliases: props.aliases ?? [...this.aliases],
|
|
208
|
+
description: props.description ?? this.description,
|
|
209
|
+
metadata: JSON.parse(JSON.stringify(props.metadata ?? this.metadata)),
|
|
210
|
+
input: typeof props.input === "function" ? (_a = props.input) == null ? void 0 : _a.call(props, zInput) : props.input instanceof ZodType ? props.input : zInput,
|
|
211
|
+
output: typeof props.output === "function" ? (_b = props.output) == null ? void 0 : _b.call(props, zOutput) : props.output instanceof ZodType ? props.output : zOutput,
|
|
212
|
+
handler: props.handler ?? this._handler,
|
|
213
|
+
retry: props.retry ?? this.retry
|
|
214
|
+
}).setStaticInputValues(props.staticInputValues ?? this._staticInputValues);
|
|
215
|
+
} catch (e) {
|
|
216
|
+
throw new Error(`Failed to clone tool "${this.name}": ${e}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
_handler;
|
|
220
|
+
/**
|
|
221
|
+
* Creates a new Tool instance.
|
|
222
|
+
*
|
|
223
|
+
* @param props - Tool configuration properties
|
|
224
|
+
* @param props.name - Unique tool name (must be valid TypeScript identifier)
|
|
225
|
+
* @param props.description - Human-readable description for the LLM
|
|
226
|
+
* @param props.input - Zui/Zod schema for input validation (optional)
|
|
227
|
+
* @param props.output - Zui/Zod schema for output validation (optional)
|
|
228
|
+
* @param props.handler - Async function that implements the tool logic
|
|
229
|
+
* @param props.aliases - Alternative names for the tool (optional)
|
|
230
|
+
* @param props.metadata - Additional metadata for the tool (optional)
|
|
231
|
+
* @param props.staticInputValues - Default input values (optional)
|
|
232
|
+
* @param props.retry - Custom retry logic function (optional)
|
|
233
|
+
*
|
|
234
|
+
* @throws Error if name is not a valid identifier
|
|
235
|
+
* @throws Error if description is not a string
|
|
236
|
+
* @throws Error if metadata is not an object
|
|
237
|
+
* @throws Error if handler is not a function
|
|
238
|
+
* @throws Error if aliases contains invalid identifiers
|
|
239
|
+
* @throws Error if input/output schemas are invalid
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const weatherTool = new Tool({
|
|
244
|
+
* name: 'getCurrentWeather',
|
|
245
|
+
* description: 'Fetches current weather data for a given city',
|
|
246
|
+
* aliases: ['weather', 'getWeather'], // Alternative names
|
|
247
|
+
*
|
|
248
|
+
* input: z.object({
|
|
249
|
+
* city: z.string().min(1).describe('City name to get weather for'),
|
|
250
|
+
* units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
|
|
251
|
+
* includeHourly: z.boolean().default(false),
|
|
252
|
+
* }),
|
|
253
|
+
*
|
|
254
|
+
* output: z.object({
|
|
255
|
+
* temperature: z.number(),
|
|
256
|
+
* description: z.string(),
|
|
257
|
+
* humidity: z.number().min(0).max(100),
|
|
258
|
+
* hourlyForecast: z.array(z.object({
|
|
259
|
+
* hour: z.number(),
|
|
260
|
+
* temp: z.number(),
|
|
261
|
+
* condition: z.string(),
|
|
262
|
+
* })).optional(),
|
|
263
|
+
* }),
|
|
264
|
+
*
|
|
265
|
+
* async handler({ city, units, includeHourly }) {
|
|
266
|
+
* const response = await fetch(`/api/weather?city=${encodeURIComponent(city)}&units=${units}`)
|
|
267
|
+
* const data = await response.json()
|
|
268
|
+
*
|
|
269
|
+
* const result = {
|
|
270
|
+
* temperature: data.main.temp,
|
|
271
|
+
* description: data.weather[0].description,
|
|
272
|
+
* humidity: data.main.humidity,
|
|
273
|
+
* }
|
|
274
|
+
*
|
|
275
|
+
* if (includeHourly) {
|
|
276
|
+
* result.hourlyForecast = data.hourly?.slice(0, 24) || []
|
|
277
|
+
* }
|
|
278
|
+
*
|
|
279
|
+
* return result
|
|
280
|
+
* },
|
|
281
|
+
*
|
|
282
|
+
* // Optional: Add retry logic for network errors
|
|
283
|
+
* retry: ({ attempt, error }) => {
|
|
284
|
+
* return attempt < 3 && error.message.includes('network')
|
|
285
|
+
* },
|
|
286
|
+
*
|
|
287
|
+
* // Optional: Add metadata
|
|
288
|
+
* metadata: {
|
|
289
|
+
* category: 'weather',
|
|
290
|
+
* rateLimit: 100,
|
|
291
|
+
* cacheable: true,
|
|
292
|
+
* },
|
|
293
|
+
* })
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
constructor(props) {
|
|
297
|
+
if (!isValidIdentifier(props.name)) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
`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.`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
if (props.description !== void 0 && typeof props.description !== "string") {
|
|
303
|
+
throw new Error(
|
|
304
|
+
`Invalid description for tool ${props.name}. Expected a string, but got type "${typeof props.description}"`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
if (props.metadata !== void 0 && typeof props.metadata !== "object") {
|
|
308
|
+
throw new Error(
|
|
309
|
+
`Invalid metadata for tool ${props.name}. Expected an object, but got type "${typeof props.metadata}"`
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
if (typeof props.handler !== "function") {
|
|
313
|
+
throw new Error(
|
|
314
|
+
`Invalid handler for tool ${props.name}. Expected a function, but got type "${typeof props.handler}"`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
if (props.aliases !== void 0 && !Array.isArray(props.aliases)) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`Invalid aliases for tool ${props.name}. Expected an array, but got type "${typeof props.aliases}"`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
if (props.aliases && props.aliases.some((alias) => !isValidIdentifier(alias))) {
|
|
323
|
+
throw new Error(`Invalid aliases for tool ${props.name}. Expected an array of valid identifiers.`);
|
|
324
|
+
}
|
|
325
|
+
if (typeof props.input !== "undefined") {
|
|
326
|
+
if (isZuiSchema(props.input)) {
|
|
327
|
+
this.input = transforms.toJSONSchemaLegacy(props.input);
|
|
328
|
+
} else if (isJsonSchema(props.input)) {
|
|
329
|
+
this.input = props.input;
|
|
330
|
+
} else {
|
|
331
|
+
throw new Error(
|
|
332
|
+
`Invalid input schema for tool ${props.name}. Expected a ZodType or JSONSchema, but got type "${typeof props.input}"`
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (typeof props.output !== "undefined") {
|
|
337
|
+
if (isZuiSchema(props.output)) {
|
|
338
|
+
this.output = transforms.toJSONSchemaLegacy(props.output);
|
|
339
|
+
} else if (isJsonSchema(props.output)) {
|
|
340
|
+
this.output = props.output;
|
|
341
|
+
} else {
|
|
342
|
+
throw new Error(
|
|
343
|
+
`Invalid output schema for tool ${props.name}. Expected a ZodType or JSONSchema, but got type "${typeof props.output}"`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
this.name = props.name;
|
|
348
|
+
this.aliases = uniq_default([props.name, ...props.aliases ?? []]);
|
|
349
|
+
this.description = props.description;
|
|
350
|
+
this.metadata = props.metadata ?? {};
|
|
351
|
+
this._handler = props.handler;
|
|
352
|
+
this.setStaticInputValues(props.staticInputValues);
|
|
353
|
+
this.retry = props.retry;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Executes the tool with the given input and context.
|
|
357
|
+
*
|
|
358
|
+
* This method handles input validation, retry logic, output validation, and error handling.
|
|
359
|
+
* It's called internally by the LLMz execution engine when generated code calls the tool.
|
|
360
|
+
*
|
|
361
|
+
* @param input - Input data to pass to the tool handler
|
|
362
|
+
* @param ctx - Tool call context containing call ID and other metadata
|
|
363
|
+
* @returns Promise resolving to the tool's output
|
|
364
|
+
* @throws Error if input validation fails
|
|
365
|
+
* @throws Error if tool execution fails after all retries
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* ```typescript
|
|
369
|
+
* // Direct tool execution (usually done by LLMz internally)
|
|
370
|
+
* const result = await weatherTool.execute(
|
|
371
|
+
* { city: 'San Francisco', units: 'fahrenheit' },
|
|
372
|
+
* { callId: 'call_123' }
|
|
373
|
+
* )
|
|
374
|
+
* console.log(result.temperature) // 72
|
|
375
|
+
* ```
|
|
376
|
+
*
|
|
377
|
+
* @internal This method is primarily used internally by the LLMz execution engine
|
|
378
|
+
*/
|
|
379
|
+
async execute(input, ctx) {
|
|
380
|
+
var _a;
|
|
381
|
+
const pInput = this.zInput.safeParse(input);
|
|
382
|
+
if (!pInput.success) {
|
|
383
|
+
throw new Error(`Tool "${this.name}" received invalid input: ${pInput.error.message}`);
|
|
384
|
+
}
|
|
385
|
+
let attempt = 0;
|
|
386
|
+
while (attempt < this.MAX_RETRIES) {
|
|
387
|
+
try {
|
|
388
|
+
const result = await this._handler(pInput.data, ctx);
|
|
389
|
+
const pOutput = this.zOutput.safeParse(result);
|
|
390
|
+
return pOutput.success ? pOutput.data : result;
|
|
391
|
+
} catch (err) {
|
|
392
|
+
const shouldRetry = await ((_a = this.retry) == null ? void 0 : _a.call(this, {
|
|
393
|
+
input: pInput.data,
|
|
394
|
+
attempt: ++attempt,
|
|
395
|
+
error: err
|
|
396
|
+
}));
|
|
397
|
+
if (!shouldRetry) {
|
|
398
|
+
throw err;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
throw new Error(
|
|
403
|
+
`Tool "${this.name}" failed after ${this.MAX_RETRIES} attempts. Last error: ${JSON.stringify(input)}`
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Generates TypeScript type definitions for this tool.
|
|
408
|
+
*
|
|
409
|
+
* This method creates TypeScript function declarations that are included in the
|
|
410
|
+
* LLM's context to help it understand how to call the tool correctly. The generated
|
|
411
|
+
* types include parameter types, return types, and JSDoc comments with descriptions.
|
|
412
|
+
*
|
|
413
|
+
* @returns Promise resolving to TypeScript declaration string
|
|
414
|
+
*/
|
|
415
|
+
async getTypings() {
|
|
416
|
+
let input = this.input ? transforms.fromJSONSchemaLegacy(this.input) : void 0;
|
|
417
|
+
const output = this.output ? transforms.fromJSONSchemaLegacy(this.output) : z.void();
|
|
418
|
+
if ((input == null ? void 0 : input.naked()) instanceof ZodObject && typeof this._staticInputValues === "object" && !isEmpty_default(this._staticInputValues)) {
|
|
419
|
+
const inputExtensions = convertObjectToZuiLiterals(this._staticInputValues);
|
|
420
|
+
input = input.extend(inputExtensions);
|
|
421
|
+
} else if (this._staticInputValues !== void 0) {
|
|
422
|
+
input = convertObjectToZuiLiterals(this._staticInputValues);
|
|
423
|
+
}
|
|
424
|
+
const fnType = z.function(input, z.promise(output)).title(this.name).describe(this.description ?? "");
|
|
425
|
+
return getTypings(fnType, {
|
|
426
|
+
declaration: true
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Ensures all tools in an array have unique names by appending numbers to duplicates.
|
|
431
|
+
*
|
|
432
|
+
* When multiple tools have the same name, this method renames them by appending
|
|
433
|
+
* incrementing numbers (tool1, tool2, etc.) to avoid conflicts. Tools with already
|
|
434
|
+
* unique names are left unchanged.
|
|
435
|
+
*
|
|
436
|
+
* You usually don't need to call this method directly, as LLMz will automatically detect and rename tools with conflicting names.
|
|
437
|
+
*
|
|
438
|
+
* @param tools - Array of tools to process for unique names
|
|
439
|
+
* @returns Array of tools with guaranteed unique names
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ```typescript
|
|
443
|
+
* const tools = [
|
|
444
|
+
* new Tool({ name: 'search', handler: async () => {} }),
|
|
445
|
+
* new Tool({ name: 'search', handler: async () => {} }),
|
|
446
|
+
* new Tool({ name: 'unique', handler: async () => {} }),
|
|
447
|
+
* new Tool({ name: 'search', handler: async () => {} }),
|
|
448
|
+
* ]
|
|
449
|
+
*
|
|
450
|
+
* const uniqueTools = Tool.withUniqueNames(tools)
|
|
451
|
+
* console.log(uniqueTools.map(t => t.name))
|
|
452
|
+
* // Output: ['search1', 'search1', 'unique', 'search']
|
|
453
|
+
* // Note: The last occurrence keeps the original name
|
|
454
|
+
* ```
|
|
455
|
+
*
|
|
456
|
+
* @static
|
|
457
|
+
*/
|
|
458
|
+
static withUniqueNames = (tools) => {
|
|
459
|
+
const names = /* @__PURE__ */ new Set();
|
|
460
|
+
return tools.map((tool) => {
|
|
461
|
+
if (tools.filter((t) => t.name === tool.name).length === 1) {
|
|
462
|
+
return tool;
|
|
463
|
+
}
|
|
464
|
+
let counter = 1;
|
|
465
|
+
let toolName = tool.name + counter;
|
|
466
|
+
while (names.has(toolName)) {
|
|
467
|
+
toolName = `${tool.name}${++counter}`;
|
|
468
|
+
}
|
|
469
|
+
return tool.rename(toolName);
|
|
470
|
+
});
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
export {
|
|
475
|
+
Tool
|
|
476
|
+
};
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } var _class;
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
var _chunk276Q6EWPcjs = require('./chunk-276Q6EWP.cjs');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
var _chunkUQOBUJIQcjs = require('./chunk-UQOBUJIQ.cjs');
|
|
9
|
+
|
|
10
|
+
// src/exit.ts
|
|
11
|
+
var _zui = require('@bpinternal/zui');
|
|
12
|
+
var Exit = (_class = class _Exit {
|
|
13
|
+
/** The primary name of the exit (used in return statements) */
|
|
14
|
+
|
|
15
|
+
/** Alternative names that can be used to reference this exit */
|
|
16
|
+
__init() {this.aliases = []}
|
|
17
|
+
/** Human-readable description of when this exit should be used */
|
|
18
|
+
|
|
19
|
+
/** Additional metadata for orchestration and custom logic */
|
|
20
|
+
|
|
21
|
+
/** JSON Schema for validating exit result data */
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns the Zod schema equivalent of the JSON schema (if available).
|
|
25
|
+
* Used internally for validation and type inference.
|
|
26
|
+
*/
|
|
27
|
+
get zSchema() {
|
|
28
|
+
return this.schema ? _zui.transforms.fromJSONSchemaLegacy(this.schema) : void 0;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Renames the exit and updates aliases accordingly.
|
|
32
|
+
*
|
|
33
|
+
* @param name - The new name for the exit (must be a valid identifier)
|
|
34
|
+
* @returns This exit instance for chaining
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const exit = new Exit({ name: 'old_name', description: 'Test exit' })
|
|
39
|
+
* exit.rename('new_name')
|
|
40
|
+
* console.log(exit.name) // 'new_name'
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
rename(name) {
|
|
44
|
+
const before = this.name;
|
|
45
|
+
if (!_chunk276Q6EWPcjs.isValidIdentifier.call(void 0, name)) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Invalid name for exit ${name}. An exit name must start with a letter and contain only letters, numbers, and underscores. It must be 1-50 characters long.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
this.name = name;
|
|
51
|
+
this.aliases = _chunkUQOBUJIQcjs.uniq_default.call(void 0, [name, ...this.aliases.map((alias) => alias === before ? name : alias)]);
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Creates a deep copy of this exit.
|
|
56
|
+
*
|
|
57
|
+
* The clone is completely independent and can be modified without affecting
|
|
58
|
+
* the original exit. This is useful for creating variations of existing exits.
|
|
59
|
+
*
|
|
60
|
+
* @returns A new Exit instance with the same configuration
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const originalExit = new Exit({
|
|
65
|
+
* name: 'base',
|
|
66
|
+
* description: 'Base exit',
|
|
67
|
+
* schema: z.object({ status: z.string() }),
|
|
68
|
+
* })
|
|
69
|
+
*
|
|
70
|
+
* const customExit = originalExit.clone().rename('custom')
|
|
71
|
+
* // customExit is independent of originalExit
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
clone() {
|
|
75
|
+
return new _Exit({
|
|
76
|
+
name: this.name,
|
|
77
|
+
aliases: [...this.aliases],
|
|
78
|
+
description: this.description,
|
|
79
|
+
metadata: JSON.parse(JSON.stringify(this.metadata)),
|
|
80
|
+
schema: this.zSchema
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Type guard to check if this exit matches another exit by name.
|
|
85
|
+
*
|
|
86
|
+
* Used internally for type narrowing and exit comparison.
|
|
87
|
+
*
|
|
88
|
+
* @param exit - The exit to compare against
|
|
89
|
+
* @returns True if the exits have the same name
|
|
90
|
+
*/
|
|
91
|
+
is(exit) {
|
|
92
|
+
return this.name === exit.name;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Type guard to check if an ExitResult matches this exit.
|
|
96
|
+
*
|
|
97
|
+
* @param result - The exit result to check
|
|
98
|
+
* @returns True if the result was created by this exit
|
|
99
|
+
*/
|
|
100
|
+
match(result) {
|
|
101
|
+
return result.exit instanceof _Exit && this.name === result.exit.name;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Creates a new Exit instance.
|
|
105
|
+
*
|
|
106
|
+
* @param props - Exit configuration
|
|
107
|
+
* @param props.name - Primary name for the exit (must be valid identifier)
|
|
108
|
+
* @param props.description - Human-readable description of the exit's purpose
|
|
109
|
+
* @param props.aliases - Alternative names that can be used to reference this exit
|
|
110
|
+
* @param props.metadata - Additional data for orchestration and custom logic
|
|
111
|
+
* @param props.schema - Zod schema for validating exit result data
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* // Simple exit without data validation
|
|
116
|
+
* const exit = new Exit({
|
|
117
|
+
* name: 'complete',
|
|
118
|
+
* description: 'Task completed successfully',
|
|
119
|
+
* })
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* // Exit with typed result data
|
|
125
|
+
* const approval = new Exit({
|
|
126
|
+
* name: 'approved',
|
|
127
|
+
* description: 'Request approved by system',
|
|
128
|
+
* schema: z.object({
|
|
129
|
+
* amount: z.number().positive(),
|
|
130
|
+
* reference: z.string().min(1),
|
|
131
|
+
* timestamp: z.date().default(() => new Date()),
|
|
132
|
+
* }),
|
|
133
|
+
* })
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* // Exit with aliases and metadata
|
|
139
|
+
* const handoff = new Exit({
|
|
140
|
+
* name: 'handoff_support',
|
|
141
|
+
* aliases: ['escalate', 'transfer'],
|
|
142
|
+
* description: 'Transfer to human support agent',
|
|
143
|
+
* metadata: {
|
|
144
|
+
* department: 'customer_service',
|
|
145
|
+
* priority: 'high',
|
|
146
|
+
* },
|
|
147
|
+
* schema: z.object({
|
|
148
|
+
* reason: z.string(),
|
|
149
|
+
* customerData: z.record(z.any()),
|
|
150
|
+
* }),
|
|
151
|
+
* })
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
constructor(props) {;_class.prototype.__init.call(this);
|
|
155
|
+
if (!_chunk276Q6EWPcjs.isValidIdentifier.call(void 0, props.name)) {
|
|
156
|
+
throw new Error(
|
|
157
|
+
`Invalid name for exit ${props.name}. A exit name must start with a letter and contain only letters, numbers, and underscores. It must be 1-50 characters long.`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
if (typeof props.description !== "string" || props.description.trim().length === 0) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Invalid description for exit ${props.name}. Expected a non-empty string, but got type "${typeof props.description}"`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (props.metadata !== void 0 && typeof props.metadata !== "object") {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Invalid metadata for exit ${props.name}. Expected an object, but got type "${typeof props.metadata}"`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (props.aliases !== void 0 && !Array.isArray(props.aliases)) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`Invalid aliases for exit ${props.name}. Expected an array, but got type "${typeof props.aliases}"`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (props.aliases && props.aliases.some((alias) => !_chunk276Q6EWPcjs.isValidIdentifier.call(void 0, alias))) {
|
|
176
|
+
throw new Error(`Invalid aliases for exit ${props.name}. Expected an array of valid identifiers.`);
|
|
177
|
+
}
|
|
178
|
+
if (typeof props.schema !== "undefined") {
|
|
179
|
+
if (_chunk276Q6EWPcjs.isZuiSchema.call(void 0, props.schema)) {
|
|
180
|
+
this.schema = _zui.transforms.toJSONSchemaLegacy(props.schema);
|
|
181
|
+
} else if (_chunk276Q6EWPcjs.isJsonSchema.call(void 0, props.schema)) {
|
|
182
|
+
this.schema = props.schema;
|
|
183
|
+
} else {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`Invalid input schema for exit ${props.name}. Expected a ZodType or JSONSchema, but got type "${typeof props.schema}"`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.name = props.name;
|
|
190
|
+
this.aliases = _chunkUQOBUJIQcjs.uniq_default.call(void 0, [props.name, ..._nullishCoalesce(props.aliases, () => ( []))]);
|
|
191
|
+
this.description = props.description;
|
|
192
|
+
this.metadata = _nullishCoalesce(props.metadata, () => ( {}));
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Ensures all exits in an array have unique names by renaming duplicates.
|
|
196
|
+
*
|
|
197
|
+
* When multiple exits have the same name, this method appends numbers to
|
|
198
|
+
* create unique names (e.g., 'exit1', 'exit2'). This prevents naming conflicts
|
|
199
|
+
* in execution contexts with multiple exits.
|
|
200
|
+
*
|
|
201
|
+
* @param exits - Array of exits that may have duplicate names
|
|
202
|
+
* @returns Array of exits with guaranteed unique names
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* const exit1 = new Exit({ name: 'done', description: 'First done' })
|
|
207
|
+
* const exit2 = new Exit({ name: 'done', description: 'Second done' })
|
|
208
|
+
*
|
|
209
|
+
* const uniqueExits = Exit.withUniqueNames([exit1, exit2])
|
|
210
|
+
* // Result: [{ name: 'done' }, { name: 'done1' }]
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
static __initStatic() {this.withUniqueNames = (exits) => {
|
|
214
|
+
const names = /* @__PURE__ */ new Set();
|
|
215
|
+
return exits.map((exit) => {
|
|
216
|
+
if (exits.filter((t) => t.name === exit.name).length === 1) {
|
|
217
|
+
return exit;
|
|
218
|
+
}
|
|
219
|
+
let counter = 1;
|
|
220
|
+
let exitName = exit.name + counter;
|
|
221
|
+
while (names.has(exitName)) {
|
|
222
|
+
exitName = `${exit.name}${++counter}`;
|
|
223
|
+
}
|
|
224
|
+
return exit.rename(exitName);
|
|
225
|
+
});
|
|
226
|
+
}}
|
|
227
|
+
}, _class.__initStatic(), _class);
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
exports.Exit = Exit;
|