@fllf/agent-sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +514 -240
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -1,240 +1,514 @@
|
|
|
1
|
-
# FAgent
|
|
2
|
-
|
|
3
|
-
FAgent is a composable TypeScript
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
LOCAL_BASE_URL=http://127.0.0.1:11434/v1
|
|
57
|
-
LOCAL_MODEL=llama3
|
|
58
|
-
LOCAL_API_KEY=local-key
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
If no OpenAI key is configured,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
1
|
+
# FAgent Agent SDK
|
|
2
|
+
|
|
3
|
+
FAgent is a composable TypeScript SDK for building LLM agents, tool-calling
|
|
4
|
+
workflows, and RAG-powered chat systems.
|
|
5
|
+
|
|
6
|
+
The published npm package is:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @fllf/agent-sdk
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Use it as the runtime core behind a web chat product, backend API, worker, or
|
|
13
|
+
third-party integration layer. The SDK does not include a web UI, database
|
|
14
|
+
migrations for your application, authentication, or deployment code.
|
|
15
|
+
|
|
16
|
+
## Capabilities
|
|
17
|
+
|
|
18
|
+
- Composable `Agent` runtime.
|
|
19
|
+
- Unified `LLM.chat()` interface.
|
|
20
|
+
- OpenAI and OpenAI-compatible local providers.
|
|
21
|
+
- Streaming through `LLM.stream()`.
|
|
22
|
+
- Timeout, retry, abort, and normalized LLM errors.
|
|
23
|
+
- `generateObject()` structured output with Zod validation.
|
|
24
|
+
- Session-scoped message history.
|
|
25
|
+
- Simple chat, tool-calling, and RAG executors.
|
|
26
|
+
- Zod-driven tool definitions.
|
|
27
|
+
- RAG ingestion, chunking, embedding, retrieval, reranking, context building,
|
|
28
|
+
generation, and verification.
|
|
29
|
+
- In-memory stores for development and tests.
|
|
30
|
+
- Postgres RAG store adapters that work with any compatible `query()` client.
|
|
31
|
+
- Observer events for logging, tracing, and debugging.
|
|
32
|
+
|
|
33
|
+
## Runtime requirements
|
|
34
|
+
|
|
35
|
+
- Node.js 18 or newer.
|
|
36
|
+
- TypeScript is recommended for typed integration.
|
|
37
|
+
- Install optional document conversion dependencies only if you use those
|
|
38
|
+
loaders:
|
|
39
|
+
- HTML: `turndown`, `turndown-plugin-gfm`
|
|
40
|
+
- DOCX: `mammoth`
|
|
41
|
+
- PDF: `pdf-parse`
|
|
42
|
+
|
|
43
|
+
## Environment variables
|
|
44
|
+
|
|
45
|
+
OpenAI:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
OPENAI_API_KEY=...
|
|
49
|
+
OPENAI_MODEL=gpt-4o-mini
|
|
50
|
+
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
OpenAI-compatible local runtime, such as Ollama, LM Studio, vLLM, or a gateway:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
LOCAL_BASE_URL=http://127.0.0.1:11434/v1
|
|
57
|
+
LOCAL_MODEL=llama3
|
|
58
|
+
LOCAL_API_KEY=local-key
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If no OpenAI key is configured, the default `LLM` auto-detection falls back to
|
|
62
|
+
the local OpenAI-compatible provider.
|
|
63
|
+
|
|
64
|
+
## Basic chat integration
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { Agent } from '@fllf/agent-sdk';
|
|
68
|
+
|
|
69
|
+
const agent = new Agent({
|
|
70
|
+
name: 'support-chat',
|
|
71
|
+
systemPrompt: 'You are a concise support assistant.',
|
|
72
|
+
requireSessionId: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const result = await agent.run('How do I reset my password?', {
|
|
76
|
+
sessionId: 'tenant_1:user_42:conversation_1001',
|
|
77
|
+
metadata: {
|
|
78
|
+
tenantId: 'tenant_1',
|
|
79
|
+
userId: 'user_42',
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
console.log(result.output);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Suggested HTTP contract
|
|
87
|
+
|
|
88
|
+
Your API layer owns authentication, authorization, persistence, rate limits, and
|
|
89
|
+
request validation. A typical chat endpoint can adapt incoming requests to
|
|
90
|
+
`Agent.run()`:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { Agent } from '@fllf/agent-sdk';
|
|
94
|
+
|
|
95
|
+
const agent = new Agent({
|
|
96
|
+
name: 'web-chat',
|
|
97
|
+
requireSessionId: true,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export async function handleChatRequest(body: {
|
|
101
|
+
conversationId: string;
|
|
102
|
+
userId: string;
|
|
103
|
+
tenantId: string;
|
|
104
|
+
message: string;
|
|
105
|
+
}) {
|
|
106
|
+
const sessionId = `${body.tenantId}:${body.userId}:${body.conversationId}`;
|
|
107
|
+
|
|
108
|
+
const result = await agent.run(body.message, {
|
|
109
|
+
sessionId,
|
|
110
|
+
metadata: {
|
|
111
|
+
tenantId: body.tenantId,
|
|
112
|
+
userId: body.userId,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
message: result.output,
|
|
118
|
+
usage: result.usage,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Recommended request body:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"conversationId": "conversation_1001",
|
|
128
|
+
"userId": "user_42",
|
|
129
|
+
"tenantId": "tenant_1",
|
|
130
|
+
"message": "How do I reset my password?"
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Recommended response body:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"message": "Open the account settings page and choose Reset password.",
|
|
139
|
+
"usage": {
|
|
140
|
+
"promptTokens": 120,
|
|
141
|
+
"completionTokens": 30,
|
|
142
|
+
"totalTokens": 150
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Sessions and message history
|
|
148
|
+
|
|
149
|
+
`Agent` is reusable. `sessionId` selects a conversation context.
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
await agent.run('Hi', { sessionId: 'tenant_1:user_42:conversation_a' });
|
|
153
|
+
await agent.run('Start another conversation', {
|
|
154
|
+
sessionId: 'tenant_1:user_42:conversation_b',
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Runs from different sessions can proceed concurrently. Runs for the same session
|
|
159
|
+
are serialized so message order remains stable.
|
|
160
|
+
|
|
161
|
+
For production APIs, set `requireSessionId: true` and pass an explicit
|
|
162
|
+
tenant/user/conversation scoped session id. The built-in
|
|
163
|
+
`InMemoryMessageHistoryStore` is suitable for development and tests. Production
|
|
164
|
+
systems should provide a persistent `MessageHistoryStore`.
|
|
165
|
+
|
|
166
|
+
## Tool calling
|
|
167
|
+
|
|
168
|
+
Tools are defined with Zod schemas. The SDK converts the schema into an
|
|
169
|
+
OpenAI-compatible tool schema and validates model-supplied arguments before
|
|
170
|
+
running the tool.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { z } from 'zod';
|
|
174
|
+
import { Agent, Tool, ToolCallingExecutor } from '@fllf/agent-sdk';
|
|
175
|
+
|
|
176
|
+
const AddToolSchema = z.object({
|
|
177
|
+
left: z.number().describe('Left number.'),
|
|
178
|
+
right: z.number().describe('Right number.'),
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
class AddTool extends Tool<typeof AddToolSchema, number> {
|
|
182
|
+
constructor() {
|
|
183
|
+
super({
|
|
184
|
+
name: 'add',
|
|
185
|
+
description: 'Adds two numbers.',
|
|
186
|
+
schema: AddToolSchema,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
protected run(input: z.infer<typeof AddToolSchema>): number {
|
|
191
|
+
return input.left + input.right;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const agent = new Agent({
|
|
196
|
+
name: 'tool-agent',
|
|
197
|
+
executor: new ToolCallingExecutor(),
|
|
198
|
+
tools: [new AddTool()],
|
|
199
|
+
requireSessionId: true,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const result = await agent.run('Calculate 19 + 23 with the tool.', {
|
|
203
|
+
sessionId: 'tenant_1:user_42:conversation_tools',
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
console.log(result.output);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## RAG integration
|
|
210
|
+
|
|
211
|
+
RAG is exposed as an independent subsystem. Agent code only depends on
|
|
212
|
+
`RagPipeline` through either:
|
|
213
|
+
|
|
214
|
+
- `RagExecutor`: always retrieve before answering. Use this for knowledge-base
|
|
215
|
+
QA.
|
|
216
|
+
- `RagSearchTool`: let a tool-calling agent retrieve evidence only when the
|
|
217
|
+
model decides it needs knowledge-base context.
|
|
218
|
+
|
|
219
|
+
### Build an in-memory RAG pipeline
|
|
220
|
+
|
|
221
|
+
This example is suitable for local development, tests, or a minimal MVP. Replace
|
|
222
|
+
the stores with Postgres, pgvector, Qdrant, Elasticsearch, or your own adapters
|
|
223
|
+
for production.
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import {
|
|
227
|
+
AutoChunker,
|
|
228
|
+
AutoDocumentLoader,
|
|
229
|
+
DefaultContextBuilder,
|
|
230
|
+
DefaultDocumentNormalizer,
|
|
231
|
+
DefaultRagGenerator,
|
|
232
|
+
DefaultRagPipeline,
|
|
233
|
+
DenseRetriever,
|
|
234
|
+
HybridRetriever,
|
|
235
|
+
InMemoryDocumentStore,
|
|
236
|
+
InMemoryKeywordStore,
|
|
237
|
+
InMemoryVectorStore,
|
|
238
|
+
OpenAIEmbedder,
|
|
239
|
+
RuleBasedVerifier,
|
|
240
|
+
SparseRetriever,
|
|
241
|
+
} from '@fllf/agent-sdk';
|
|
242
|
+
|
|
243
|
+
const documentStore = new InMemoryDocumentStore();
|
|
244
|
+
const keywordStore = new InMemoryKeywordStore();
|
|
245
|
+
const embedder = new OpenAIEmbedder();
|
|
246
|
+
const vectorStore = new InMemoryVectorStore({
|
|
247
|
+
dimensions: embedder.dimensions,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const denseRetriever = new DenseRetriever({
|
|
251
|
+
embedder,
|
|
252
|
+
vectorStore,
|
|
253
|
+
documentStore,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const sparseRetriever = new SparseRetriever({
|
|
257
|
+
keywordStore,
|
|
258
|
+
documentStore,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
export const ragPipeline = new DefaultRagPipeline({
|
|
262
|
+
loader: new AutoDocumentLoader(),
|
|
263
|
+
normalizer: new DefaultDocumentNormalizer(),
|
|
264
|
+
chunker: new AutoChunker(),
|
|
265
|
+
embedder,
|
|
266
|
+
documentStore,
|
|
267
|
+
vectorStore,
|
|
268
|
+
keywordStore,
|
|
269
|
+
retriever: new HybridRetriever({
|
|
270
|
+
denseRetriever,
|
|
271
|
+
sparseRetriever,
|
|
272
|
+
}),
|
|
273
|
+
contextBuilder: new DefaultContextBuilder({
|
|
274
|
+
maxContextTokens: 3000,
|
|
275
|
+
}),
|
|
276
|
+
generator: new DefaultRagGenerator(),
|
|
277
|
+
verifier: new RuleBasedVerifier(),
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Ingest documents
|
|
282
|
+
|
|
283
|
+
Your application should run ingestion from an admin API, background worker, or
|
|
284
|
+
document synchronization job.
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
await ragPipeline.ingest({
|
|
288
|
+
content: '# Refund policy\n\nRefunds are available within 30 days.',
|
|
289
|
+
source: 'policies/refund.md',
|
|
290
|
+
tenantId: 'tenant_1',
|
|
291
|
+
knowledgeBaseId: 'kb_support',
|
|
292
|
+
acl: ['user:user_42', 'role:support'],
|
|
293
|
+
replaceExisting: true,
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Important ingestion fields:
|
|
298
|
+
|
|
299
|
+
- `source`: stable source path, URL, or document id shown in citations.
|
|
300
|
+
- `tenantId`: tenant boundary.
|
|
301
|
+
- `knowledgeBaseId`: knowledge-base boundary.
|
|
302
|
+
- `acl`: access-control labels used at retrieval time.
|
|
303
|
+
- `replaceExisting`: delete previous chunks and indexes for the same document id
|
|
304
|
+
before writing new ones.
|
|
305
|
+
|
|
306
|
+
### Knowledge-base QA agent
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
import { Agent, RagExecutor } from '@fllf/agent-sdk';
|
|
310
|
+
import { ragPipeline } from './rag-pipeline';
|
|
311
|
+
|
|
312
|
+
const knowledgeAgent = new Agent({
|
|
313
|
+
name: 'knowledge-agent',
|
|
314
|
+
executor: new RagExecutor({
|
|
315
|
+
pipeline: ragPipeline,
|
|
316
|
+
requireCitations: true,
|
|
317
|
+
}),
|
|
318
|
+
requireSessionId: true,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const result = await knowledgeAgent.run('What is the refund window?', {
|
|
322
|
+
sessionId: 'tenant_1:user_42:conversation_1001',
|
|
323
|
+
metadata: {
|
|
324
|
+
tenantId: 'tenant_1',
|
|
325
|
+
knowledgeBaseId: 'kb_support',
|
|
326
|
+
acl: ['user:user_42', 'role:support'],
|
|
327
|
+
topK: 5,
|
|
328
|
+
maxContextTokens: 3000,
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
console.log(result.output);
|
|
333
|
+
console.log(result.raw);
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
`result.raw` is the `RagAnswer`:
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
type RagAnswer = {
|
|
340
|
+
answer: string;
|
|
341
|
+
citations: Array<{
|
|
342
|
+
index: number;
|
|
343
|
+
chunkId: string;
|
|
344
|
+
documentId: string;
|
|
345
|
+
source: string;
|
|
346
|
+
title?: string;
|
|
347
|
+
headingPath?: string[];
|
|
348
|
+
page?: number;
|
|
349
|
+
quote?: string;
|
|
350
|
+
}>;
|
|
351
|
+
confidence: 'high' | 'medium' | 'low';
|
|
352
|
+
retrieved: unknown[];
|
|
353
|
+
usage?: unknown;
|
|
354
|
+
metadata?: Record<string, unknown>;
|
|
355
|
+
};
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Recommended RAG chat response:
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
return {
|
|
362
|
+
message: result.output,
|
|
363
|
+
citations: (result.raw as any).citations,
|
|
364
|
+
confidence: (result.raw as any).confidence,
|
|
365
|
+
usage: result.usage,
|
|
366
|
+
};
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### RAG as an optional search tool
|
|
370
|
+
|
|
371
|
+
Use `RagSearchTool` when the agent should decide whether to search the knowledge
|
|
372
|
+
base.
|
|
373
|
+
|
|
374
|
+
```ts
|
|
375
|
+
import {
|
|
376
|
+
Agent,
|
|
377
|
+
RagSearchTool,
|
|
378
|
+
ToolCallingExecutor,
|
|
379
|
+
} from '@fllf/agent-sdk';
|
|
380
|
+
import { ragPipeline } from './rag-pipeline';
|
|
381
|
+
|
|
382
|
+
const agent = new Agent({
|
|
383
|
+
name: 'general-agent',
|
|
384
|
+
executor: new ToolCallingExecutor(),
|
|
385
|
+
tools: [
|
|
386
|
+
new RagSearchTool({
|
|
387
|
+
pipeline: ragPipeline,
|
|
388
|
+
defaultRetrieveOptions: {
|
|
389
|
+
topK: 5,
|
|
390
|
+
},
|
|
391
|
+
}),
|
|
392
|
+
],
|
|
393
|
+
requireSessionId: true,
|
|
394
|
+
});
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Structured output
|
|
398
|
+
|
|
399
|
+
```ts
|
|
400
|
+
import { z } from 'zod';
|
|
401
|
+
import { LLM } from '@fllf/agent-sdk';
|
|
402
|
+
|
|
403
|
+
const llm = new LLM();
|
|
404
|
+
|
|
405
|
+
const result = await llm.generateObject({
|
|
406
|
+
messages: [
|
|
407
|
+
{ role: 'user', content: 'Return a short project summary.' },
|
|
408
|
+
],
|
|
409
|
+
schema: z.object({
|
|
410
|
+
summary: z.string(),
|
|
411
|
+
confidence: z.number(),
|
|
412
|
+
}),
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
console.log(result.value);
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Observability
|
|
419
|
+
|
|
420
|
+
The SDK emits structured runtime events and does not print by default.
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
import { Agent, ConsoleObserver } from '@fllf/agent-sdk';
|
|
424
|
+
|
|
425
|
+
const agent = new Agent({
|
|
426
|
+
name: 'debug-agent',
|
|
427
|
+
observer: new ConsoleObserver(),
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
await agent.run('Hello');
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Event types include:
|
|
434
|
+
|
|
435
|
+
- `agent:start`
|
|
436
|
+
- `agent:end`
|
|
437
|
+
- `agent:error`
|
|
438
|
+
- `llm:start`
|
|
439
|
+
- `llm:end`
|
|
440
|
+
- `llm:error`
|
|
441
|
+
- `tool:start`
|
|
442
|
+
- `tool:end`
|
|
443
|
+
- `tool:error`
|
|
444
|
+
|
|
445
|
+
Forward events to your logging or tracing system:
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
const observer = {
|
|
449
|
+
onEvent(event) {
|
|
450
|
+
logger.info({ event }, 'agent runtime event');
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
## Production integration checklist
|
|
456
|
+
|
|
457
|
+
The SDK provides runtime primitives. A complete product should add:
|
|
458
|
+
|
|
459
|
+
- Persistent users, conversations, messages, and audit logs.
|
|
460
|
+
- A persistent `MessageHistoryStore`.
|
|
461
|
+
- Persistent RAG stores and a vector/search backend.
|
|
462
|
+
- AuthN/AuthZ before `Agent.run()` or `RagPipeline.retrieve()`.
|
|
463
|
+
- Tenant, knowledge-base, and ACL filters in every RAG request.
|
|
464
|
+
- Background document ingestion and index rebuild jobs.
|
|
465
|
+
- Rate limits, model cost controls, and request timeouts.
|
|
466
|
+
- Redaction or filtering for logs that may contain user messages or document
|
|
467
|
+
snippets.
|
|
468
|
+
|
|
469
|
+
## Public API map
|
|
470
|
+
|
|
471
|
+
All public APIs are exported from the package root:
|
|
472
|
+
|
|
473
|
+
```ts
|
|
474
|
+
import {
|
|
475
|
+
Agent,
|
|
476
|
+
LLM,
|
|
477
|
+
Tool,
|
|
478
|
+
ToolCallingExecutor,
|
|
479
|
+
RagExecutor,
|
|
480
|
+
RagSearchTool,
|
|
481
|
+
DefaultRagPipeline,
|
|
482
|
+
} from '@fllf/agent-sdk';
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
Main areas:
|
|
486
|
+
|
|
487
|
+
- `agent`: runtime composition.
|
|
488
|
+
- `llm`: provider abstraction and structured output.
|
|
489
|
+
- `history`: session-scoped message history interfaces and in-memory store.
|
|
490
|
+
- `tools`: Zod-driven tools and tool execution.
|
|
491
|
+
- `executors`: simple chat, tool calling, and RAG execution strategies.
|
|
492
|
+
- `rag`: ingestion, chunking, embeddings, stores, retrieval, generation, and
|
|
493
|
+
verification.
|
|
494
|
+
- `observability`: observer interface and console observer.
|
|
495
|
+
|
|
496
|
+
## Development
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
npm install
|
|
500
|
+
npm run typecheck
|
|
501
|
+
npm test
|
|
502
|
+
npm run build
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Examples:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
npm run example:simple-chat
|
|
509
|
+
npm run example:tool-calling
|
|
510
|
+
npm run example:local-ollama
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
During local development, examples may import from `../src` so they can run
|
|
514
|
+
without building first.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "@fllf/agent-sdk",
|
|
3
|
-
"version": "0.1.
|
|
2
|
+
"name": "@fllf/agent-sdk",
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A composable TypeScript Agent runtime foundation.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -10,20 +10,20 @@
|
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
-
"files": [
|
|
14
|
-
"dist",
|
|
15
|
-
"README.md"
|
|
16
|
-
],
|
|
17
|
-
"publishConfig": {
|
|
18
|
-
"access": "public",
|
|
19
|
-
"registry": "https://registry.npmjs.org/"
|
|
20
|
-
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsc -p tsconfig.build.json",
|
|
23
|
-
"typecheck": "tsc --noEmit",
|
|
24
|
-
"test": "tsx --test test/**/*.test.ts",
|
|
25
|
-
"prepublishOnly": "npm run typecheck && npm test && npm run build",
|
|
26
|
-
"example:simple-chat": "tsx examples/simple-chat.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public",
|
|
19
|
+
"registry": "https://registry.npmjs.org/"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -p tsconfig.build.json",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"test": "tsx --test test/**/*.test.ts",
|
|
25
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build",
|
|
26
|
+
"example:simple-chat": "tsx examples/simple-chat.ts",
|
|
27
27
|
"example:tool-calling": "tsx examples/tool-calling.ts",
|
|
28
28
|
"example:local-ollama": "tsx examples/local-ollama.ts"
|
|
29
29
|
},
|