@lantos1618/better-ui 0.2.2 → 0.3.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 (59) hide show
  1. package/README.md +231 -148
  2. package/dist/index.d.mts +314 -0
  3. package/dist/index.d.ts +314 -0
  4. package/dist/index.js +522 -0
  5. package/dist/index.mjs +491 -0
  6. package/package.json +59 -20
  7. package/lib/aui/README.md +0 -136
  8. package/lib/aui/__tests__/aui-complete.test.ts +0 -251
  9. package/lib/aui/__tests__/aui-comprehensive.test.ts +0 -376
  10. package/lib/aui/__tests__/aui-concise.test.ts +0 -278
  11. package/lib/aui/__tests__/aui-integration.test.ts +0 -309
  12. package/lib/aui/__tests__/aui-simple.test.ts +0 -116
  13. package/lib/aui/__tests__/aui.test.ts +0 -269
  14. package/lib/aui/__tests__/concise-api.test.ts +0 -165
  15. package/lib/aui/__tests__/core.test.ts +0 -265
  16. package/lib/aui/__tests__/simple-api.test.ts +0 -200
  17. package/lib/aui/ai-assistant.ts +0 -408
  18. package/lib/aui/ai-control.ts +0 -353
  19. package/lib/aui/client/use-aui.ts +0 -55
  20. package/lib/aui/client-control.ts +0 -551
  21. package/lib/aui/client-executor.ts +0 -417
  22. package/lib/aui/components/ToolRenderer.tsx +0 -22
  23. package/lib/aui/core.ts +0 -137
  24. package/lib/aui/demo.tsx +0 -89
  25. package/lib/aui/examples/ai-complete-demo.tsx +0 -359
  26. package/lib/aui/examples/ai-control-demo.tsx +0 -356
  27. package/lib/aui/examples/ai-control-tools.ts +0 -308
  28. package/lib/aui/examples/concise-api.tsx +0 -153
  29. package/lib/aui/examples/index.tsx +0 -163
  30. package/lib/aui/examples/quick-demo.tsx +0 -91
  31. package/lib/aui/examples/simple-demo.tsx +0 -71
  32. package/lib/aui/examples/simple-tools.tsx +0 -160
  33. package/lib/aui/examples/user-api.tsx +0 -208
  34. package/lib/aui/examples/user-requested.tsx +0 -174
  35. package/lib/aui/examples/weather-search-tools.tsx +0 -119
  36. package/lib/aui/examples.tsx +0 -367
  37. package/lib/aui/hooks/useAUITool.ts +0 -142
  38. package/lib/aui/hooks/useAUIToolEnhanced.ts +0 -343
  39. package/lib/aui/hooks/useAUITools.ts +0 -195
  40. package/lib/aui/index.ts +0 -156
  41. package/lib/aui/provider.tsx +0 -45
  42. package/lib/aui/server-control.ts +0 -386
  43. package/lib/aui/server-executor.ts +0 -165
  44. package/lib/aui/server.ts +0 -167
  45. package/lib/aui/tool-registry.ts +0 -380
  46. package/lib/aui/tools/advanced-examples.tsx +0 -86
  47. package/lib/aui/tools/ai-complete.ts +0 -375
  48. package/lib/aui/tools/api-tools.tsx +0 -230
  49. package/lib/aui/tools/data-tools.tsx +0 -232
  50. package/lib/aui/tools/dom-tools.tsx +0 -202
  51. package/lib/aui/tools/examples.ts +0 -43
  52. package/lib/aui/tools/file-tools.tsx +0 -202
  53. package/lib/aui/tools/form-tools.tsx +0 -233
  54. package/lib/aui/tools/index.ts +0 -8
  55. package/lib/aui/tools/navigation-tools.tsx +0 -172
  56. package/lib/aui/tools/notification-tools.ts +0 -213
  57. package/lib/aui/tools/state-tools.tsx +0 -209
  58. package/lib/aui/types.ts +0 -47
  59. package/lib/aui/vercel-ai.ts +0 -100
