@reminix/runtime 0.0.7 → 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 +343 -167
- package/dist/agent-adapter.d.ts +4 -9
- package/dist/agent-adapter.d.ts.map +1 -1
- package/dist/agent-adapter.js +3 -13
- package/dist/agent-adapter.js.map +1 -1
- 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 +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- 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/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
|
|
@@ -73,16 +69,43 @@ Returns runtime information, available agents, and tools:
|
|
|
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": "
|
|
78
|
+
"name": "calculator",
|
|
83
79
|
"type": "agent",
|
|
84
|
-
"
|
|
85
|
-
"
|
|
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
|
|
86
109
|
}
|
|
87
110
|
],
|
|
88
111
|
"tools": [
|
|
@@ -97,39 +120,36 @@ Returns runtime information, available agents, and tools:
|
|
|
97
120
|
}
|
|
98
121
|
```
|
|
99
122
|
|
|
100
|
-
###
|
|
123
|
+
### Agent Execute Endpoint
|
|
124
|
+
|
|
125
|
+
`POST /agents/{name}/execute` - Execute an agent.
|
|
101
126
|
|
|
102
|
-
`
|
|
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:
|
|
103
128
|
|
|
129
|
+
**Task-oriented agent:**
|
|
104
130
|
```bash
|
|
105
|
-
curl -X POST http://localhost:8080/agents/
|
|
131
|
+
curl -X POST http://localhost:8080/agents/calculator/execute \
|
|
106
132
|
-H "Content-Type: application/json" \
|
|
107
|
-
-d '{
|
|
108
|
-
"input": {
|
|
109
|
-
"task": "summarize",
|
|
110
|
-
"text": "Lorem ipsum..."
|
|
111
|
-
}
|
|
112
|
-
}'
|
|
133
|
+
-d '{"a": 5, "b": 3}'
|
|
113
134
|
```
|
|
114
135
|
|
|
115
136
|
**Response:**
|
|
116
137
|
```json
|
|
117
138
|
{
|
|
118
|
-
"output":
|
|
139
|
+
"output": 8
|
|
119
140
|
}
|
|
120
141
|
```
|
|
121
142
|
|
|
122
|
-
|
|
143
|
+
**Chat agent:**
|
|
123
144
|
|
|
124
|
-
|
|
145
|
+
Chat agents expect `messages` at the top level and return `message`:
|
|
125
146
|
|
|
126
147
|
```bash
|
|
127
|
-
curl -X POST http://localhost:8080/agents/
|
|
148
|
+
curl -X POST http://localhost:8080/agents/assistant/execute \
|
|
128
149
|
-H "Content-Type: application/json" \
|
|
129
150
|
-d '{
|
|
130
151
|
"messages": [
|
|
131
|
-
{"role": "
|
|
132
|
-
{"role": "user", "content": "What is the weather?"}
|
|
152
|
+
{"role": "user", "content": "Hello!"}
|
|
133
153
|
]
|
|
134
154
|
}'
|
|
135
155
|
```
|
|
@@ -137,17 +157,13 @@ curl -X POST http://localhost:8080/agents/my-agent/chat \
|
|
|
137
157
|
**Response:**
|
|
138
158
|
```json
|
|
139
159
|
{
|
|
140
|
-
"
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
{"role": "assistant", "content": "The weather is 72°F and sunny!"}
|
|
145
|
-
]
|
|
160
|
+
"message": {
|
|
161
|
+
"role": "assistant",
|
|
162
|
+
"content": "You said: Hello!"
|
|
163
|
+
}
|
|
146
164
|
}
|
|
147
165
|
```
|
|
148
166
|
|
|
149
|
-
The `output` field contains the assistant's response, while `messages` includes the full conversation history.
|
|
150
|
-
|
|
151
167
|
### Tool Execute Endpoint
|
|
152
168
|
|
|
153
169
|
`POST /tools/{name}/execute` - Execute a standalone tool.
|
|
@@ -155,11 +171,7 @@ The `output` field contains the assistant's response, while `messages` includes
|
|
|
155
171
|
```bash
|
|
156
172
|
curl -X POST http://localhost:8080/tools/get_weather/execute \
|
|
157
173
|
-H "Content-Type: application/json" \
|
|
158
|
-
-d '{
|
|
159
|
-
"input": {
|
|
160
|
-
"location": "San Francisco"
|
|
161
|
-
}
|
|
162
|
-
}'
|
|
174
|
+
-d '{"location": "San Francisco"}'
|
|
163
175
|
```
|
|
164
176
|
|
|
165
177
|
**Response:**
|
|
@@ -169,9 +181,119 @@ curl -X POST http://localhost:8080/tools/get_weather/execute \
|
|
|
169
181
|
}
|
|
170
182
|
```
|
|
171
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
|
+
|
|
172
294
|
## Tools
|
|
173
295
|
|
|
174
|
-
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.
|
|
175
297
|
|
|
176
298
|
### Creating Tools
|
|
177
299
|
|
|
@@ -190,54 +312,50 @@ const getWeather = tool('get_weather', {
|
|
|
190
312
|
},
|
|
191
313
|
required: ['location'],
|
|
192
314
|
},
|
|
193
|
-
// Optional: define output schema for documentation and type inference
|
|
194
315
|
output: {
|
|
195
316
|
type: 'object',
|
|
196
317
|
properties: {
|
|
197
318
|
temp: { type: 'number' },
|
|
198
319
|
condition: { type: 'string' },
|
|
199
|
-
location: { type: 'string' },
|
|
200
320
|
},
|
|
201
321
|
},
|
|
202
322
|
execute: async (input) => {
|
|
203
323
|
const location = input.location as string;
|
|
204
|
-
// Call weather API...
|
|
205
324
|
return { temp: 72, condition: 'sunny', location };
|
|
206
325
|
},
|
|
207
326
|
});
|
|
208
327
|
|
|
209
|
-
// Serve tools (with or without agents)
|
|
210
328
|
serve({ tools: [getWeather], port: 8080 });
|
|
211
329
|
```
|
|
212
330
|
|
|
213
|
-
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.
|
|
214
|
-
|
|
215
331
|
### Serving Agents and Tools Together
|
|
216
332
|
|
|
217
333
|
You can serve both agents and tools from the same runtime:
|
|
218
334
|
|
|
219
335
|
```typescript
|
|
220
|
-
import {
|
|
336
|
+
import { agent, tool, serve } from '@reminix/runtime';
|
|
221
337
|
|
|
222
|
-
const
|
|
223
|
-
|
|
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
|
+
});
|
|
224
347
|
|
|
225
348
|
const calculator = tool('calculate', {
|
|
226
349
|
description: 'Perform basic math operations',
|
|
227
350
|
parameters: {
|
|
228
351
|
type: 'object',
|
|
229
|
-
properties: {
|
|
230
|
-
expression: { type: 'string', description: 'Math expression to evaluate' },
|
|
231
|
-
},
|
|
352
|
+
properties: { expression: { type: 'string' } },
|
|
232
353
|
required: ['expression'],
|
|
233
354
|
},
|
|
234
|
-
execute: async (input) => {
|
|
235
|
-
const expr = input.expression as string;
|
|
236
|
-
return { result: eval(expr) }; // Note: use a safe evaluator in production
|
|
237
|
-
},
|
|
355
|
+
execute: async (input) => ({ result: eval(input.expression as string) }),
|
|
238
356
|
});
|
|
239
357
|
|
|
240
|
-
serve({ agents: [
|
|
358
|
+
serve({ agents: [summarizer], tools: [calculator], port: 8080 });
|
|
241
359
|
```
|
|
242
360
|
|
|
243
361
|
## Framework Adapters
|
|
@@ -278,6 +396,77 @@ const app = createApp({ agents: [myAgent], tools: [myTool] });
|
|
|
278
396
|
// Use with any runtime: Node.js, Deno, Bun, Cloudflare Workers, etc.
|
|
279
397
|
```
|
|
280
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
|
+
|
|
281
470
|
### `tool(name, options)`
|
|
282
471
|
|
|
283
472
|
Factory function to create a tool.
|
|
@@ -287,6 +476,7 @@ Factory function to create a tool.
|
|
|
287
476
|
| `name` | `string` | Unique identifier for the tool |
|
|
288
477
|
| `options.description` | `string` | Human-readable description |
|
|
289
478
|
| `options.parameters` | `object` | JSON Schema for input parameters |
|
|
479
|
+
| `options.output` | `object` | Optional JSON Schema for output |
|
|
290
480
|
| `options.execute` | `function` | Async function to execute when called |
|
|
291
481
|
|
|
292
482
|
```typescript
|
|
@@ -296,150 +486,136 @@ const myTool = tool('my_tool', {
|
|
|
296
486
|
description: 'Does something useful',
|
|
297
487
|
parameters: {
|
|
298
488
|
type: 'object',
|
|
299
|
-
properties: {
|
|
300
|
-
input: { type: 'string' },
|
|
301
|
-
},
|
|
489
|
+
properties: { input: { type: 'string' } },
|
|
302
490
|
required: ['input'],
|
|
303
491
|
},
|
|
304
|
-
execute: async (input) => {
|
|
305
|
-
return { result: input.input };
|
|
306
|
-
},
|
|
492
|
+
execute: async (input) => ({ result: input.input }),
|
|
307
493
|
});
|
|
308
494
|
```
|
|
309
495
|
|
|
310
|
-
###
|
|
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
|
+
}
|
|
311
507
|
|
|
312
|
-
|
|
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
|
|
530
|
+
|
|
531
|
+
For more control, you can use the `Agent` class directly:
|
|
313
532
|
|
|
314
533
|
```typescript
|
|
315
|
-
import { Agent } from '@reminix/runtime';
|
|
534
|
+
import { Agent, serve } from '@reminix/runtime';
|
|
316
535
|
|
|
317
536
|
const agent = new Agent('my-agent', { metadata: { version: '1.0' } });
|
|
318
537
|
|
|
319
|
-
agent.
|
|
538
|
+
agent.onExecute(async (request) => {
|
|
320
539
|
return { output: 'Hello!' };
|
|
321
540
|
});
|
|
322
541
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
// Optional: streaming handlers
|
|
328
|
-
agent.onInvokeStream(async function* (request) {
|
|
329
|
-
yield '{"chunk": "Hello"}';
|
|
330
|
-
yield '{"chunk": " world!"}';
|
|
542
|
+
// Optional: streaming handler
|
|
543
|
+
agent.onExecuteStream(async function* (request) {
|
|
544
|
+
yield 'Hello';
|
|
545
|
+
yield ' world!';
|
|
331
546
|
});
|
|
332
547
|
|
|
333
|
-
|
|
334
|
-
yield '{"chunk": "Hi"}';
|
|
335
|
-
});
|
|
548
|
+
serve({ agents: [agent], port: 8080 });
|
|
336
549
|
```
|
|
337
550
|
|
|
338
|
-
|
|
339
|
-
|--------|-------------|
|
|
340
|
-
| `onInvoke(fn)` | Register invoke handler, returns `this` for chaining |
|
|
341
|
-
| `onChat(fn)` | Register chat handler, returns `this` for chaining |
|
|
342
|
-
| `onInvokeStream(fn)` | Register streaming invoke handler |
|
|
343
|
-
| `onChatStream(fn)` | Register streaming chat handler |
|
|
344
|
-
| `toHandler()` | Returns a web-standard fetch handler for serverless |
|
|
345
|
-
|
|
346
|
-
### `agent.toHandler()`
|
|
551
|
+
### Tool Class
|
|
347
552
|
|
|
348
|
-
|
|
553
|
+
For programmatic tool creation:
|
|
349
554
|
|
|
350
555
|
```typescript
|
|
351
|
-
|
|
352
|
-
import { Agent } from '@reminix/runtime';
|
|
353
|
-
|
|
354
|
-
const agent = new Agent('my-agent');
|
|
355
|
-
agent.onInvoke(async (req) => ({ output: 'Hello!' }));
|
|
556
|
+
import { Tool, serve } from '@reminix/runtime';
|
|
356
557
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
+
});
|
|
365
567
|
|
|
366
|
-
|
|
367
|
-
Bun.serve({ fetch: agent.toHandler() });
|
|
568
|
+
serve({ tools: [myTool], port: 8080 });
|
|
368
569
|
```
|
|
369
570
|
|
|
370
|
-
###
|
|
571
|
+
### AgentAdapter
|
|
371
572
|
|
|
372
|
-
|
|
573
|
+
For building framework integrations. See the [framework adapter packages](#framework-adapters) for examples.
|
|
373
574
|
|
|
374
575
|
```typescript
|
|
375
|
-
import { AgentAdapter
|
|
576
|
+
import { AgentAdapter } from '@reminix/runtime';
|
|
376
577
|
|
|
377
578
|
class MyFrameworkAdapter extends AgentAdapter {
|
|
378
|
-
// Adapter name shown in /info endpoint
|
|
379
579
|
static adapterName = 'my-framework';
|
|
380
|
-
|
|
381
|
-
// AgentAdapter defaults both to true; override if your adapter doesn't support streaming
|
|
382
|
-
// override readonly invokeStreaming = false;
|
|
383
|
-
// override readonly chatStreaming = false;
|
|
384
|
-
|
|
385
|
-
private client: MyFrameworkClient;
|
|
386
|
-
private _name: string;
|
|
387
580
|
|
|
388
|
-
constructor(client:
|
|
581
|
+
constructor(private client: MyClient, private _name = 'my-framework') {
|
|
389
582
|
super();
|
|
390
|
-
this.client = client;
|
|
391
|
-
this._name = name;
|
|
392
583
|
}
|
|
393
584
|
|
|
394
|
-
get name()
|
|
585
|
+
get name() {
|
|
395
586
|
return this._name;
|
|
396
587
|
}
|
|
397
588
|
|
|
398
|
-
async
|
|
399
|
-
// Pass input to your framework
|
|
589
|
+
async execute(request) {
|
|
400
590
|
const result = await this.client.run(request.input);
|
|
401
591
|
return { output: result };
|
|
402
592
|
}
|
|
403
|
-
|
|
404
|
-
async chat(request: ChatRequest): Promise<ChatResponse> {
|
|
405
|
-
// Convert messages and call your framework
|
|
406
|
-
const result = await this.client.chat(request.messages);
|
|
407
|
-
return {
|
|
408
|
-
output: result,
|
|
409
|
-
messages: [...request.messages, { role: 'assistant', content: result }],
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Optional: provide a wrap() factory function
|
|
415
|
-
export function wrap(client: MyFrameworkClient, name = 'my-framework'): MyFrameworkAdapter {
|
|
416
|
-
return new MyFrameworkAdapter(client, name);
|
|
417
593
|
}
|
|
418
594
|
```
|
|
419
595
|
|
|
420
|
-
###
|
|
596
|
+
### Serverless Deployment
|
|
597
|
+
|
|
598
|
+
Use `toHandler()` for serverless deployments:
|
|
421
599
|
|
|
422
600
|
```typescript
|
|
423
|
-
|
|
424
|
-
input: Record<string, unknown>; // Arbitrary input for task execution
|
|
425
|
-
stream?: boolean; // Whether to stream the response
|
|
426
|
-
context?: Record<string, unknown>; // Optional metadata
|
|
427
|
-
}
|
|
601
|
+
import { agent } from '@reminix/runtime';
|
|
428
602
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
603
|
+
const myAgent = agent('my-agent', {
|
|
604
|
+
execute: async ({ task }) => `Completed: ${task}`,
|
|
605
|
+
});
|
|
432
606
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
context?: Record<string, unknown>; // Optional metadata
|
|
437
|
-
}
|
|
607
|
+
// Vercel Edge Function
|
|
608
|
+
export const POST = myAgent.toHandler();
|
|
609
|
+
export const GET = myAgent.toHandler();
|
|
438
610
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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() });
|
|
443
619
|
```
|
|
444
620
|
|
|
445
621
|
## Deployment
|