@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,208 @@
|
|
|
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 (exactly as user requested)
|
|
8
|
+
const simpleTool = 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 (exactly as user requested)
|
|
15
|
+
const complexTool = aui
|
|
16
|
+
.tool('search')
|
|
17
|
+
.input(z.object({ query: z.string() }))
|
|
18
|
+
.execute(async ({ input }) => {
|
|
19
|
+
// Simulated database search
|
|
20
|
+
const results = await Promise.resolve([
|
|
21
|
+
`Result 1 for ${input.query}`,
|
|
22
|
+
`Result 2 for ${input.query}`,
|
|
23
|
+
`Result 3 for ${input.query}`
|
|
24
|
+
]);
|
|
25
|
+
return { results, query: input.query };
|
|
26
|
+
})
|
|
27
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
28
|
+
// Only when you need caching, offline, etc.
|
|
29
|
+
const cached = ctx.cache.get(input.query);
|
|
30
|
+
if (cached) {
|
|
31
|
+
console.log('Using cached result for:', input.query);
|
|
32
|
+
return cached;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const result = await ctx.fetch('/api/tools/search', {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: { 'Content-Type': 'application/json' },
|
|
38
|
+
body: JSON.stringify(input)
|
|
39
|
+
}).then(r => r.json()).catch(() => {
|
|
40
|
+
// Fallback for demo
|
|
41
|
+
return {
|
|
42
|
+
results: [`Client result for ${input.query}`],
|
|
43
|
+
query: input.query
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
ctx.cache.set(input.query, result);
|
|
48
|
+
return result;
|
|
49
|
+
})
|
|
50
|
+
.render(({ data }) => (
|
|
51
|
+
<div className="space-y-2">
|
|
52
|
+
<h3 className="font-semibold">Search Results for "{data.query}"</h3>
|
|
53
|
+
<ul className="list-disc pl-5">
|
|
54
|
+
{data.results.map((result, i) => (
|
|
55
|
+
<li key={i}>{result}</li>
|
|
56
|
+
))}
|
|
57
|
+
</ul>
|
|
58
|
+
</div>
|
|
59
|
+
));
|
|
60
|
+
|
|
61
|
+
// Additional examples showing the power of the API
|
|
62
|
+
|
|
63
|
+
// Tool with middleware for auth/logging
|
|
64
|
+
const protectedTool = aui
|
|
65
|
+
.tool('protected')
|
|
66
|
+
.input(z.object({ action: z.string() }))
|
|
67
|
+
.middleware(async ({ input, ctx, next }) => {
|
|
68
|
+
console.log('Logging action:', input.action);
|
|
69
|
+
if (!ctx.user) {
|
|
70
|
+
throw new Error('Authentication required');
|
|
71
|
+
}
|
|
72
|
+
return next();
|
|
73
|
+
})
|
|
74
|
+
.execute(async ({ input, ctx }) => ({
|
|
75
|
+
result: `Executed ${input.action} for user ${ctx?.user?.name || 'unknown'}`
|
|
76
|
+
}))
|
|
77
|
+
.render(({ data }) => <div className="text-green-600">{data.result}</div>);
|
|
78
|
+
|
|
79
|
+
// Tool with tags and description for AI discovery
|
|
80
|
+
const aiReadyTool = aui
|
|
81
|
+
.tool('database')
|
|
82
|
+
.input(z.object({
|
|
83
|
+
table: z.string(),
|
|
84
|
+
operation: z.enum(['read', 'write', 'delete'])
|
|
85
|
+
}))
|
|
86
|
+
.describe('Database operations tool for CRUD operations')
|
|
87
|
+
.tag('database', 'crud', 'ai-controlled')
|
|
88
|
+
.execute(async ({ input }) => ({
|
|
89
|
+
success: true,
|
|
90
|
+
operation: input.operation,
|
|
91
|
+
table: input.table,
|
|
92
|
+
result: `${input.operation} operation on ${input.table} completed`
|
|
93
|
+
}))
|
|
94
|
+
.render(({ data }) => (
|
|
95
|
+
<div className={`p-2 rounded ${data.success ? 'bg-green-100' : 'bg-red-100'}`}>
|
|
96
|
+
{data.result}
|
|
97
|
+
</div>
|
|
98
|
+
));
|
|
99
|
+
|
|
100
|
+
// Export all tools
|
|
101
|
+
export { simpleTool, complexTool, protectedTool, aiReadyTool };
|
|
102
|
+
|
|
103
|
+
// Demo component showing usage
|
|
104
|
+
export function UserAPIDemo() {
|
|
105
|
+
const [results, setResults] = React.useState<Record<string, any>>({});
|
|
106
|
+
const [loading, setLoading] = React.useState<Record<string, boolean>>({});
|
|
107
|
+
|
|
108
|
+
const runTool = async (toolName: string, tool: any, input: any) => {
|
|
109
|
+
setLoading(prev => ({ ...prev, [toolName]: true }));
|
|
110
|
+
try {
|
|
111
|
+
const result = await tool.run(input, aui.createContext({
|
|
112
|
+
user: { name: 'Demo User' }
|
|
113
|
+
}));
|
|
114
|
+
setResults(prev => ({ ...prev, [toolName]: result }));
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(`Error running ${toolName}:`, error);
|
|
117
|
+
setResults(prev => ({ ...prev, [toolName]: { error: error instanceof Error ? error.message : 'Unknown error' } }));
|
|
118
|
+
} finally {
|
|
119
|
+
setLoading(prev => ({ ...prev, [toolName]: false }));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<div className="p-6 space-y-6 max-w-4xl mx-auto">
|
|
125
|
+
<h1 className="text-3xl font-bold">AUI User API Demo</h1>
|
|
126
|
+
|
|
127
|
+
<div className="space-y-4">
|
|
128
|
+
<div className="border rounded-lg p-4">
|
|
129
|
+
<h2 className="text-xl font-semibold mb-2">Simple Tool</h2>
|
|
130
|
+
<p className="text-gray-600 mb-3">Just input → execute → render</p>
|
|
131
|
+
<button
|
|
132
|
+
onClick={() => runTool('weather', simpleTool, { city: 'San Francisco' })}
|
|
133
|
+
disabled={loading.weather}
|
|
134
|
+
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-50"
|
|
135
|
+
>
|
|
136
|
+
{loading.weather ? 'Loading...' : 'Get Weather'}
|
|
137
|
+
</button>
|
|
138
|
+
{results.weather && simpleTool.renderer && (
|
|
139
|
+
<div className="mt-3">
|
|
140
|
+
{simpleTool.renderer({ data: results.weather })}
|
|
141
|
+
</div>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div className="border rounded-lg p-4">
|
|
146
|
+
<h2 className="text-xl font-semibold mb-2">Complex Tool</h2>
|
|
147
|
+
<p className="text-gray-600 mb-3">With client-side caching and optimization</p>
|
|
148
|
+
<button
|
|
149
|
+
onClick={() => runTool('search', complexTool, { query: 'AI tools' })}
|
|
150
|
+
disabled={loading.search}
|
|
151
|
+
className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 disabled:opacity-50"
|
|
152
|
+
>
|
|
153
|
+
{loading.search ? 'Searching...' : 'Search'}
|
|
154
|
+
</button>
|
|
155
|
+
{results.search && complexTool.renderer && (
|
|
156
|
+
<div className="mt-3">
|
|
157
|
+
{complexTool.renderer({ data: results.search })}
|
|
158
|
+
</div>
|
|
159
|
+
)}
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div className="border rounded-lg p-4">
|
|
163
|
+
<h2 className="text-xl font-semibold mb-2">Protected Tool</h2>
|
|
164
|
+
<p className="text-gray-600 mb-3">With middleware for auth/logging</p>
|
|
165
|
+
<button
|
|
166
|
+
onClick={() => runTool('protected', protectedTool, { action: 'update-profile' })}
|
|
167
|
+
disabled={loading.protected}
|
|
168
|
+
className="px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600 disabled:opacity-50"
|
|
169
|
+
>
|
|
170
|
+
{loading.protected ? 'Executing...' : 'Execute Protected Action'}
|
|
171
|
+
</button>
|
|
172
|
+
{results.protected && protectedTool.renderer && (
|
|
173
|
+
<div className="mt-3">
|
|
174
|
+
{protectedTool.renderer({ data: results.protected })}
|
|
175
|
+
</div>
|
|
176
|
+
)}
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<div className="border rounded-lg p-4">
|
|
180
|
+
<h2 className="text-xl font-semibold mb-2">AI-Ready Tool</h2>
|
|
181
|
+
<p className="text-gray-600 mb-3">With tags and description for AI discovery</p>
|
|
182
|
+
<button
|
|
183
|
+
onClick={() => runTool('database', aiReadyTool, {
|
|
184
|
+
table: 'users',
|
|
185
|
+
operation: 'read'
|
|
186
|
+
})}
|
|
187
|
+
disabled={loading.database}
|
|
188
|
+
className="px-4 py-2 bg-indigo-500 text-white rounded hover:bg-indigo-600 disabled:opacity-50"
|
|
189
|
+
>
|
|
190
|
+
{loading.database ? 'Processing...' : 'Run Database Operation'}
|
|
191
|
+
</button>
|
|
192
|
+
{results.database && aiReadyTool.renderer && (
|
|
193
|
+
<div className="mt-3">
|
|
194
|
+
{aiReadyTool.renderer({ data: results.database })}
|
|
195
|
+
</div>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<div className="border-t pt-4">
|
|
201
|
+
<h3 className="text-lg font-semibold mb-2">Tool Discovery</h3>
|
|
202
|
+
<p className="text-gray-600">
|
|
203
|
+
Tools registered: {aui.getToolNames().join(', ')}
|
|
204
|
+
</p>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import aui from '../index';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
// Simple tool - just 2 methods
|
|
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
|
+
// In real app, this would be: db.search(input.query)
|
|
19
|
+
return [
|
|
20
|
+
{ id: 1, title: `Result for ${input.query}` },
|
|
21
|
+
{ id: 2, title: `Another result for ${input.query}` }
|
|
22
|
+
];
|
|
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
|
+
<h3 className="font-semibold">Search Results:</h3>
|
|
36
|
+
{data.map((item: any) => (
|
|
37
|
+
<div key={item.id} className="p-2 border rounded">
|
|
38
|
+
{item.title}
|
|
39
|
+
</div>
|
|
40
|
+
))}
|
|
41
|
+
</div>
|
|
42
|
+
));
|
|
43
|
+
|
|
44
|
+
// AI Control Examples - Frontend UI manipulation
|
|
45
|
+
const uiControlTool = aui
|
|
46
|
+
.tool('ui-control')
|
|
47
|
+
.input(z.object({
|
|
48
|
+
action: z.enum(['show', 'hide', 'toggle']),
|
|
49
|
+
element: z.string()
|
|
50
|
+
}))
|
|
51
|
+
.clientExecute(async ({ input }) => {
|
|
52
|
+
const element = document.querySelector(input.element);
|
|
53
|
+
if (!element) throw new Error(`Element ${input.element} not found`);
|
|
54
|
+
|
|
55
|
+
switch (input.action) {
|
|
56
|
+
case 'show':
|
|
57
|
+
(element as HTMLElement).style.display = 'block';
|
|
58
|
+
break;
|
|
59
|
+
case 'hide':
|
|
60
|
+
(element as HTMLElement).style.display = 'none';
|
|
61
|
+
break;
|
|
62
|
+
case 'toggle':
|
|
63
|
+
const el = element as HTMLElement;
|
|
64
|
+
el.style.display = el.style.display === 'none' ? 'block' : 'none';
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { success: true, action: input.action, element: input.element };
|
|
69
|
+
})
|
|
70
|
+
.render(({ data }) => (
|
|
71
|
+
<div className="text-green-600">
|
|
72
|
+
✓ {data.action} {data.element}
|
|
73
|
+
</div>
|
|
74
|
+
));
|
|
75
|
+
|
|
76
|
+
// Backend control - Database operations
|
|
77
|
+
const databaseTool = aui
|
|
78
|
+
.tool('database')
|
|
79
|
+
.input(z.object({
|
|
80
|
+
operation: z.enum(['create', 'read', 'update', 'delete']),
|
|
81
|
+
table: z.string(),
|
|
82
|
+
data: z.any().optional()
|
|
83
|
+
}))
|
|
84
|
+
.execute(async ({ input }) => {
|
|
85
|
+
// This would connect to your actual database
|
|
86
|
+
// For demo, just return mock response
|
|
87
|
+
switch (input.operation) {
|
|
88
|
+
case 'create':
|
|
89
|
+
return { id: Math.random(), ...input.data };
|
|
90
|
+
case 'read':
|
|
91
|
+
return { id: 1, name: 'Example', table: input.table };
|
|
92
|
+
case 'update':
|
|
93
|
+
return { success: true, updated: input.data };
|
|
94
|
+
case 'delete':
|
|
95
|
+
return { success: true, deleted: input.data?.id };
|
|
96
|
+
default:
|
|
97
|
+
throw new Error('Unknown operation');
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
.render(({ data }) => (
|
|
101
|
+
<pre className="bg-gray-100 p-2 rounded">
|
|
102
|
+
{JSON.stringify(data, null, 2)}
|
|
103
|
+
</pre>
|
|
104
|
+
));
|
|
105
|
+
|
|
106
|
+
// Form generation - AI can create forms dynamically
|
|
107
|
+
const formGeneratorTool = aui
|
|
108
|
+
.tool('form-generator')
|
|
109
|
+
.input(z.object({
|
|
110
|
+
fields: z.array(z.object({
|
|
111
|
+
name: z.string(),
|
|
112
|
+
type: z.enum(['text', 'number', 'email', 'select']),
|
|
113
|
+
label: z.string(),
|
|
114
|
+
required: z.boolean().optional(),
|
|
115
|
+
options: z.array(z.string()).optional()
|
|
116
|
+
}))
|
|
117
|
+
}))
|
|
118
|
+
.execute(async ({ input }) => input.fields)
|
|
119
|
+
.render(({ data }) => (
|
|
120
|
+
<form className="space-y-4">
|
|
121
|
+
{data.map(field => (
|
|
122
|
+
<div key={field.name}>
|
|
123
|
+
<label className="block text-sm font-medium mb-1">
|
|
124
|
+
{field.label}
|
|
125
|
+
{field.required && <span className="text-red-500">*</span>}
|
|
126
|
+
</label>
|
|
127
|
+
{field.type === 'select' ? (
|
|
128
|
+
<select className="w-full p-2 border rounded">
|
|
129
|
+
{field.options?.map(opt => (
|
|
130
|
+
<option key={opt} value={opt}>{opt}</option>
|
|
131
|
+
))}
|
|
132
|
+
</select>
|
|
133
|
+
) : (
|
|
134
|
+
<input
|
|
135
|
+
type={field.type}
|
|
136
|
+
name={field.name}
|
|
137
|
+
required={field.required}
|
|
138
|
+
className="w-full p-2 border rounded"
|
|
139
|
+
/>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
))}
|
|
143
|
+
<button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
|
|
144
|
+
Submit
|
|
145
|
+
</button>
|
|
146
|
+
</form>
|
|
147
|
+
));
|
|
148
|
+
|
|
149
|
+
// Export all tools for use
|
|
150
|
+
export {
|
|
151
|
+
simpleTool,
|
|
152
|
+
complexTool,
|
|
153
|
+
uiControlTool,
|
|
154
|
+
databaseTool,
|
|
155
|
+
formGeneratorTool
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// Example of using tools programmatically
|
|
159
|
+
export async function exampleUsage() {
|
|
160
|
+
// Execute a tool
|
|
161
|
+
const weatherResult = await simpleTool.run({ city: 'New York' });
|
|
162
|
+
console.log(weatherResult); // { temp: 72, city: 'New York' }
|
|
163
|
+
|
|
164
|
+
// Execute with context
|
|
165
|
+
const searchResult = await complexTool.run(
|
|
166
|
+
{ query: 'typescript' },
|
|
167
|
+
{
|
|
168
|
+
cache: new Map(),
|
|
169
|
+
fetch: globalThis.fetch,
|
|
170
|
+
user: { id: 1, name: 'User' }
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
console.log(searchResult);
|
|
174
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import aui from '../index';
|
|
4
|
+
|
|
5
|
+
// Simple tool - just 2 methods (input, execute, render)
|
|
6
|
+
export const weatherTool = aui
|
|
7
|
+
.tool('weather')
|
|
8
|
+
.input(z.object({ city: z.string() }))
|
|
9
|
+
.execute(async ({ input }) => ({
|
|
10
|
+
temp: 72,
|
|
11
|
+
city: input.city,
|
|
12
|
+
conditions: 'Sunny',
|
|
13
|
+
humidity: 65
|
|
14
|
+
}))
|
|
15
|
+
.render(({ data }) => (
|
|
16
|
+
<div className="p-4 bg-blue-50 rounded-lg">
|
|
17
|
+
<h3 className="font-bold text-lg">{data.city}</h3>
|
|
18
|
+
<p className="text-2xl">{data.temp}°F</p>
|
|
19
|
+
<p className="text-gray-600">{data.conditions}</p>
|
|
20
|
+
<p className="text-sm">Humidity: {data.humidity}%</p>
|
|
21
|
+
</div>
|
|
22
|
+
));
|
|
23
|
+
|
|
24
|
+
// Mock database search function
|
|
25
|
+
const mockDbSearch = async (query: string) => {
|
|
26
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
27
|
+
return [
|
|
28
|
+
{ id: 1, title: `Result for "${query}"`, score: 0.95 },
|
|
29
|
+
{ id: 2, title: `Another match for "${query}"`, score: 0.87 },
|
|
30
|
+
{ id: 3, title: `Related to "${query}"`, score: 0.72 }
|
|
31
|
+
];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Complex tool - adds client optimization
|
|
35
|
+
export const searchTool = aui
|
|
36
|
+
.tool('search')
|
|
37
|
+
.input(z.object({ query: z.string() }))
|
|
38
|
+
.execute(async ({ input }) => mockDbSearch(input.query))
|
|
39
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
40
|
+
// Only when you need caching, offline, etc.
|
|
41
|
+
const cacheKey = `search:${input.query}`;
|
|
42
|
+
const cached = ctx.cache.get(cacheKey);
|
|
43
|
+
|
|
44
|
+
if (cached) {
|
|
45
|
+
console.log(`Cache hit for query: ${input.query}`);
|
|
46
|
+
return cached;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(`Cache miss for query: ${input.query}, fetching...`);
|
|
50
|
+
const results = await ctx.fetch('/api/tools/search', {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: { 'Content-Type': 'application/json' },
|
|
53
|
+
body: JSON.stringify(input)
|
|
54
|
+
}).then(res => res.json());
|
|
55
|
+
|
|
56
|
+
ctx.cache.set(cacheKey, results);
|
|
57
|
+
return results;
|
|
58
|
+
})
|
|
59
|
+
.render(({ data }) => (
|
|
60
|
+
<div className="p-4 bg-gray-50 rounded-lg">
|
|
61
|
+
<h3 className="font-bold mb-3">Search Results</h3>
|
|
62
|
+
<ul className="space-y-2">
|
|
63
|
+
{data.map((result: any) => (
|
|
64
|
+
<li key={result.id} className="bg-white p-2 rounded shadow-sm">
|
|
65
|
+
<div className="font-medium">{result.title}</div>
|
|
66
|
+
<div className="text-sm text-gray-500">Score: {result.score}</div>
|
|
67
|
+
</li>
|
|
68
|
+
))}
|
|
69
|
+
</ul>
|
|
70
|
+
</div>
|
|
71
|
+
));
|
|
72
|
+
|
|
73
|
+
// Demo component showing both tools in action
|
|
74
|
+
export function WeatherSearchDemo() {
|
|
75
|
+
const [weatherData, setWeatherData] = React.useState<any>(null);
|
|
76
|
+
const [searchResults, setSearchResults] = React.useState<any>(null);
|
|
77
|
+
|
|
78
|
+
const handleWeatherSearch = async () => {
|
|
79
|
+
const result = await weatherTool.run({ city: 'San Francisco' });
|
|
80
|
+
setWeatherData(result);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const handleSearch = async () => {
|
|
84
|
+
const results = await searchTool.run({ query: 'TypeScript patterns' });
|
|
85
|
+
setSearchResults(results);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className="p-8 space-y-6">
|
|
90
|
+
<div>
|
|
91
|
+
<h2 className="text-2xl font-bold mb-4">AUI Tool Examples</h2>
|
|
92
|
+
|
|
93
|
+
<div className="space-y-4">
|
|
94
|
+
<div>
|
|
95
|
+
<button
|
|
96
|
+
onClick={handleWeatherSearch}
|
|
97
|
+
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
|
98
|
+
>
|
|
99
|
+
Get Weather for San Francisco
|
|
100
|
+
</button>
|
|
101
|
+
{weatherData && weatherTool.renderer &&
|
|
102
|
+
weatherTool.renderer({ data: weatherData })}
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div>
|
|
106
|
+
<button
|
|
107
|
+
onClick={handleSearch}
|
|
108
|
+
className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
|
|
109
|
+
>
|
|
110
|
+
Search for TypeScript patterns
|
|
111
|
+
</button>
|
|
112
|
+
{searchResults && searchTool.renderer &&
|
|
113
|
+
searchTool.renderer({ data: searchResults })}
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|