@lantos1618/better-ui 0.1.0
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/LICENSE +21 -0
- package/README.md +190 -0
- package/lib/aui/README.md +136 -0
- package/lib/aui/__tests__/aui-complete.test.ts +251 -0
- package/lib/aui/__tests__/aui-comprehensive.test.ts +376 -0
- package/lib/aui/__tests__/aui-concise.test.ts +278 -0
- package/lib/aui/__tests__/aui-integration.test.ts +309 -0
- package/lib/aui/__tests__/aui-simple.test.ts +116 -0
- package/lib/aui/__tests__/aui.test.ts +269 -0
- package/lib/aui/__tests__/concise-api.test.ts +165 -0
- package/lib/aui/__tests__/core.test.ts +265 -0
- package/lib/aui/__tests__/simple-api.test.ts +200 -0
- package/lib/aui/ai-assistant.ts +408 -0
- package/lib/aui/ai-control.ts +353 -0
- package/lib/aui/client/use-aui.ts +55 -0
- package/lib/aui/client-control.ts +551 -0
- package/lib/aui/client-executor.ts +417 -0
- package/lib/aui/components/ToolRenderer.tsx +22 -0
- package/lib/aui/core.ts +137 -0
- package/lib/aui/demo.tsx +89 -0
- package/lib/aui/examples/ai-complete-demo.tsx +359 -0
- package/lib/aui/examples/ai-control-demo.tsx +356 -0
- package/lib/aui/examples/ai-control-tools.ts +308 -0
- package/lib/aui/examples/concise-api.tsx +153 -0
- package/lib/aui/examples/index.tsx +163 -0
- package/lib/aui/examples/quick-demo.tsx +91 -0
- package/lib/aui/examples/simple-demo.tsx +71 -0
- package/lib/aui/examples/simple-tools.tsx +160 -0
- package/lib/aui/examples/user-api.tsx +208 -0
- package/lib/aui/examples/user-requested.tsx +174 -0
- package/lib/aui/examples/weather-search-tools.tsx +119 -0
- package/lib/aui/examples.tsx +367 -0
- package/lib/aui/hooks/useAUITool.ts +142 -0
- package/lib/aui/hooks/useAUIToolEnhanced.ts +343 -0
- package/lib/aui/hooks/useAUITools.ts +195 -0
- package/lib/aui/index.ts +156 -0
- package/lib/aui/provider.tsx +45 -0
- package/lib/aui/server-control.ts +386 -0
- package/lib/aui/server-executor.ts +165 -0
- package/lib/aui/server.ts +167 -0
- package/lib/aui/tool-registry.ts +380 -0
- package/lib/aui/tools/advanced-examples.tsx +86 -0
- package/lib/aui/tools/ai-complete.ts +375 -0
- package/lib/aui/tools/api-tools.tsx +230 -0
- package/lib/aui/tools/data-tools.tsx +232 -0
- package/lib/aui/tools/dom-tools.tsx +202 -0
- package/lib/aui/tools/examples.ts +43 -0
- package/lib/aui/tools/file-tools.tsx +202 -0
- package/lib/aui/tools/form-tools.tsx +233 -0
- package/lib/aui/tools/index.ts +8 -0
- package/lib/aui/tools/navigation-tools.tsx +172 -0
- package/lib/aui/tools/notification-tools.ts +213 -0
- package/lib/aui/tools/state-tools.tsx +209 -0
- package/lib/aui/types.ts +47 -0
- package/lib/aui/vercel-ai.ts +100 -0
- package/package.json +51 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import aui, { z } from '../index';
|
|
2
|
+
|
|
3
|
+
// Simple tool - just 2 methods
|
|
4
|
+
export const simpleTool = aui
|
|
5
|
+
.tool('weather')
|
|
6
|
+
.input(z.object({ city: z.string() }))
|
|
7
|
+
.execute(async ({ input }) => ({ temp: 72, city: input.city }));
|
|
8
|
+
|
|
9
|
+
// With render
|
|
10
|
+
export const withRenderTool = aui
|
|
11
|
+
.tool('temperature')
|
|
12
|
+
.input(z.object({ city: z.string() }))
|
|
13
|
+
.execute(async ({ input }) => ({ temp: 72, city: input.city }))
|
|
14
|
+
.render(({ data }) => <div>{data.city}: {data.temp}°</div>);
|
|
15
|
+
|
|
16
|
+
// Complex tool - adds client optimization
|
|
17
|
+
export const complexTool = aui
|
|
18
|
+
.tool('search')
|
|
19
|
+
.input(z.object({ query: z.string() }))
|
|
20
|
+
.execute(async ({ input }) => {
|
|
21
|
+
// Server-side: database search
|
|
22
|
+
return { results: [`Server result for ${input.query}`] };
|
|
23
|
+
})
|
|
24
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
25
|
+
// Client-side: check cache first, then fetch
|
|
26
|
+
const cached = ctx.cache.get(input.query);
|
|
27
|
+
if (cached) return cached;
|
|
28
|
+
|
|
29
|
+
const result = await ctx.fetch('/api/tools/search', {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
body: JSON.stringify(input)
|
|
32
|
+
}).then(r => r.json());
|
|
33
|
+
|
|
34
|
+
ctx.cache.set(input.query, result);
|
|
35
|
+
return result;
|
|
36
|
+
})
|
|
37
|
+
.render(({ data }) => (
|
|
38
|
+
<ul>
|
|
39
|
+
{data.results.map((r, i) => <li key={i}>{r}</li>)}
|
|
40
|
+
</ul>
|
|
41
|
+
));
|
|
42
|
+
|
|
43
|
+
// AI control tool
|
|
44
|
+
export const aiTool = aui
|
|
45
|
+
.tool('dom-click')
|
|
46
|
+
.input(z.object({ selector: z.string() }))
|
|
47
|
+
.execute(async ({ input }) => {
|
|
48
|
+
const element = document.querySelector(input.selector);
|
|
49
|
+
if (element instanceof HTMLElement) {
|
|
50
|
+
element.click();
|
|
51
|
+
return { success: true, selector: input.selector };
|
|
52
|
+
}
|
|
53
|
+
return { success: false, selector: input.selector };
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// With middleware for auth/logging
|
|
57
|
+
export const protectedTool = aui
|
|
58
|
+
.tool('user-data')
|
|
59
|
+
.input(z.object({ userId: z.string() }))
|
|
60
|
+
.middleware(async ({ input, ctx, next }) => {
|
|
61
|
+
// Check auth
|
|
62
|
+
if (!ctx.user) throw new Error('Unauthorized');
|
|
63
|
+
// Log access
|
|
64
|
+
console.log(`User ${ctx.user.id} accessing ${input.userId}`);
|
|
65
|
+
return next();
|
|
66
|
+
})
|
|
67
|
+
.execute(async ({ input }) => ({
|
|
68
|
+
userId: input.userId,
|
|
69
|
+
data: 'sensitive data'
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
// Chained/composed tools
|
|
73
|
+
export const composedTool = aui
|
|
74
|
+
.tool('weather-report')
|
|
75
|
+
.input(z.object({ city: z.string() }))
|
|
76
|
+
.execute(async ({ input }) => {
|
|
77
|
+
// Get weather
|
|
78
|
+
const weather = await simpleTool.run({ city: input.city });
|
|
79
|
+
// Get forecast
|
|
80
|
+
const forecast = ['Sunny', 'Cloudy', 'Rainy'][Math.floor(Math.random() * 3)];
|
|
81
|
+
return { ...weather, forecast };
|
|
82
|
+
})
|
|
83
|
+
.render(({ data }) => (
|
|
84
|
+
<div>
|
|
85
|
+
<h3>{data.city}</h3>
|
|
86
|
+
<p>Current: {data.temp}°</p>
|
|
87
|
+
<p>Forecast: {data.forecast}</p>
|
|
88
|
+
</div>
|
|
89
|
+
));
|
|
90
|
+
|
|
91
|
+
// Tagged tools for discovery
|
|
92
|
+
export const notificationTool = aui
|
|
93
|
+
.tool('notify')
|
|
94
|
+
.tag('ui', 'notification')
|
|
95
|
+
.describe('Show a notification to the user')
|
|
96
|
+
.input(z.object({
|
|
97
|
+
message: z.string(),
|
|
98
|
+
type: z.enum(['info', 'success', 'error']).optional()
|
|
99
|
+
}))
|
|
100
|
+
.execute(async ({ input }) => {
|
|
101
|
+
// In real app, would show actual notification
|
|
102
|
+
console.log(`[${input.type || 'info'}] ${input.message}`);
|
|
103
|
+
return { shown: true };
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Streaming tool (for Vercel AI SDK)
|
|
107
|
+
export const streamingTool = aui
|
|
108
|
+
.tool('generate-text')
|
|
109
|
+
.input(z.object({ prompt: z.string() }))
|
|
110
|
+
.execute(async function* ({ input }) {
|
|
111
|
+
const words = input.prompt.split(' ');
|
|
112
|
+
for (const word of words) {
|
|
113
|
+
await new Promise(r => setTimeout(r, 100));
|
|
114
|
+
yield word + ' ';
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Batch processing tool
|
|
119
|
+
export const batchTool = aui
|
|
120
|
+
.tool('batch-process')
|
|
121
|
+
.input(z.object({ items: z.array(z.string()) }))
|
|
122
|
+
.execute(async ({ input }) => {
|
|
123
|
+
const results = await Promise.all(
|
|
124
|
+
input.items.map(async item => {
|
|
125
|
+
// Process each item
|
|
126
|
+
return { item, processed: item.toUpperCase() };
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
return { results };
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Error handling
|
|
133
|
+
export const errorHandlingTool = aui
|
|
134
|
+
.tool('safe-fetch')
|
|
135
|
+
.input(z.object({ url: z.string().url() }))
|
|
136
|
+
.execute(async ({ input }) => {
|
|
137
|
+
try {
|
|
138
|
+
const response = await fetch(input.url);
|
|
139
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
140
|
+
return { data: await response.json(), error: null };
|
|
141
|
+
} catch (error) {
|
|
142
|
+
return { data: null, error: error instanceof Error ? error.message : 'Unknown error' };
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
.render(({ data }) => (
|
|
146
|
+
<div>
|
|
147
|
+
{data.error ? (
|
|
148
|
+
<p style={{ color: 'red' }}>Error: {data.error}</p>
|
|
149
|
+
) : (
|
|
150
|
+
<pre>{JSON.stringify(data.data, null, 2)}</pre>
|
|
151
|
+
)}
|
|
152
|
+
</div>
|
|
153
|
+
));
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// AUI Tool Examples
|
|
2
|
+
import aui, { z } from '../index';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
// Simple weather tool - just 2 methods
|
|
6
|
+
export const weatherTool = aui
|
|
7
|
+
.tool('weather')
|
|
8
|
+
.input(z.object({ city: z.string() }))
|
|
9
|
+
.execute(async ({ input }) => ({
|
|
10
|
+
temp: Math.floor(Math.random() * 30) + 60,
|
|
11
|
+
city: input.city,
|
|
12
|
+
condition: 'Sunny',
|
|
13
|
+
humidity: 65
|
|
14
|
+
}))
|
|
15
|
+
.render(({ data }) => (
|
|
16
|
+
<div className="p-4 bg-blue-50 rounded-lg">
|
|
17
|
+
<h3 className="text-lg font-semibold">{data.city}</h3>
|
|
18
|
+
<p className="text-2xl font-bold">{data.temp}°F</p>
|
|
19
|
+
<p className="text-sm text-gray-600">{data.condition} • {data.humidity}% humidity</p>
|
|
20
|
+
</div>
|
|
21
|
+
));
|
|
22
|
+
|
|
23
|
+
// Complex search tool - adds client optimization
|
|
24
|
+
export const searchTool = aui
|
|
25
|
+
.tool('search')
|
|
26
|
+
.input(z.object({ query: z.string() }))
|
|
27
|
+
.execute(async ({ input }) => {
|
|
28
|
+
// Simulate database search
|
|
29
|
+
await new Promise(r => setTimeout(r, 500));
|
|
30
|
+
return {
|
|
31
|
+
results: [
|
|
32
|
+
{ id: 1, title: `Result for "${input.query}"`, score: 0.95, description: 'First matching result with high relevance' },
|
|
33
|
+
{ id: 2, title: `Another result`, score: 0.87, description: 'Secondary result with good relevance' },
|
|
34
|
+
{ id: 3, title: `Related content`, score: 0.72, description: 'Additional related information' }
|
|
35
|
+
],
|
|
36
|
+
total: 3,
|
|
37
|
+
query: input.query
|
|
38
|
+
};
|
|
39
|
+
})
|
|
40
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
41
|
+
// Client-side optimization with caching
|
|
42
|
+
const cacheKey = `search:${input.query}`;
|
|
43
|
+
const cached = ctx.cache.get(cacheKey);
|
|
44
|
+
if (cached) {
|
|
45
|
+
console.log(`Cache hit for query: ${input.query}`);
|
|
46
|
+
return cached;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const result = await ctx.fetch('/api/aui/execute', {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: { 'Content-Type': 'application/json' },
|
|
52
|
+
body: JSON.stringify({ tool: 'search', input })
|
|
53
|
+
}).then(r => r.json());
|
|
54
|
+
|
|
55
|
+
ctx.cache.set(cacheKey, result);
|
|
56
|
+
return result;
|
|
57
|
+
})
|
|
58
|
+
.render(({ data, loading, error }) => {
|
|
59
|
+
if (loading) return <div className="animate-pulse">Searching...</div>;
|
|
60
|
+
if (error) return <div className="text-red-500">Error: {error.message}</div>;
|
|
61
|
+
if (!data) return <div className="text-gray-400">No data</div>;
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="space-y-3">
|
|
65
|
+
<p className="text-sm text-gray-600">Found {data.total} results for "{data.query}"</p>
|
|
66
|
+
{data.results.map((result: any) => (
|
|
67
|
+
<div key={result.id} className="p-3 bg-white border rounded-lg hover:shadow-md transition-shadow">
|
|
68
|
+
<h4 className="font-semibold">{result.title}</h4>
|
|
69
|
+
<p className="text-sm text-gray-600">{result.description}</p>
|
|
70
|
+
<span className="text-xs text-blue-600">Score: {result.score}</span>
|
|
71
|
+
</div>
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Calculator tool with render
|
|
78
|
+
export const calculatorTool = aui
|
|
79
|
+
.tool('calculator')
|
|
80
|
+
.input(z.object({
|
|
81
|
+
a: z.number(),
|
|
82
|
+
b: z.number(),
|
|
83
|
+
op: z.enum(['+', '-', '*', '/'])
|
|
84
|
+
}))
|
|
85
|
+
.execute(({ input }) => {
|
|
86
|
+
const ops = {
|
|
87
|
+
'+': (a: number, b: number) => a + b,
|
|
88
|
+
'-': (a: number, b: number) => a - b,
|
|
89
|
+
'*': (a: number, b: number) => a * b,
|
|
90
|
+
'/': (a: number, b: number) => a / b
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
result: ops[input.op](input.a, input.b),
|
|
94
|
+
expression: `${input.a} ${input.op} ${input.b}`
|
|
95
|
+
};
|
|
96
|
+
})
|
|
97
|
+
.render(({ data }) => (
|
|
98
|
+
<div className="p-3 bg-gray-50 rounded-lg font-mono">
|
|
99
|
+
<span className="text-gray-600">{data?.expression || ''} =</span>
|
|
100
|
+
<span className="ml-2 text-xl font-bold">{data?.result || 0}</span>
|
|
101
|
+
</div>
|
|
102
|
+
));
|
|
103
|
+
|
|
104
|
+
// Data fetcher tool with both server and client execution
|
|
105
|
+
export const dataFetcherTool = aui
|
|
106
|
+
.tool('dataFetcher')
|
|
107
|
+
.input(z.object({
|
|
108
|
+
endpoint: z.string(),
|
|
109
|
+
method: z.enum(['GET', 'POST']).default('GET'),
|
|
110
|
+
body: z.any().optional()
|
|
111
|
+
}))
|
|
112
|
+
.execute(async ({ input }) => {
|
|
113
|
+
// Server-side execution
|
|
114
|
+
const response = await fetch(input.endpoint, {
|
|
115
|
+
method: input.method,
|
|
116
|
+
headers: { 'Content-Type': 'application/json' },
|
|
117
|
+
body: input.body ? JSON.stringify(input.body) : undefined
|
|
118
|
+
});
|
|
119
|
+
return await response.json();
|
|
120
|
+
})
|
|
121
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
122
|
+
// Client-side with request deduplication
|
|
123
|
+
const requestKey = `${input.method}:${input.endpoint}:${JSON.stringify(input.body)}`;
|
|
124
|
+
|
|
125
|
+
// Check if request is already in flight
|
|
126
|
+
const inflight = ctx.cache.get(`inflight:${requestKey}`);
|
|
127
|
+
if (inflight) return inflight;
|
|
128
|
+
|
|
129
|
+
// Check cache
|
|
130
|
+
const cached = ctx.cache.get(requestKey);
|
|
131
|
+
if (cached && input.method === 'GET') return cached;
|
|
132
|
+
|
|
133
|
+
// Make request
|
|
134
|
+
const promise = ctx.fetch(input.endpoint, {
|
|
135
|
+
method: input.method,
|
|
136
|
+
headers: { 'Content-Type': 'application/json' },
|
|
137
|
+
body: input.body ? JSON.stringify(input.body) : undefined
|
|
138
|
+
}).then(r => r.json());
|
|
139
|
+
|
|
140
|
+
// Store inflight request
|
|
141
|
+
ctx.cache.set(`inflight:${requestKey}`, promise);
|
|
142
|
+
|
|
143
|
+
const result = await promise;
|
|
144
|
+
|
|
145
|
+
// Clear inflight and cache result
|
|
146
|
+
ctx.cache.delete(`inflight:${requestKey}`);
|
|
147
|
+
if (input.method === 'GET') {
|
|
148
|
+
ctx.cache.set(requestKey, result);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return result;
|
|
152
|
+
})
|
|
153
|
+
.render(({ data, loading }) => {
|
|
154
|
+
if (loading) return <div className="animate-pulse">Fetching data...</div>;
|
|
155
|
+
return (
|
|
156
|
+
<pre className="p-3 bg-gray-900 text-green-400 rounded-lg overflow-auto">
|
|
157
|
+
{data ? JSON.stringify(data, null, 2) : 'No data'}
|
|
158
|
+
</pre>
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Export advanced tools
|
|
163
|
+
export * from '../tools/advanced-examples';
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import aui from '../index';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
// Simple tool - just 2 methods
|
|
8
|
+
const weatherTool = aui
|
|
9
|
+
.tool('weather')
|
|
10
|
+
.input(z.object({ city: z.string() }))
|
|
11
|
+
.execute(async ({ input }) => ({ temp: 72, city: input.city }))
|
|
12
|
+
.render(({ data }) => <div>{data.city}: {data.temp}°</div>);
|
|
13
|
+
|
|
14
|
+
// Complex tool - adds client optimization
|
|
15
|
+
const searchTool = aui
|
|
16
|
+
.tool('search')
|
|
17
|
+
.input(z.object({ query: z.string() }))
|
|
18
|
+
.execute(async ({ input }) => {
|
|
19
|
+
// Server-side: database search
|
|
20
|
+
return { results: [`Result for ${input.query}`] };
|
|
21
|
+
})
|
|
22
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
23
|
+
// Client-side: cache-first strategy
|
|
24
|
+
const cached = ctx.cache.get(input.query);
|
|
25
|
+
if (cached) return cached;
|
|
26
|
+
|
|
27
|
+
const result = await ctx.fetch('/api/tools/search', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
body: JSON.stringify(input)
|
|
30
|
+
}).then(r => r.json());
|
|
31
|
+
|
|
32
|
+
ctx.cache.set(input.query, result);
|
|
33
|
+
return result;
|
|
34
|
+
})
|
|
35
|
+
.render(({ data }) => (
|
|
36
|
+
<div>
|
|
37
|
+
<h3>Search Results</h3>
|
|
38
|
+
<ul>
|
|
39
|
+
{data.results?.map((item: string, i: number) => (
|
|
40
|
+
<li key={i}>{item}</li>
|
|
41
|
+
))}
|
|
42
|
+
</ul>
|
|
43
|
+
</div>
|
|
44
|
+
));
|
|
45
|
+
|
|
46
|
+
// Export tools for use in components
|
|
47
|
+
export { weatherTool, searchTool };
|
|
48
|
+
|
|
49
|
+
// Demo component
|
|
50
|
+
export function QuickDemo() {
|
|
51
|
+
const [weatherData, setWeatherData] = React.useState<any>(null);
|
|
52
|
+
const [searchData, setSearchData] = React.useState<any>(null);
|
|
53
|
+
|
|
54
|
+
const handleWeather = async () => {
|
|
55
|
+
const data = await weatherTool.run({ city: 'San Francisco' });
|
|
56
|
+
setWeatherData(data);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleSearch = async () => {
|
|
60
|
+
const data = await searchTool.run({ query: 'AI tools' });
|
|
61
|
+
setSearchData(data);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className="p-4 space-y-4">
|
|
66
|
+
<h2 className="text-2xl font-bold">AUI Quick Demo</h2>
|
|
67
|
+
|
|
68
|
+
<div className="space-y-2">
|
|
69
|
+
<button
|
|
70
|
+
onClick={handleWeather}
|
|
71
|
+
className="px-4 py-2 bg-blue-500 text-white rounded"
|
|
72
|
+
>
|
|
73
|
+
Get Weather
|
|
74
|
+
</button>
|
|
75
|
+
{weatherData && weatherTool.renderer &&
|
|
76
|
+
weatherTool.renderer({ data: weatherData })}
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div className="space-y-2">
|
|
80
|
+
<button
|
|
81
|
+
onClick={handleSearch}
|
|
82
|
+
className="px-4 py-2 bg-green-500 text-white rounded"
|
|
83
|
+
>
|
|
84
|
+
Search
|
|
85
|
+
</button>
|
|
86
|
+
{searchData && searchTool.renderer &&
|
|
87
|
+
searchTool.renderer({ data: searchData })}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import aui from '../index';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
// Simple tool - just 2 methods (execute + render)
|
|
6
|
+
const simpleTool = aui
|
|
7
|
+
.tool('weather')
|
|
8
|
+
.input(z.object({ city: z.string() }))
|
|
9
|
+
.execute(async ({ input }) => ({ temp: 72, city: input.city }))
|
|
10
|
+
.render(({ data }) => <div>{data.city}: {data.temp}°</div>);
|
|
11
|
+
|
|
12
|
+
// Complex tool - adds client optimization
|
|
13
|
+
const complexTool = aui
|
|
14
|
+
.tool('search')
|
|
15
|
+
.input(z.object({ query: z.string() }))
|
|
16
|
+
.execute(async ({ input }) => {
|
|
17
|
+
// Server-side database search
|
|
18
|
+
const response = await fetch('/api/db/search', {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
body: JSON.stringify(input)
|
|
21
|
+
});
|
|
22
|
+
return response.json();
|
|
23
|
+
})
|
|
24
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
25
|
+
// Only when you need caching, offline, etc.
|
|
26
|
+
const cached = ctx.cache.get(input.query);
|
|
27
|
+
return cached || ctx.fetch('/api/tools/search', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: { 'Content-Type': 'application/json' },
|
|
30
|
+
body: JSON.stringify(input)
|
|
31
|
+
}).then(r => r.json());
|
|
32
|
+
})
|
|
33
|
+
.render(({ data }) => (
|
|
34
|
+
<div className="space-y-2">
|
|
35
|
+
{data.map((item: any) => (
|
|
36
|
+
<div key={item.id} className="p-2 border rounded">
|
|
37
|
+
{item.title}
|
|
38
|
+
</div>
|
|
39
|
+
))}
|
|
40
|
+
</div>
|
|
41
|
+
));
|
|
42
|
+
|
|
43
|
+
// Tool with middleware for auth/logging
|
|
44
|
+
const protectedTool = aui
|
|
45
|
+
.tool('admin-action')
|
|
46
|
+
.input(z.object({ action: z.string() }))
|
|
47
|
+
.middleware(async ({ input, ctx, next }) => {
|
|
48
|
+
if (!ctx?.user) throw new Error('Auth required');
|
|
49
|
+
console.log(`User ${ctx.user.id} performing ${input.action}`);
|
|
50
|
+
return next();
|
|
51
|
+
})
|
|
52
|
+
.execute(async ({ input }) => ({
|
|
53
|
+
success: true,
|
|
54
|
+
action: input.action
|
|
55
|
+
}))
|
|
56
|
+
.render(({ data }) => (
|
|
57
|
+
<div className="text-green-600">
|
|
58
|
+
✓ {data.action} completed
|
|
59
|
+
</div>
|
|
60
|
+
));
|
|
61
|
+
|
|
62
|
+
// Minimal tool - execute only (no render needed)
|
|
63
|
+
const apiTool = aui
|
|
64
|
+
.tool('api-call')
|
|
65
|
+
.input(z.object({ endpoint: z.string() }))
|
|
66
|
+
.execute(async ({ input }) => {
|
|
67
|
+
const res = await fetch(input.endpoint);
|
|
68
|
+
return res.json();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
export { simpleTool, complexTool, protectedTool, apiTool };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import aui from '../index';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
// Simple weather tool - just 2 methods
|
|
6
|
+
export const weatherTool = aui
|
|
7
|
+
.tool('weather')
|
|
8
|
+
.input(z.object({ city: z.string() }))
|
|
9
|
+
.execute(async ({ input }) => ({ temp: 72, city: input.city }))
|
|
10
|
+
.render(({ data }) => <div>{data.city}: {data.temp}°</div>);
|
|
11
|
+
|
|
12
|
+
// Complex search tool - adds client optimization
|
|
13
|
+
export const searchTool = aui
|
|
14
|
+
.tool('search')
|
|
15
|
+
.input(z.object({ query: z.string() }))
|
|
16
|
+
.execute(async ({ input }) => {
|
|
17
|
+
// Server-side: Mock database search
|
|
18
|
+
const results = [
|
|
19
|
+
{ id: 1, title: `Result for "${input.query}"`, content: 'Lorem ipsum...' },
|
|
20
|
+
{ id: 2, title: `Another match for "${input.query}"`, content: 'Dolor sit amet...' },
|
|
21
|
+
];
|
|
22
|
+
return { query: input.query, results };
|
|
23
|
+
})
|
|
24
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
25
|
+
// Client-side: Check cache first, then fetch
|
|
26
|
+
const cacheKey = `search:${input.query}`;
|
|
27
|
+
const cached = ctx.cache.get(cacheKey);
|
|
28
|
+
if (cached) return cached;
|
|
29
|
+
|
|
30
|
+
const response = await ctx.fetch('/api/aui/execute', {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: { 'Content-Type': 'application/json' },
|
|
33
|
+
body: JSON.stringify({ tool: 'search', input })
|
|
34
|
+
});
|
|
35
|
+
const data = await response.json();
|
|
36
|
+
ctx.cache.set(cacheKey, data);
|
|
37
|
+
return data;
|
|
38
|
+
})
|
|
39
|
+
.render(({ data }) => (
|
|
40
|
+
<div className="search-results">
|
|
41
|
+
<h3>Search Results for "{data.query}"</h3>
|
|
42
|
+
{data.results.map((result: any) => (
|
|
43
|
+
<div key={result.id} className="result-item">
|
|
44
|
+
<h4>{result.title}</h4>
|
|
45
|
+
<p>{result.content}</p>
|
|
46
|
+
</div>
|
|
47
|
+
))}
|
|
48
|
+
</div>
|
|
49
|
+
));
|
|
50
|
+
|
|
51
|
+
// Calculator tool - demonstrates middleware
|
|
52
|
+
export const calculatorTool = aui
|
|
53
|
+
.tool('calculator')
|
|
54
|
+
.input(z.object({
|
|
55
|
+
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
|
|
56
|
+
a: z.number(),
|
|
57
|
+
b: z.number()
|
|
58
|
+
}))
|
|
59
|
+
.middleware(async ({ input, next }) => {
|
|
60
|
+
console.log(`Calculating: ${input.a} ${input.operation} ${input.b}`);
|
|
61
|
+
const result = await next();
|
|
62
|
+
console.log(`Result: ${result}`);
|
|
63
|
+
return result;
|
|
64
|
+
})
|
|
65
|
+
.execute(({ input }) => {
|
|
66
|
+
const { operation, a, b } = input;
|
|
67
|
+
switch (operation) {
|
|
68
|
+
case 'add': return a + b;
|
|
69
|
+
case 'subtract': return a - b;
|
|
70
|
+
case 'multiply': return a * b;
|
|
71
|
+
case 'divide': return b !== 0 ? a / b : NaN;
|
|
72
|
+
default: throw new Error(`Unknown operation: ${operation}`);
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
.render(({ data, input }) => (
|
|
76
|
+
<div className="calculator-result">
|
|
77
|
+
{input?.a} {input?.operation} {input?.b} = {data}
|
|
78
|
+
</div>
|
|
79
|
+
));
|
|
80
|
+
|
|
81
|
+
// Form submission tool
|
|
82
|
+
export const formTool = aui
|
|
83
|
+
.tool('submitForm')
|
|
84
|
+
.input(z.object({
|
|
85
|
+
name: z.string().min(1),
|
|
86
|
+
email: z.string().email(),
|
|
87
|
+
message: z.string().optional()
|
|
88
|
+
}))
|
|
89
|
+
.execute(async ({ input }) => {
|
|
90
|
+
// Simulate form submission
|
|
91
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
92
|
+
return { success: true, id: Math.random().toString(36).substr(2, 9), ...input };
|
|
93
|
+
})
|
|
94
|
+
.render(({ data, loading, error }) => {
|
|
95
|
+
if (loading) return <div>Submitting...</div>;
|
|
96
|
+
if (error) return <div className="error">Error: {error.message}</div>;
|
|
97
|
+
return (
|
|
98
|
+
<div className="form-success">
|
|
99
|
+
✅ Form submitted successfully!
|
|
100
|
+
<p>Submission ID: {data.id}</p>
|
|
101
|
+
<p>Name: {data.name}</p>
|
|
102
|
+
<p>Email: {data.email}</p>
|
|
103
|
+
{data.message && <p>Message: {data.message}</p>}
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Navigation tool - AI can control page navigation
|
|
109
|
+
export const navigationTool = aui
|
|
110
|
+
.tool('navigate')
|
|
111
|
+
.input(z.object({
|
|
112
|
+
path: z.string(),
|
|
113
|
+
params: z.record(z.string()).optional()
|
|
114
|
+
}))
|
|
115
|
+
.clientExecute(async ({ input }) => {
|
|
116
|
+
const url = new URL(input.path, window.location.origin);
|
|
117
|
+
if (input.params) {
|
|
118
|
+
Object.entries(input.params).forEach(([key, value]) => {
|
|
119
|
+
url.searchParams.set(key, value);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
window.history.pushState({}, '', url.toString());
|
|
123
|
+
return { navigated: true, url: url.toString() };
|
|
124
|
+
})
|
|
125
|
+
.render(({ data }) => (
|
|
126
|
+
<div>Navigated to: {data.url}</div>
|
|
127
|
+
));
|
|
128
|
+
|
|
129
|
+
// State management tool - AI can control application state
|
|
130
|
+
export const stateTool = aui
|
|
131
|
+
.tool('setState')
|
|
132
|
+
.input(z.object({
|
|
133
|
+
key: z.string(),
|
|
134
|
+
value: z.any(),
|
|
135
|
+
action: z.enum(['set', 'get', 'delete']).default('set')
|
|
136
|
+
}))
|
|
137
|
+
.clientExecute(({ input, ctx }) => {
|
|
138
|
+
const stateMap = ctx.cache.get('appState') || new Map();
|
|
139
|
+
|
|
140
|
+
switch (input.action) {
|
|
141
|
+
case 'get':
|
|
142
|
+
return { key: input.key, value: stateMap.get(input.key) };
|
|
143
|
+
case 'delete':
|
|
144
|
+
stateMap.delete(input.key);
|
|
145
|
+
ctx.cache.set('appState', stateMap);
|
|
146
|
+
return { key: input.key, deleted: true };
|
|
147
|
+
case 'set':
|
|
148
|
+
default:
|
|
149
|
+
stateMap.set(input.key, input.value);
|
|
150
|
+
ctx.cache.set('appState', stateMap);
|
|
151
|
+
return { key: input.key, value: input.value, updated: true };
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
.render(({ data }) => (
|
|
155
|
+
<div className="state-update">
|
|
156
|
+
{data.deleted ? `Deleted: ${data.key}` :
|
|
157
|
+
data.updated ? `Updated: ${data.key} = ${JSON.stringify(data.value)}` :
|
|
158
|
+
`Retrieved: ${data.key} = ${JSON.stringify(data.value)}`}
|
|
159
|
+
</div>
|
|
160
|
+
));
|