@@ -1,376 +0,0 @@
1
- import aui from '../index';
2
- import { z } from 'zod';
3
-
4
- describe('AUI Comprehensive Tests', () => {
5
- beforeEach(() => {
6
- aui.clear();
7
- });
8
-
9
- describe('Core AUI Functionality', () => {
10
- it('should create a simple tool with execute and render', async () => {
11
- const tool = aui
12
- .tool('test-tool')
13
- .input(z.object({ value: z.number() }))
14
- .execute(async ({ input }) => ({ result: input.value * 2 }));
15
-
16
- expect(tool.name).toBe('test-tool');
17
-
18
- const result = await tool.run({ value: 5 });
19
- expect(result).toEqual({ result: 10 });
20
- });
21
-
22
- it('should handle client execution when not on server', async () => {
23
- const clientFn = jest.fn(async ({ input }) => ({ server: false, client: true, value: input.value }));
24
- const serverFn = jest.fn(async ({ input }) => ({ server: true, value: input.value }));
25
-
26
- const tool = aui
27
- .tool('dual-tool')
28
- .input(z.object({ value: z.string() }))
29
- .execute(serverFn)
30
- .clientExecute(clientFn);
31
-
32
- // Simulate client environment
33
- const result = await tool.run(
34
- { value: 'test' },
35
- {
36
- isServer: false,
37
- cache: new Map(),
38
- fetch: global.fetch
39
- }
40
- );
41
-
42
- expect(result).toEqual({ server: false, client: true, value: 'test' });
43
- expect(clientFn).toHaveBeenCalled();
44
- expect(serverFn).not.toHaveBeenCalled();
45
- });
46
-
47
- it('should use server execution when on server', async () => {
48
- const clientFn = jest.fn(async ({ input }) => ({ server: false, client: true }));
49
- const serverFn = jest.fn(async ({ input }) => ({ server: true }));
50
-
51
- const tool = aui
52
- .tool('server-tool')
53
- .execute(serverFn)
54
- .clientExecute(clientFn);
55
-
56
- const result = await tool.run(
57
- {},
58
- {
59
- isServer: true,
60
- cache: new Map(),
61
- fetch: global.fetch
62
- }
63
- );
64
-
65
- expect(result).toEqual({ server: true });
66
- expect(serverFn).toHaveBeenCalled();
67
- expect(clientFn).not.toHaveBeenCalled();
68
- });
69
-
70
- it('should validate input with Zod schema', async () => {
71
- const tool = aui
72
- .tool('validated-tool')
73
- .input(z.object({
74
- name: z.string().min(3),
75
- age: z.number().positive()
76
- }))
77
- .execute(async ({ input }) => input);
78
-
79
- await expect(tool.run({ name: 'ab', age: 25 }))
80
- .rejects.toThrow();
81
-
82
- await expect(tool.run({ name: 'Alice', age: -5 }))
83
- .rejects.toThrow();
84
-
85
- const result = await tool.run({ name: 'Alice', age: 25 });
86
- expect(result).toEqual({ name: 'Alice', age: 25 });
87
- });
88
-
89
- it('should support middleware', async () => {
90
- const middleware1 = jest.fn(async ({ input, next }) => {
91
- const result = await next();
92
- return { ...result, middleware1: true };
93
- });
94
-
95
- const middleware2 = jest.fn(async ({ input, next }) => {
96
- const result = await next();
97
- return { ...result, middleware2: true };
98
- });
99
-
100
- const tool = aui
101
- .tool('middleware-tool')
102
- .execute(async () => ({ base: true }))
103
- .middleware(middleware1)
104
- .middleware(middleware2);
105
-
106
- const result = await tool.run({});
107
-
108
- expect(result).toEqual({
109
- base: true,
110
- middleware1: true,
111
- middleware2: true
112
- });
113
- expect(middleware1).toHaveBeenCalled();
114
- expect(middleware2).toHaveBeenCalled();
115
- });
116
-
117
- it('should support tags and descriptions', () => {
118
- const tool = aui
119
- .tool('tagged-tool')
120
- .describe('A tool with tags')
121
- .tag('ai', 'control', 'frontend');
122
-
123
- expect(tool.description).toBe('A tool with tags');
124
- expect(tool.tags).toEqual(['ai', 'control', 'frontend']);
125
- });
126
- });
127
-
128
- describe('AUI Instance Management', () => {
129
- it('should store and retrieve tools', () => {
130
- const tool1 = aui.tool('tool1');
131
- const tool2 = aui.tool('tool2');
132
-
133
- expect(aui.has('tool1')).toBe(true);
134
- expect(aui.has('tool2')).toBe(true);
135
- expect(aui.has('tool3')).toBe(false);
136
-
137
- expect(aui.get('tool1')).toBe(tool1);
138
- expect(aui.get('tool2')).toBe(tool2);
139
- expect(aui.get('tool3')).toBeUndefined();
140
- });
141
-
142
- it('should list all tools', () => {
143
- aui.tool('a').tag('type1');
144
- aui.tool('b').tag('type2');
145
- aui.tool('c').tag('type1', 'type2');
146
-
147
- const tools = aui.list();
148
- expect(tools).toHaveLength(3);
149
- expect(tools.map(t => t.name)).toEqual(['a', 'b', 'c']);
150
- });
151
-
152
- it('should find tools by tags', () => {
153
- aui.tool('frontend').tag('ui', 'client');
154
- aui.tool('backend').tag('api', 'server');
155
- aui.tool('fullstack').tag('ui', 'api');
156
-
157
- const uiTools = aui.findByTag('ui');
158
- expect(uiTools).toHaveLength(2);
159
- expect(uiTools.map(t => t.name)).toContain('frontend');
160
- expect(uiTools.map(t => t.name)).toContain('fullstack');
161
-
162
- const apiTools = aui.findByTags('ui', 'api');
163
- expect(apiTools).toHaveLength(1);
164
- expect(apiTools[0].name).toBe('fullstack');
165
- });
166
-
167
- it('should execute tools by name', async () => {
168
- aui
169
- .tool('calculator')
170
- .input(z.object({ a: z.number(), b: z.number() }))
171
- .execute(async ({ input }) => ({ sum: input.a + input.b }));
172
-
173
- const result = await aui.execute('calculator', { a: 3, b: 4 });
174
- expect(result).toEqual({ sum: 7 });
175
-
176
- await expect(aui.execute('nonexistent', {}))
177
- .rejects.toThrow('Tool "nonexistent" not found');
178
- });
179
-
180
- it('should remove tools', () => {
181
- aui.tool('temp');
182
- expect(aui.has('temp')).toBe(true);
183
-
184
- const removed = aui.remove('temp');
185
- expect(removed).toBe(true);
186
- expect(aui.has('temp')).toBe(false);
187
-
188
- const removedAgain = aui.remove('temp');
189
- expect(removedAgain).toBe(false);
190
- });
191
-
192
- it('should clear all tools', () => {
193
- aui.tool('t1');
194
- aui.tool('t2');
195
- aui.tool('t3');
196
-
197
- expect(aui.list()).toHaveLength(3);
198
-
199
- aui.clear();
200
- expect(aui.list()).toHaveLength(0);
201
- });
202
- });
203
-
204
- describe('Context Management', () => {
205
- it('should create default context', () => {
206
- const ctx = aui.createContext();
207
-
208
- expect(ctx.cache).toBeInstanceOf(Map);
209
- expect(ctx.fetch).toBeDefined();
210
- expect(ctx.isServer).toBe(true); // In test environment
211
- });
212
-
213
- it('should merge context additions', () => {
214
- const ctx = aui.createContext({
215
- user: { id: 1, name: 'Test' },
216
- session: { token: 'abc123' },
217
- env: { API_KEY: 'secret' }
218
- });
219
-
220
- expect(ctx.user).toEqual({ id: 1, name: 'Test' });
221
- expect(ctx.session).toEqual({ token: 'abc123' });
222
- expect(ctx.env).toEqual({ API_KEY: 'secret' });
223
- expect(ctx.cache).toBeInstanceOf(Map);
224
- });
225
-
226
- it('should use context cache in client execution', async () => {
227
- const fetchMock = jest.fn(async () => ({ data: 'fresh' }));
228
-
229
- const tool = aui
230
- .tool('cached-tool')
231
- .input(z.object({ key: z.string() }))
232
- .clientExecute(async ({ input, ctx }) => {
233
- const cached = ctx.cache.get(input.key);
234
- if (cached) return cached;
235
-
236
- const result = await fetchMock();
237
- ctx.cache.set(input.key, result);
238
- return result;
239
- });
240
-
241
- const ctx = {
242
- cache: new Map(),
243
- fetch: global.fetch,
244
- isServer: false
245
- };
246
-
247
- // First call - should fetch
248
- const result1 = await tool.run({ key: 'test' }, ctx);
249
- expect(result1).toEqual({ data: 'fresh' });
250
- expect(fetchMock).toHaveBeenCalledTimes(1);
251
-
252
- // Second call - should use cache
253
- const result2 = await tool.run({ key: 'test' }, ctx);
254
- expect(result2).toEqual({ data: 'fresh' });
255
- expect(fetchMock).toHaveBeenCalledTimes(1); // Still 1, not called again
256
- });
257
- });
258
-
259
- describe('Tool Configuration', () => {
260
- it('should return tool configuration', () => {
261
- const tool = aui
262
- .tool('config-tool')
263
- .input(z.object({ value: z.string() }))
264
- .execute(async ({ input }) => input)
265
- .describe('A configurable tool')
266
- .tag('test', 'config');
267
-
268
- const config = tool.getConfig();
269
-
270
- expect(config.name).toBe('config-tool');
271
- expect(config.description).toBe('A configurable tool');
272
- expect(config.tags).toEqual(['test', 'config']);
273
- expect(config.inputSchema).toBeDefined();
274
- expect(config.executeHandler).toBeDefined();
275
- });
276
-
277
- it('should serialize tool to JSON', () => {
278
- const tool = aui
279
- .tool('json-tool')
280
- .input(z.object({ id: z.number() }))
281
- .execute(async () => ({}))
282
- .clientExecute(async () => ({}))
283
- .middleware(async ({ next }) => next())
284
- .describe('JSON serializable tool')
285
- .tag('json');
286
-
287
- const json = tool.toJSON();
288
-
289
- expect(json).toEqual({
290
- name: 'json-tool',
291
- description: 'JSON serializable tool',
292
- tags: ['json'],
293
- hasInput: true,
294
- hasExecute: true,
295
- hasClientExecute: true,
296
- hasRender: false,
297
- hasMiddleware: true
298
- });
299
- });
300
- });
301
-
302
- describe('Complex Tool Scenarios', () => {
303
- it('should handle async operations correctly', async () => {
304
- const delays = [100, 50, 150];
305
- const results: number[] = [];
306
-
307
- const tool = aui
308
- .tool('async-tool')
309
- .input(z.object({ delay: z.number(), value: z.number() }))
310
- .execute(async ({ input }) => {
311
- await new Promise(resolve => setTimeout(resolve, input.delay));
312
- results.push(input.value);
313
- return { processed: input.value };
314
- });
315
-
316
- // Execute in parallel
317
- const promises = delays.map((delay, i) =>
318
- tool.run({ delay, value: i })
319
- );
320
-
321
- const responses = await Promise.all(promises);
322
-
323
- // Results should be in order of completion (50ms, 100ms, 150ms)
324
- expect(results).toEqual([1, 0, 2]);
325
- expect(responses).toEqual([
326
- { processed: 0 },
327
- { processed: 1 },
328
- { processed: 2 }
329
- ]);
330
- });
331
-
332
- it('should handle errors gracefully', async () => {
333
- const tool = aui
334
- .tool('error-tool')
335
- .input(z.object({ shouldFail: z.boolean() }))
336
- .execute(async ({ input }) => {
337
- if (input.shouldFail) {
338
- throw new Error('Intentional failure');
339
- }
340
- return { success: true };
341
- });
342
-
343
- const success = await tool.run({ shouldFail: false });
344
- expect(success).toEqual({ success: true });
345
-
346
- await expect(tool.run({ shouldFail: true }))
347
- .rejects.toThrow('Intentional failure');
348
- });
349
-
350
- it('should chain multiple tools together', async () => {
351
- const preprocessor = aui
352
- .tool('preprocessor')
353
- .input(z.object({ text: z.string() }))
354
- .execute(async ({ input }) => ({
355
- processed: input.text.toUpperCase()
356
- }));
357
-
358
- const analyzer = aui
359
- .tool('analyzer')
360
- .input(z.object({ processed: z.string() }))
361
- .execute(async ({ input }) => ({
362
- length: input.processed.length,
363
- words: input.processed.split(' ').length
364
- }));
365
-
366
- const input = { text: 'hello world' };
367
- const preprocessed = await preprocessor.run(input);
368
- const analyzed = await analyzer.run(preprocessed);
369
-
370
- expect(analyzed).toEqual({
371
- length: 11,
372
- words: 2
373
- });
374
- });
375
- });
376
- });
@@ -1,278 +0,0 @@
1
- import { describe, it, expect, beforeEach } from '@jest/globals';
2
- import aui, { AUITool, z } from '../index';
3
-
4
- describe('AUI Concise API', () => {
5
- beforeEach(() => {
6
- aui.clear();
7
- });
8
-
9
- describe('Simple Tools', () => {
10
- it('should create a simple tool with just input and execute', async () => {
11
- const weatherTool = aui
12
- .tool('weather')
13
- .input(z.object({ city: z.string() }))
14
- .execute(async ({ input }) => ({ temp: 72, city: input.city }));
15
-
16
- const result = await weatherTool.run({ city: 'SF' });
17
- expect(result).toEqual({ temp: 72, city: 'SF' });
18
- });
19
-
20
- it('should work without input schema', async () => {
21
- const simpleTool = aui
22
- .tool('simple')
23
- .execute(async () => ({ success: true }));
24
-
25
- const result = await simpleTool.run({});
26
- expect(result).toEqual({ success: true });
27
- });
28
-
29
- it('should support render method', () => {
30
- const tool = aui
31
- .tool('ui')
32
- .input(z.object({ text: z.string() }))
33
- .execute(async ({ input }) => ({ message: input.text }))
34
- .render(({ data }) => ({ type: 'div', props: { children: data.message } } as any));
35
-
36
- expect(tool.renderer).toBeDefined();
37
- });
38
- });
39
-
40
- describe('Complex Tools with Client Execution', () => {
41
- it('should use client execution when available and not on server', async () => {
42
- const searchTool = aui
43
- .tool('search')
44
- .input(z.object({ query: z.string() }))
45
- .execute(async ({ input }) => ({ server: true, query: input.query }))
46
- .clientExecute(async ({ input }) => ({ server: true, query: input.query }));
47
-
48
- const clientContext = {
49
- cache: new Map(),
50
- fetch: global.fetch,
51
- isServer: false
52
- };
53
-
54
- const result = await searchTool.run({ query: 'test' }, clientContext);
55
- expect(result).toEqual({ server: true, query: 'test' });
56
- });
57
-
58
- it('should use server execution when on server', async () => {
59
- const searchTool = aui
60
- .tool('search')
61
- .input(z.object({ query: z.string() }))
62
- .execute(async ({ input }) => ({ server: true, query: input.query }))
63
- .clientExecute(async ({ input }) => ({ server: false, query: input.query }));
64
-
65
- const serverContext = {
66
- cache: new Map(),
67
- fetch: global.fetch,
68
- isServer: true
69
- };
70
-
71
- const result = await searchTool.run({ query: 'test' }, serverContext);
72
- expect(result).toEqual({ server: true, query: 'test' });
73
- });
74
-
75
- it('should support caching in client execution', async () => {
76
- const cache = new Map();
77
- let fetchCalls = 0;
78
-
79
- const cachedTool = aui
80
- .tool('cached')
81
- .input(z.object({ key: z.string() }))
82
- .clientExecute(async ({ input, ctx }) => {
83
- const cached = ctx.cache.get(input.key);
84
- if (cached) return cached;
85
-
86
- fetchCalls++;
87
- const data = { value: `fetched-${input.key}`, calls: fetchCalls };
88
- ctx.cache.set(input.key, data);
89
- return data;
90
- });
91
-
92
- const context = { cache, fetch: global.fetch, isServer: false };
93
-
94
- const result1 = await cachedTool.run({ key: 'test' }, context);
95
- const result2 = await cachedTool.run({ key: 'test' }, context);
96
-
97
- expect(result1).toEqual({ value: 'fetched-test', calls: 1 });
98
- expect(result2).toEqual({ value: 'fetched-test', calls: 1 });
99
- expect(fetchCalls).toBe(1);
100
- });
101
- });
102
-
103
- describe('AI Control Tools', () => {
104
- it('should create DOM control tool', async () => {
105
- const domTool = aui
106
- .tool('dom')
107
- .input(z.object({
108
- action: z.enum(['click', 'type', 'scroll']),
109
- selector: z.string(),
110
- value: z.string().optional()
111
- }))
112
- .clientExecute(async ({ input }) => {
113
- return { success: true, action: input.action };
114
- });
115
-
116
- const result = await domTool.run(
117
- { action: 'click', selector: '#button' },
118
- { cache: new Map(), fetch: global.fetch, isServer: false }
119
- );
120
-
121
- expect(result).toEqual({ success: true, action: 'click' });
122
- });
123
-
124
- it('should create database control tool', async () => {
125
- const dbTool = aui
126
- .tool('database')
127
- .input(z.object({
128
- operation: z.enum(['find', 'create', 'update', 'delete']),
129
- collection: z.string(),
130
- data: z.any().optional()
131
- }))
132
- .execute(async ({ input }) => {
133
- return {
134
- operation: input.operation,
135
- collection: input.collection,
136
- result: 'mock-data'
137
- };
138
- });
139
-
140
- const result = await dbTool.run({
141
- operation: 'find',
142
- collection: 'users'
143
- });
144
-
145
- expect(result).toEqual({
146
- operation: 'find',
147
- collection: 'users',
148
- result: 'mock-data'
149
- });
150
- });
151
- });
152
-
153
- describe('Tool Middleware', () => {
154
- it('should support middleware', async () => {
155
- const events: string[] = [];
156
-
157
- const toolWithMiddleware = aui
158
- .tool('middleware-test')
159
- .input(z.object({ value: z.number() }))
160
- .middleware(async ({ input, next }) => {
161
- events.push('middleware-1-before');
162
- input.value = input.value * 2;
163
- const result = await next();
164
- events.push('middleware-1-after');
165
- return result;
166
- })
167
- .middleware(async ({ input, next }) => {
168
- events.push('middleware-2-before');
169
- input.value = input.value + 1;
170
- const result = await next();
171
- events.push('middleware-2-after');
172
- return result;
173
- })
174
- .execute(async ({ input }) => {
175
- events.push('execute');
176
- return { finalValue: input.value };
177
- });
178
-
179
- const result = await toolWithMiddleware.run({ value: 5 });
180
-
181
- expect(result).toEqual({ finalValue: 11 }); // (5 * 2) + 1
182
- expect(events).toEqual([
183
- 'middleware-1-before',
184
- 'middleware-2-before',
185
- 'execute',
186
- 'middleware-2-after',
187
- 'middleware-1-after'
188
- ]);
189
- });
190
- });
191
-
192
- describe('Tool Registry', () => {
193
- it('should register and retrieve tools', () => {
194
- const tool1 = aui.tool('tool1').execute(async () => ({ id: 1 }));
195
- const tool2 = aui.tool('tool2').execute(async () => ({ id: 2 }));
196
-
197
- expect(aui.has('tool1')).toBe(true);
198
- expect(aui.has('tool2')).toBe(true);
199
- expect(aui.getToolNames()).toContain('tool1');
200
- expect(aui.getToolNames()).toContain('tool2');
201
- });
202
-
203
- it('should execute tools by name', async () => {
204
- aui.tool('named')
205
- .input(z.object({ value: z.string() }))
206
- .execute(async ({ input }) => ({ result: input.value }));
207
-
208
- const result = await aui.execute('named', { value: 'test' });
209
- expect(result).toEqual({ result: 'test' });
210
- });
211
- });
212
-
213
- describe('Type Safety', () => {
214
- it('should validate input with Zod schema', async () => {
215
- const strictTool = aui
216
- .tool('strict')
217
- .input(z.object({
218
- name: z.string().min(1),
219
- age: z.number().positive()
220
- }))
221
- .execute(async ({ input }) => input);
222
-
223
- await expect(
224
- strictTool.run({ name: '', age: 25 })
225
- ).rejects.toThrow();
226
-
227
- await expect(
228
- strictTool.run({ name: 'John', age: -5 })
229
- ).rejects.toThrow();
230
-
231
- const validResult = await strictTool.run({ name: 'John', age: 25 });
232
- expect(validResult).toEqual({ name: 'John', age: 25 });
233
- });
234
- });
235
-
236
- describe('Tool Composition', () => {
237
- it('should support workflow tools that compose other tools', async () => {
238
- aui.tool('add')
239
- .input(z.object({ a: z.number(), b: z.number() }))
240
- .execute(async ({ input }) => ({ sum: input.a + input.b }));
241
-
242
- aui.tool('multiply')
243
- .input(z.object({ a: z.number(), b: z.number() }))
244
- .execute(async ({ input }) => ({ product: input.a * input.b }));
245
-
246
- const workflowTool = aui
247
- .tool('workflow')
248
- .input(z.object({
249
- steps: z.array(z.object({
250
- tool: z.string(),
251
- input: z.any()
252
- }))
253
- }))
254
- .execute(async ({ input }) => {
255
- const results = [];
256
- for (const step of input.steps) {
257
- const result = await aui.execute(step.tool, step.input);
258
- results.push(result);
259
- }
260
- return { results };
261
- });
262
-
263
- const result = await workflowTool.run({
264
- steps: [
265
- { tool: 'add', input: { a: 2, b: 3 } },
266
- { tool: 'multiply', input: { a: 4, b: 5 } }
267
- ]
268
- });
269
-
270
- expect(result).toEqual({
271
- results: [
272
- { sum: 5 },
273
- { product: 20 }
274
- ]
275
- });
276
- });
277
- });
278
- });