@reminix/runtime 0.0.6 → 0.0.8
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/README.md +355 -171
- package/dist/agent-adapter.d.ts +30 -0
- package/dist/agent-adapter.d.ts.map +1 -0
- package/dist/{adapter.js → agent-adapter.js} +8 -18
- package/dist/agent-adapter.js.map +1 -0
- package/dist/agent.d.ts +149 -80
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +279 -150
- package/dist/agent.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +20 -40
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +8 -13
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/adapter.d.ts +0 -35
- package/dist/adapter.d.ts.map +0 -1
- package/dist/adapter.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @reminix/runtime
|
|
2
2
|
|
|
3
|
-
Core runtime package for serving AI agents and tools via REST APIs. Provides the `
|
|
3
|
+
Core runtime package for serving AI agents and tools via REST APIs. Provides the `agent()`, `chatAgent()`, and `tool()` factory functions for building and serving AI agents.
|
|
4
4
|
|
|
5
5
|
Built on [Hono](https://hono.dev) for portability across Node.js, Deno, Bun, and edge runtimes.
|
|
6
6
|
|
|
@@ -15,30 +15,27 @@ npm install @reminix/runtime
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import {
|
|
18
|
+
import { agent, chatAgent, serve } from '@reminix/runtime';
|
|
19
19
|
|
|
20
|
-
// Create an agent
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
// Create an agent for task-oriented operations
|
|
21
|
+
const calculator = agent('calculator', {
|
|
22
|
+
description: 'Add two numbers',
|
|
23
|
+
parameters: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: { a: { type: 'number' }, b: { type: 'number' } },
|
|
26
|
+
required: ['a', 'b'],
|
|
27
|
+
},
|
|
28
|
+
execute: async ({ a, b }) => (a as number) + (b as number),
|
|
26
29
|
});
|
|
27
30
|
|
|
28
|
-
agent
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
output: response,
|
|
33
|
-
messages: [
|
|
34
|
-
...request.messages,
|
|
35
|
-
{ role: 'assistant', content: response },
|
|
36
|
-
],
|
|
37
|
-
};
|
|
31
|
+
// Create a chat agent for conversational interactions
|
|
32
|
+
const assistant = chatAgent('assistant', {
|
|
33
|
+
description: 'A helpful assistant',
|
|
34
|
+
execute: async (messages) => `You said: ${messages.at(-1)?.content}`,
|
|
38
35
|
});
|
|
39
36
|
|
|
40
|
-
// Serve the
|
|
41
|
-
serve({ agents: [
|
|
37
|
+
// Serve the agents
|
|
38
|
+
serve({ agents: [calculator, assistant], port: 8080 });
|
|
42
39
|
```
|
|
43
40
|
|
|
44
41
|
## How It Works
|
|
@@ -49,8 +46,7 @@ The runtime creates a REST server (powered by [Hono](https://hono.dev)) with the
|
|
|
49
46
|
|----------|--------|-------------|
|
|
50
47
|
| `/health` | GET | Health check |
|
|
51
48
|
| `/info` | GET | Runtime discovery (version, agents, tools) |
|
|
52
|
-
| `/agents/{name}/
|
|
53
|
-
| `/agents/{name}/chat` | POST | Conversational chat |
|
|
49
|
+
| `/agents/{name}/execute` | POST | Execute an agent |
|
|
54
50
|
| `/tools/{name}/execute` | POST | Execute a tool |
|
|
55
51
|
|
|
56
52
|
### Health Endpoint
|
|
@@ -67,61 +63,93 @@ Returns `{"status": "ok"}` if the server is running.
|
|
|
67
63
|
curl http://localhost:8080/info
|
|
68
64
|
```
|
|
69
65
|
|
|
70
|
-
Returns runtime information
|
|
66
|
+
Returns runtime information, available agents, and tools:
|
|
71
67
|
|
|
72
68
|
```json
|
|
73
69
|
{
|
|
74
70
|
"runtime": {
|
|
75
71
|
"name": "reminix-runtime",
|
|
76
|
-
"version": "0.0.
|
|
72
|
+
"version": "0.0.8",
|
|
77
73
|
"language": "typescript",
|
|
78
74
|
"framework": "hono"
|
|
79
75
|
},
|
|
80
76
|
"agents": [
|
|
81
77
|
{
|
|
82
|
-
"name": "
|
|
83
|
-
"type": "
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
|
|
78
|
+
"name": "calculator",
|
|
79
|
+
"type": "agent",
|
|
80
|
+
"description": "Add two numbers",
|
|
81
|
+
"parameters": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"properties": { "a": { "type": "number" }, "b": { "type": "number" } },
|
|
84
|
+
"required": ["a", "b"]
|
|
85
|
+
},
|
|
86
|
+
"output": { "type": "number" },
|
|
87
|
+
"requestKeys": ["a", "b"],
|
|
88
|
+
"responseKeys": ["output"],
|
|
89
|
+
"streaming": false
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"name": "assistant",
|
|
93
|
+
"type": "chat_agent",
|
|
94
|
+
"description": "A helpful assistant",
|
|
95
|
+
"parameters": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"properties": {
|
|
98
|
+
"messages": {
|
|
99
|
+
"type": "array",
|
|
100
|
+
"items": { "type": "object", "properties": { "role": { "type": "string" }, "content": { "type": "string" } }, "required": ["role", "content"] }
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"required": ["messages"]
|
|
104
|
+
},
|
|
105
|
+
"output": { "type": "object" },
|
|
106
|
+
"requestKeys": ["messages"],
|
|
107
|
+
"responseKeys": ["message"],
|
|
108
|
+
"streaming": false
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"tools": [
|
|
112
|
+
{
|
|
113
|
+
"name": "get_weather",
|
|
114
|
+
"type": "tool",
|
|
115
|
+
"description": "Get current weather for a location",
|
|
116
|
+
"parameters": { ... },
|
|
117
|
+
"output": { ... }
|
|
87
118
|
}
|
|
88
119
|
]
|
|
89
120
|
}
|
|
90
121
|
```
|
|
91
122
|
|
|
92
|
-
###
|
|
123
|
+
### Agent Execute Endpoint
|
|
93
124
|
|
|
94
|
-
`POST /agents/{name}/
|
|
125
|
+
`POST /agents/{name}/execute` - Execute an agent.
|
|
95
126
|
|
|
127
|
+
Request keys are defined by the agent's `parameters` schema. For example, a calculator agent with `parameters: { properties: { a, b } }` expects `a` and `b` at the top level:
|
|
128
|
+
|
|
129
|
+
**Task-oriented agent:**
|
|
96
130
|
```bash
|
|
97
|
-
curl -X POST http://localhost:8080/agents/
|
|
131
|
+
curl -X POST http://localhost:8080/agents/calculator/execute \
|
|
98
132
|
-H "Content-Type: application/json" \
|
|
99
|
-
-d '{
|
|
100
|
-
"input": {
|
|
101
|
-
"task": "summarize",
|
|
102
|
-
"text": "Lorem ipsum..."
|
|
103
|
-
}
|
|
104
|
-
}'
|
|
133
|
+
-d '{"a": 5, "b": 3}'
|
|
105
134
|
```
|
|
106
135
|
|
|
107
136
|
**Response:**
|
|
108
137
|
```json
|
|
109
138
|
{
|
|
110
|
-
"output":
|
|
139
|
+
"output": 8
|
|
111
140
|
}
|
|
112
141
|
```
|
|
113
142
|
|
|
114
|
-
|
|
143
|
+
**Chat agent:**
|
|
115
144
|
|
|
116
|
-
|
|
145
|
+
Chat agents expect `messages` at the top level and return `message`:
|
|
117
146
|
|
|
118
147
|
```bash
|
|
119
|
-
curl -X POST http://localhost:8080/agents/
|
|
148
|
+
curl -X POST http://localhost:8080/agents/assistant/execute \
|
|
120
149
|
-H "Content-Type: application/json" \
|
|
121
150
|
-d '{
|
|
122
151
|
"messages": [
|
|
123
|
-
{"role": "
|
|
124
|
-
{"role": "user", "content": "What is the weather?"}
|
|
152
|
+
{"role": "user", "content": "Hello!"}
|
|
125
153
|
]
|
|
126
154
|
}'
|
|
127
155
|
```
|
|
@@ -129,17 +157,13 @@ curl -X POST http://localhost:8080/agents/my-agent/chat \
|
|
|
129
157
|
**Response:**
|
|
130
158
|
```json
|
|
131
159
|
{
|
|
132
|
-
"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{"role": "assistant", "content": "The weather is 72°F and sunny!"}
|
|
137
|
-
]
|
|
160
|
+
"message": {
|
|
161
|
+
"role": "assistant",
|
|
162
|
+
"content": "You said: Hello!"
|
|
163
|
+
}
|
|
138
164
|
}
|
|
139
165
|
```
|
|
140
166
|
|
|
141
|
-
The `output` field contains the assistant's response, while `messages` includes the full conversation history.
|
|
142
|
-
|
|
143
167
|
### Tool Execute Endpoint
|
|
144
168
|
|
|
145
169
|
`POST /tools/{name}/execute` - Execute a standalone tool.
|
|
@@ -147,11 +171,7 @@ The `output` field contains the assistant's response, while `messages` includes
|
|
|
147
171
|
```bash
|
|
148
172
|
curl -X POST http://localhost:8080/tools/get_weather/execute \
|
|
149
173
|
-H "Content-Type: application/json" \
|
|
150
|
-
-d '{
|
|
151
|
-
"input": {
|
|
152
|
-
"location": "San Francisco"
|
|
153
|
-
}
|
|
154
|
-
}'
|
|
174
|
+
-d '{"location": "San Francisco"}'
|
|
155
175
|
```
|
|
156
176
|
|
|
157
177
|
**Response:**
|
|
@@ -161,9 +181,119 @@ curl -X POST http://localhost:8080/tools/get_weather/execute \
|
|
|
161
181
|
}
|
|
162
182
|
```
|
|
163
183
|
|
|
184
|
+
## Agents
|
|
185
|
+
|
|
186
|
+
Agents handle requests via the `/agents/{name}/execute` endpoint.
|
|
187
|
+
|
|
188
|
+
### Task-Oriented Agent
|
|
189
|
+
|
|
190
|
+
Use `agent()` for task-oriented agents that take structured input and return output:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { agent, serve } from '@reminix/runtime';
|
|
194
|
+
|
|
195
|
+
const calculator = agent('calculator', {
|
|
196
|
+
description: 'Add two numbers',
|
|
197
|
+
parameters: {
|
|
198
|
+
type: 'object',
|
|
199
|
+
properties: {
|
|
200
|
+
a: { type: 'number' },
|
|
201
|
+
b: { type: 'number' },
|
|
202
|
+
},
|
|
203
|
+
required: ['a', 'b'],
|
|
204
|
+
},
|
|
205
|
+
execute: async ({ a, b }) => (a as number) + (b as number),
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const textProcessor = agent('text-processor', {
|
|
209
|
+
description: 'Process text in various ways',
|
|
210
|
+
parameters: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
properties: {
|
|
213
|
+
text: { type: 'string' },
|
|
214
|
+
operation: { type: 'string', enum: ['uppercase', 'lowercase'] },
|
|
215
|
+
},
|
|
216
|
+
required: ['text'],
|
|
217
|
+
},
|
|
218
|
+
execute: async ({ text, operation }) => {
|
|
219
|
+
const t = text as string;
|
|
220
|
+
return operation === 'uppercase' ? t.toUpperCase() : t.toLowerCase();
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
serve({ agents: [calculator, textProcessor], port: 8080 });
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Chat Agent
|
|
228
|
+
|
|
229
|
+
Use `chatAgent()` for conversational agents that handle message history:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { chatAgent, serve } from '@reminix/runtime';
|
|
233
|
+
|
|
234
|
+
const assistant = chatAgent('assistant', {
|
|
235
|
+
description: 'A helpful assistant',
|
|
236
|
+
execute: async (messages) => {
|
|
237
|
+
const lastMsg = messages.at(-1)?.content ?? '';
|
|
238
|
+
return `You said: ${lastMsg}`;
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// With context support
|
|
243
|
+
const contextualBot = chatAgent('contextual-bot', {
|
|
244
|
+
description: 'Bot with context awareness',
|
|
245
|
+
execute: async (messages, context) => {
|
|
246
|
+
const userId = context?.user_id ?? 'unknown';
|
|
247
|
+
return `Hello user ${userId}!`;
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
serve({ agents: [assistant, contextualBot], port: 8080 });
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Streaming
|
|
255
|
+
|
|
256
|
+
Both factories support streaming via async generators. When you use an async generator function, the agent automatically supports streaming:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { agent, chatAgent, serve } from '@reminix/runtime';
|
|
260
|
+
|
|
261
|
+
// Streaming task agent
|
|
262
|
+
const streamer = agent('streamer', {
|
|
263
|
+
description: 'Stream text word by word',
|
|
264
|
+
parameters: {
|
|
265
|
+
type: 'object',
|
|
266
|
+
properties: { text: { type: 'string' } },
|
|
267
|
+
required: ['text'],
|
|
268
|
+
},
|
|
269
|
+
execute: async function* ({ text }) {
|
|
270
|
+
for (const word of (text as string).split(' ')) {
|
|
271
|
+
yield word + ' ';
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Streaming chat agent
|
|
277
|
+
const streamingAssistant = chatAgent('streaming-assistant', {
|
|
278
|
+
description: 'Stream responses token by token',
|
|
279
|
+
execute: async function* (messages) {
|
|
280
|
+
const response = `You said: ${messages.at(-1)?.content}`;
|
|
281
|
+
for (const char of response) {
|
|
282
|
+
yield char;
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
serve({ agents: [streamer, streamingAssistant], port: 8080 });
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
For streaming agents:
|
|
291
|
+
- `stream: true` in the request → chunks are sent via SSE
|
|
292
|
+
- `stream: false` in the request → chunks are collected and returned as a single response
|
|
293
|
+
|
|
164
294
|
## Tools
|
|
165
295
|
|
|
166
|
-
Tools are standalone functions
|
|
296
|
+
Tools are standalone functions served via `/tools/{name}/execute`. They're useful for exposing utility functions, external API integrations, or any reusable logic.
|
|
167
297
|
|
|
168
298
|
### Creating Tools
|
|
169
299
|
|
|
@@ -182,54 +312,50 @@ const getWeather = tool('get_weather', {
|
|
|
182
312
|
},
|
|
183
313
|
required: ['location'],
|
|
184
314
|
},
|
|
185
|
-
// Optional: define output schema for documentation and type inference
|
|
186
315
|
output: {
|
|
187
316
|
type: 'object',
|
|
188
317
|
properties: {
|
|
189
318
|
temp: { type: 'number' },
|
|
190
319
|
condition: { type: 'string' },
|
|
191
|
-
location: { type: 'string' },
|
|
192
320
|
},
|
|
193
321
|
},
|
|
194
322
|
execute: async (input) => {
|
|
195
323
|
const location = input.location as string;
|
|
196
|
-
// Call weather API...
|
|
197
324
|
return { temp: 72, condition: 'sunny', location };
|
|
198
325
|
},
|
|
199
326
|
});
|
|
200
327
|
|
|
201
|
-
// Serve tools (with or without agents)
|
|
202
328
|
serve({ tools: [getWeather], port: 8080 });
|
|
203
329
|
```
|
|
204
330
|
|
|
205
|
-
The optional `output` property defines the JSON Schema for the tool's return value. This is included in the `/info` endpoint for documentation and enables better type inference for clients.
|
|
206
|
-
|
|
207
331
|
### Serving Agents and Tools Together
|
|
208
332
|
|
|
209
333
|
You can serve both agents and tools from the same runtime:
|
|
210
334
|
|
|
211
335
|
```typescript
|
|
212
|
-
import {
|
|
336
|
+
import { agent, tool, serve } from '@reminix/runtime';
|
|
213
337
|
|
|
214
|
-
const
|
|
215
|
-
|
|
338
|
+
const summarizer = agent('summarizer', {
|
|
339
|
+
description: 'Summarize text',
|
|
340
|
+
parameters: {
|
|
341
|
+
type: 'object',
|
|
342
|
+
properties: { text: { type: 'string' } },
|
|
343
|
+
required: ['text'],
|
|
344
|
+
},
|
|
345
|
+
execute: async ({ text }) => (text as string).slice(0, 100) + '...',
|
|
346
|
+
});
|
|
216
347
|
|
|
217
348
|
const calculator = tool('calculate', {
|
|
218
349
|
description: 'Perform basic math operations',
|
|
219
350
|
parameters: {
|
|
220
351
|
type: 'object',
|
|
221
|
-
properties: {
|
|
222
|
-
expression: { type: 'string', description: 'Math expression to evaluate' },
|
|
223
|
-
},
|
|
352
|
+
properties: { expression: { type: 'string' } },
|
|
224
353
|
required: ['expression'],
|
|
225
354
|
},
|
|
226
|
-
execute: async (input) => {
|
|
227
|
-
const expr = input.expression as string;
|
|
228
|
-
return { result: eval(expr) }; // Note: use a safe evaluator in production
|
|
229
|
-
},
|
|
355
|
+
execute: async (input) => ({ result: eval(input.expression as string) }),
|
|
230
356
|
});
|
|
231
357
|
|
|
232
|
-
serve({ agents: [
|
|
358
|
+
serve({ agents: [summarizer], tools: [calculator], port: 8080 });
|
|
233
359
|
```
|
|
234
360
|
|
|
235
361
|
## Framework Adapters
|
|
@@ -270,6 +396,77 @@ const app = createApp({ agents: [myAgent], tools: [myTool] });
|
|
|
270
396
|
// Use with any runtime: Node.js, Deno, Bun, Cloudflare Workers, etc.
|
|
271
397
|
```
|
|
272
398
|
|
|
399
|
+
### `agent(name, options)`
|
|
400
|
+
|
|
401
|
+
Factory function to create a task-oriented agent.
|
|
402
|
+
|
|
403
|
+
| Parameter | Type | Description |
|
|
404
|
+
|-----------|------|-------------|
|
|
405
|
+
| `name` | `string` | Unique identifier for the agent |
|
|
406
|
+
| `options.description` | `string` | Human-readable description |
|
|
407
|
+
| `options.parameters` | `object` | JSON Schema for input parameters |
|
|
408
|
+
| `options.output` | `object` | Optional JSON Schema for output |
|
|
409
|
+
| `options.execute` | `function` | Async function or async generator to execute |
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
import { agent } from '@reminix/runtime';
|
|
413
|
+
|
|
414
|
+
// Regular agent
|
|
415
|
+
const myAgent = agent('my-agent', {
|
|
416
|
+
description: 'Does something useful',
|
|
417
|
+
parameters: {
|
|
418
|
+
type: 'object',
|
|
419
|
+
properties: { input: { type: 'string' } },
|
|
420
|
+
required: ['input'],
|
|
421
|
+
},
|
|
422
|
+
execute: async ({ input }) => ({ result: input }),
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// Streaming agent
|
|
426
|
+
const streamingAgent = agent('streaming-agent', {
|
|
427
|
+
description: 'Streams output',
|
|
428
|
+
parameters: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
|
|
429
|
+
execute: async function* ({ text }) {
|
|
430
|
+
for (const word of (text as string).split(' ')) {
|
|
431
|
+
yield word + ' ';
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### `chatAgent(name, options)`
|
|
438
|
+
|
|
439
|
+
Factory function to create a chat agent.
|
|
440
|
+
|
|
441
|
+
| Parameter | Type | Description |
|
|
442
|
+
|-----------|------|-------------|
|
|
443
|
+
| `name` | `string` | Unique identifier for the agent |
|
|
444
|
+
| `options.description` | `string` | Human-readable description |
|
|
445
|
+
| `options.execute` | `function` | Async function or async generator receiving messages |
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
import { chatAgent } from '@reminix/runtime';
|
|
449
|
+
|
|
450
|
+
// Regular chat agent
|
|
451
|
+
const bot = chatAgent('bot', {
|
|
452
|
+
description: 'A simple bot',
|
|
453
|
+
execute: async (messages) => `You said: ${messages.at(-1)?.content}`,
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// With context
|
|
457
|
+
const contextBot = chatAgent('context-bot', {
|
|
458
|
+
execute: async (messages, context) => `Hello ${context?.user_id}!`,
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// Streaming chat agent
|
|
462
|
+
const streamingBot = chatAgent('streaming-bot', {
|
|
463
|
+
execute: async function* (messages) {
|
|
464
|
+
yield 'Hello';
|
|
465
|
+
yield ' world!';
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
```
|
|
469
|
+
|
|
273
470
|
### `tool(name, options)`
|
|
274
471
|
|
|
275
472
|
Factory function to create a tool.
|
|
@@ -279,6 +476,7 @@ Factory function to create a tool.
|
|
|
279
476
|
| `name` | `string` | Unique identifier for the tool |
|
|
280
477
|
| `options.description` | `string` | Human-readable description |
|
|
281
478
|
| `options.parameters` | `object` | JSON Schema for input parameters |
|
|
479
|
+
| `options.output` | `object` | Optional JSON Schema for output |
|
|
282
480
|
| `options.execute` | `function` | Async function to execute when called |
|
|
283
481
|
|
|
284
482
|
```typescript
|
|
@@ -288,150 +486,136 @@ const myTool = tool('my_tool', {
|
|
|
288
486
|
description: 'Does something useful',
|
|
289
487
|
parameters: {
|
|
290
488
|
type: 'object',
|
|
291
|
-
properties: {
|
|
292
|
-
input: { type: 'string' },
|
|
293
|
-
},
|
|
489
|
+
properties: { input: { type: 'string' } },
|
|
294
490
|
required: ['input'],
|
|
295
491
|
},
|
|
296
|
-
execute: async (input) => {
|
|
297
|
-
return { result: input.input };
|
|
298
|
-
},
|
|
492
|
+
execute: async (input) => ({ result: input.input }),
|
|
299
493
|
});
|
|
300
494
|
```
|
|
301
495
|
|
|
302
|
-
###
|
|
496
|
+
### Request/Response Types
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
// Request: top-level keys based on agent's requestKeys (derived from parameters)
|
|
500
|
+
// For a calculator agent with parameters { a: number, b: number }:
|
|
501
|
+
interface CalculatorRequest {
|
|
502
|
+
a: number; // Top-level key from parameters
|
|
503
|
+
b: number; // Top-level key from parameters
|
|
504
|
+
stream?: boolean; // Whether to stream the response
|
|
505
|
+
context?: Record<string, unknown>; // Optional metadata
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// For a chat agent:
|
|
509
|
+
interface ChatRequest {
|
|
510
|
+
messages: Message[]; // Top-level key (requestKeys: ['messages'])
|
|
511
|
+
stream?: boolean;
|
|
512
|
+
context?: Record<string, unknown>;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Response: keys based on agent's responseKeys
|
|
516
|
+
// Regular agent (responseKeys: ['output']):
|
|
517
|
+
interface AgentResponse {
|
|
518
|
+
output: unknown;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Chat agent (responseKeys: ['message']):
|
|
522
|
+
interface ChatResponse {
|
|
523
|
+
message: { role: string; content: string };
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
## Advanced
|
|
528
|
+
|
|
529
|
+
### Agent Class
|
|
303
530
|
|
|
304
|
-
|
|
531
|
+
For more control, you can use the `Agent` class directly:
|
|
305
532
|
|
|
306
533
|
```typescript
|
|
307
|
-
import { Agent } from '@reminix/runtime';
|
|
534
|
+
import { Agent, serve } from '@reminix/runtime';
|
|
308
535
|
|
|
309
536
|
const agent = new Agent('my-agent', { metadata: { version: '1.0' } });
|
|
310
537
|
|
|
311
|
-
agent.
|
|
538
|
+
agent.onExecute(async (request) => {
|
|
312
539
|
return { output: 'Hello!' };
|
|
313
540
|
});
|
|
314
541
|
|
|
315
|
-
|
|
316
|
-
|
|
542
|
+
// Optional: streaming handler
|
|
543
|
+
agent.onExecuteStream(async function* (request) {
|
|
544
|
+
yield 'Hello';
|
|
545
|
+
yield ' world!';
|
|
317
546
|
});
|
|
318
547
|
|
|
319
|
-
|
|
320
|
-
agent.onInvokeStream(async function* (request) {
|
|
321
|
-
yield '{"chunk": "Hello"}';
|
|
322
|
-
yield '{"chunk": " world!"}';
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
agent.onChatStream(async function* (request) {
|
|
326
|
-
yield '{"chunk": "Hi"}';
|
|
327
|
-
});
|
|
548
|
+
serve({ agents: [agent], port: 8080 });
|
|
328
549
|
```
|
|
329
550
|
|
|
330
|
-
|
|
331
|
-
|--------|-------------|
|
|
332
|
-
| `onInvoke(fn)` | Register invoke handler, returns `this` for chaining |
|
|
333
|
-
| `onChat(fn)` | Register chat handler, returns `this` for chaining |
|
|
334
|
-
| `onInvokeStream(fn)` | Register streaming invoke handler |
|
|
335
|
-
| `onChatStream(fn)` | Register streaming chat handler |
|
|
336
|
-
| `toHandler()` | Returns a web-standard fetch handler for serverless |
|
|
551
|
+
### Tool Class
|
|
337
552
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
Returns a web-standard `(Request) => Promise<Response>` handler for serverless deployments.
|
|
553
|
+
For programmatic tool creation:
|
|
341
554
|
|
|
342
555
|
```typescript
|
|
343
|
-
|
|
344
|
-
import { Agent } from '@reminix/runtime';
|
|
556
|
+
import { Tool, serve } from '@reminix/runtime';
|
|
345
557
|
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
// Deno Deploy
|
|
356
|
-
Deno.serve(agent.toHandler());
|
|
558
|
+
const myTool = new Tool('get_weather', {
|
|
559
|
+
description: 'Get weather for a location',
|
|
560
|
+
parameters: {
|
|
561
|
+
type: 'object',
|
|
562
|
+
properties: { location: { type: 'string' } },
|
|
563
|
+
required: ['location'],
|
|
564
|
+
},
|
|
565
|
+
execute: async (input) => ({ temp: 72, location: input.location }),
|
|
566
|
+
});
|
|
357
567
|
|
|
358
|
-
|
|
359
|
-
Bun.serve({ fetch: agent.toHandler() });
|
|
568
|
+
serve({ tools: [myTool], port: 8080 });
|
|
360
569
|
```
|
|
361
570
|
|
|
362
|
-
###
|
|
571
|
+
### AgentAdapter
|
|
363
572
|
|
|
364
|
-
|
|
573
|
+
For building framework integrations. See the [framework adapter packages](#framework-adapters) for examples.
|
|
365
574
|
|
|
366
575
|
```typescript
|
|
367
|
-
import {
|
|
576
|
+
import { AgentAdapter } from '@reminix/runtime';
|
|
368
577
|
|
|
369
|
-
class MyFrameworkAdapter extends
|
|
370
|
-
// Adapter name shown in /info endpoint
|
|
578
|
+
class MyFrameworkAdapter extends AgentAdapter {
|
|
371
579
|
static adapterName = 'my-framework';
|
|
372
|
-
|
|
373
|
-
// BaseAdapter defaults both to true; override if your adapter doesn't support streaming
|
|
374
|
-
// override readonly invokeStreaming = false;
|
|
375
|
-
// override readonly chatStreaming = false;
|
|
376
580
|
|
|
377
|
-
private client:
|
|
378
|
-
private _name: string;
|
|
379
|
-
|
|
380
|
-
constructor(client: MyFrameworkClient, name = 'my-framework') {
|
|
581
|
+
constructor(private client: MyClient, private _name = 'my-framework') {
|
|
381
582
|
super();
|
|
382
|
-
this.client = client;
|
|
383
|
-
this._name = name;
|
|
384
583
|
}
|
|
385
584
|
|
|
386
|
-
get name()
|
|
585
|
+
get name() {
|
|
387
586
|
return this._name;
|
|
388
587
|
}
|
|
389
588
|
|
|
390
|
-
async
|
|
391
|
-
// Pass input to your framework
|
|
589
|
+
async execute(request) {
|
|
392
590
|
const result = await this.client.run(request.input);
|
|
393
591
|
return { output: result };
|
|
394
592
|
}
|
|
395
|
-
|
|
396
|
-
async chat(request: ChatRequest): Promise<ChatResponse> {
|
|
397
|
-
// Convert messages and call your framework
|
|
398
|
-
const result = await this.client.chat(request.messages);
|
|
399
|
-
return {
|
|
400
|
-
output: result,
|
|
401
|
-
messages: [...request.messages, { role: 'assistant', content: result }],
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Optional: provide a wrap() factory function
|
|
407
|
-
export function wrap(client: MyFrameworkClient, name = 'my-framework'): MyFrameworkAdapter {
|
|
408
|
-
return new MyFrameworkAdapter(client, name);
|
|
409
593
|
}
|
|
410
594
|
```
|
|
411
595
|
|
|
412
|
-
###
|
|
596
|
+
### Serverless Deployment
|
|
597
|
+
|
|
598
|
+
Use `toHandler()` for serverless deployments:
|
|
413
599
|
|
|
414
600
|
```typescript
|
|
415
|
-
|
|
416
|
-
input: Record<string, unknown>; // Arbitrary input for task execution
|
|
417
|
-
stream?: boolean; // Whether to stream the response
|
|
418
|
-
context?: Record<string, unknown>; // Optional metadata
|
|
419
|
-
}
|
|
601
|
+
import { agent } from '@reminix/runtime';
|
|
420
602
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}
|
|
603
|
+
const myAgent = agent('my-agent', {
|
|
604
|
+
execute: async ({ task }) => `Completed: ${task}`,
|
|
605
|
+
});
|
|
424
606
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
context?: Record<string, unknown>; // Optional metadata
|
|
429
|
-
}
|
|
607
|
+
// Vercel Edge Function
|
|
608
|
+
export const POST = myAgent.toHandler();
|
|
609
|
+
export const GET = myAgent.toHandler();
|
|
430
610
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
611
|
+
// Cloudflare Workers
|
|
612
|
+
export default { fetch: myAgent.toHandler() };
|
|
613
|
+
|
|
614
|
+
// Deno Deploy
|
|
615
|
+
Deno.serve(myAgent.toHandler());
|
|
616
|
+
|
|
617
|
+
// Bun
|
|
618
|
+
Bun.serve({ fetch: myAgent.toHandler() });
|
|
435
619
|
```
|
|
436
620
|
|
|
437
621
|
## Deployment
|