@delofarag/ai-utils 1.5.0 → 1.7.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/README.md +648 -97
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +13 -0
- package/dist/client/index.js.map +1 -0
- package/dist/heart/agent.d.ts +6 -6
- package/dist/heart/agent.d.ts.map +1 -1
- package/dist/heart/agent.js +22 -22
- package/dist/heart/agent.js.map +1 -1
- package/dist/heart/chain.d.ts +4 -8
- package/dist/heart/chain.d.ts.map +1 -1
- package/dist/heart/chain.js +1 -1
- package/dist/heart/chain.js.map +1 -1
- package/dist/heart/deepAgent.d.ts +110 -0
- package/dist/heart/deepAgent.d.ts.map +1 -0
- package/dist/heart/deepAgent.js +304 -0
- package/dist/heart/deepAgent.js.map +1 -0
- package/dist/heart/tools/RAGTool.d.ts +3 -7
- package/dist/heart/tools/RAGTool.d.ts.map +1 -1
- package/dist/heart/tools/RAGTool.js +1 -1
- package/dist/heart/tools/RAGTool.js.map +1 -1
- package/dist/heart/tools/Tavily.d.ts +3 -7
- package/dist/heart/tools/Tavily.d.ts.map +1 -1
- package/dist/heart/tools/Tavily.js +1 -1
- package/dist/heart/tools/Tavily.js.map +1 -1
- package/dist/heart/tools/ToolRegistry.d.ts +14 -5
- package/dist/heart/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/heart/tools/ToolRegistry.js +2 -3
- package/dist/heart/tools/ToolRegistry.js.map +1 -1
- package/dist/heart/tools/zodios/CombinedRegistry.js +1 -1
- package/dist/heart/tools/zodios/ZodiosToolRegistry.js +1 -1
- package/dist/helpers/chatbot.d.ts +12 -6
- package/dist/helpers/chatbot.d.ts.map +1 -1
- package/dist/helpers/chatbot.js +45 -7
- package/dist/helpers/chatbot.js.map +1 -1
- package/dist/helpers/deepagent/backend.d.ts +92 -0
- package/dist/helpers/deepagent/backend.d.ts.map +1 -0
- package/dist/helpers/deepagent/backend.js +93 -0
- package/dist/helpers/deepagent/backend.js.map +1 -0
- package/dist/helpers/deepagent/interruptOn.d.ts +93 -0
- package/dist/helpers/deepagent/interruptOn.d.ts.map +1 -0
- package/dist/helpers/deepagent/interruptOn.js +190 -0
- package/dist/helpers/deepagent/interruptOn.js.map +1 -0
- package/dist/helpers/deepagent/interruptTypes.d.ts +65 -0
- package/dist/helpers/deepagent/interruptTypes.d.ts.map +1 -0
- package/dist/helpers/deepagent/interruptTypes.js +2 -0
- package/dist/helpers/deepagent/interruptTypes.js.map +1 -0
- package/dist/helpers/deepagent/permissions.d.ts +58 -0
- package/dist/helpers/deepagent/permissions.d.ts.map +1 -0
- package/dist/helpers/deepagent/permissions.js +72 -0
- package/dist/helpers/deepagent/permissions.js.map +1 -0
- package/dist/helpers/deepagent/sandbox.d.ts +7 -0
- package/dist/helpers/deepagent/sandbox.d.ts.map +1 -0
- package/dist/helpers/deepagent/sandbox.js +8 -0
- package/dist/helpers/deepagent/sandbox.js.map +1 -0
- package/dist/helpers/deepagent/streamEvents.d.ts +10 -0
- package/dist/helpers/deepagent/streamEvents.d.ts.map +1 -0
- package/dist/helpers/deepagent/streamEvents.js +149 -0
- package/dist/helpers/deepagent/streamEvents.js.map +1 -0
- package/dist/helpers/helpers.d.ts +4 -3
- package/dist/helpers/helpers.d.ts.map +1 -1
- package/dist/helpers/helpers.js +25 -0
- package/dist/helpers/helpers.js.map +1 -1
- package/dist/helpers/llms.d.ts +18 -15
- package/dist/helpers/llms.d.ts.map +1 -1
- package/dist/helpers/llms.js +15 -2
- package/dist/helpers/llms.js.map +1 -1
- package/dist/helpers/memory.d.ts +18 -2
- package/dist/helpers/memory.d.ts.map +1 -1
- package/dist/helpers/memory.js +194 -48
- package/dist/helpers/memory.js.map +1 -1
- package/dist/helpers/rag.d.ts +1 -1
- package/dist/imports.d.ts +14 -2
- package/dist/imports.d.ts.map +1 -1
- package/dist/imports.js +15 -2
- package/dist/imports.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/magic-funcs/evaluators/classify.d.ts +3 -11
- package/dist/magic-funcs/evaluators/classify.d.ts.map +1 -1
- package/dist/magic-funcs/evaluators/classify.js +1 -1
- package/dist/magic-funcs/evaluators/decide.d.ts +7 -11
- package/dist/magic-funcs/evaluators/decide.d.ts.map +1 -1
- package/dist/magic-funcs/evaluators/decide.js +1 -1
- package/dist/magic-funcs/parsers/extract.d.ts +2 -2
- package/dist/magic-funcs/parsers/extract.d.ts.map +1 -1
- package/dist/magic-funcs/parsers/extract.js.map +1 -1
- package/dist/magic-funcs/parsers/structure.d.ts +1 -1
- package/dist/magic-funcs/parsers/structure.js.map +1 -1
- package/package.json +28 -13
package/README.md
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Ein praktisches Utility-Package für LLM-Apps mit LangChain:
|
|
4
4
|
|
|
5
|
-
- `Chain`, `
|
|
5
|
+
- `Chain`, `Agent`, `DeepAgent` (Filesystem, HITL, Sandboxes, Subagents)
|
|
6
|
+
- Memory via Checkpoint-Saver (`MemorySaver`, `SmartCheckpointSaver`, `SupabaseCheckpointSaver`)
|
|
6
7
|
- RAG-Helper (FAISS, Supabase, In-Memory)
|
|
7
|
-
- Tooling (`ToolRegistry`, `createRAGTool`, `tavilySearchTool`)
|
|
8
|
+
- Tooling (`ToolRegistry`, `CombinedToolRegistry`, `ZodiosToolRegistry`, `createRAGTool`, `tavilySearchTool`)
|
|
8
9
|
- Magic-Funcs (Parser, Evaluator, Optimizer, Answerer)
|
|
9
10
|
- Modalities (STT, TTS, Vision, Image Generation)
|
|
10
11
|
|
|
@@ -19,7 +20,7 @@ Im Package gilt als Standard-LLM-Default für die allgemeine Nutzung:
|
|
|
19
20
|
|
|
20
21
|
Wenn du nichts explizit setzt, orientiere dich an diesem Default in deinen Aufrufen.
|
|
21
22
|
|
|
22
|
-
Für modality-spezifische Flows (`stt`, `tts`, `vision`, `image-gen`) wird zusätzlich mit `type` gearbeitet, damit passende Modelle gewählt werden
|
|
23
|
+
Für modality-spezifische Flows (`stt`, `tts`, `vision`, `image-gen`) wird zusätzlich mit `type` gearbeitet, damit passende Modelle gewählt werden können.
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -29,6 +30,12 @@ Für modality-spezifische Flows (`stt`, `tts`, `vision`, `image-gen`) wird zusä
|
|
|
29
30
|
npm i @delofarag/ai-utils
|
|
30
31
|
```
|
|
31
32
|
|
|
33
|
+
Peer dependency für Supabase-Features:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm i @delofarag/supabase-utils
|
|
37
|
+
```
|
|
38
|
+
|
|
32
39
|
---
|
|
33
40
|
|
|
34
41
|
## Environment Variables
|
|
@@ -44,12 +51,12 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=...
|
|
|
44
51
|
SUPABASE_SERVICE_ROLE_KEY=...
|
|
45
52
|
```
|
|
46
53
|
|
|
47
|
-
### Welche Variable
|
|
54
|
+
### Welche Variable wofür?
|
|
48
55
|
|
|
49
56
|
- `OPENROUTER_API_KEY`: OpenRouter-Modelle und Modalities
|
|
50
57
|
- `CHATGROQ_API_KEY`: wenn du `provider: "chatgroq"` nutzt
|
|
51
58
|
- `TAVILY_API_KEY`: `websearch()` / `tavilySearchTool`
|
|
52
|
-
- Supabase-Variablen:
|
|
59
|
+
- Supabase-Variablen: für `createSupabaseVectoreStore()`, `getSupabaseVectorStore()` und `SupabaseCheckpointSaver`
|
|
53
60
|
|
|
54
61
|
---
|
|
55
62
|
|
|
@@ -78,19 +85,27 @@ getLLM({ provider: "openrouter", type: "vision" })
|
|
|
78
85
|
getLLM({ provider: "openrouter", type: "image-gen" })
|
|
79
86
|
```
|
|
80
87
|
|
|
88
|
+
EU-Datenrouting (OpenRouter):
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
getLLM({ provider: "openrouter", dataSafe: true })
|
|
92
|
+
```
|
|
93
|
+
|
|
81
94
|
---
|
|
82
95
|
|
|
83
96
|
## Core Classes
|
|
84
97
|
|
|
98
|
+
> **DeepAgent-Dokumentation:** Abschnitt [3) DeepAgent](#3-deepagent) — Feature-Übersicht, Backend, HITL, Stream-Chunks, CLI-Testing.
|
|
99
|
+
|
|
85
100
|
## 1) `Chain`
|
|
86
101
|
|
|
87
|
-
Stateless LLM-Chain
|
|
102
|
+
Stateless LLM-Chain für strukturierte Ergebnisse. Kein Memory — `thread_id` wird ignoriert.
|
|
88
103
|
|
|
89
|
-
###
|
|
104
|
+
### Custom Output Schema
|
|
90
105
|
|
|
91
106
|
```ts
|
|
92
107
|
import { Chain, getLLM } from "@delofarag/ai-utils"
|
|
93
|
-
import { z } from "zod/
|
|
108
|
+
import { z } from "zod/v4"
|
|
94
109
|
|
|
95
110
|
const productBriefSchema = z.object({
|
|
96
111
|
title: z.string().describe("Kurzer Produktname"),
|
|
@@ -106,7 +121,7 @@ const chain = new Chain({
|
|
|
106
121
|
})
|
|
107
122
|
|
|
108
123
|
const result = await chain.invoke({
|
|
109
|
-
product: "AI-Notizapp
|
|
124
|
+
product: "AI-Notizapp für Teams",
|
|
110
125
|
market: "DACH SaaS"
|
|
111
126
|
})
|
|
112
127
|
```
|
|
@@ -115,7 +130,7 @@ const result = await chain.invoke({
|
|
|
115
130
|
|
|
116
131
|
```ts
|
|
117
132
|
import { Chain, createFaissStore } from "@delofarag/ai-utils"
|
|
118
|
-
import { z } from "zod/
|
|
133
|
+
import { z } from "zod/v4"
|
|
119
134
|
|
|
120
135
|
const vectorStore = await createFaissStore(["Dokument A", "Dokument B"])
|
|
121
136
|
|
|
@@ -130,121 +145,560 @@ await chain.addContext(["Dokument C"])
|
|
|
130
145
|
const answer = await chain.invoke({ question: "Was steht in Dokument C?" })
|
|
131
146
|
```
|
|
132
147
|
|
|
148
|
+
### Streaming
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
for await (const chunk of chain.stream({ question: "Erkläre das kurz." })) {
|
|
152
|
+
process.stdout.write(chunk)
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
133
156
|
---
|
|
134
157
|
|
|
135
|
-
## 2) `
|
|
158
|
+
## 2) `Agent`
|
|
136
159
|
|
|
137
|
-
|
|
160
|
+
Tool-using Agent auf Basis von `createReactAgent`. Unterstützt optional Checkpointer (Thread-State) und strukturierten Output.
|
|
138
161
|
|
|
139
162
|
### Basis
|
|
140
163
|
|
|
141
164
|
```ts
|
|
142
|
-
import {
|
|
165
|
+
import { Agent, ToolRegistry, getLLM } from "@delofarag/ai-utils"
|
|
166
|
+
import { z } from "zod/v4"
|
|
167
|
+
|
|
168
|
+
const tools = new ToolRegistry([
|
|
169
|
+
{
|
|
170
|
+
name: "sum",
|
|
171
|
+
description: "Addiert zwei Zahlen",
|
|
172
|
+
schema: z.object({ a: z.number(), b: z.number() }),
|
|
173
|
+
func: ({ a, b }) => a + b
|
|
174
|
+
}
|
|
175
|
+
]).allTools
|
|
143
176
|
|
|
144
|
-
const
|
|
177
|
+
const agent = new Agent({
|
|
145
178
|
llm: getLLM({ provider: "openrouter", model: "openai/gpt-5.4-mini" }),
|
|
146
|
-
prompt: "Du
|
|
179
|
+
prompt: "Du darfst Tools nutzen wenn nötig.",
|
|
180
|
+
tools
|
|
147
181
|
})
|
|
148
182
|
|
|
149
|
-
await
|
|
150
|
-
const r2 = await memoryChain.invoke({ thread_id: "u1", input: "Wie heisse ich?" })
|
|
183
|
+
const result = await agent.invoke({ input: "Was ist 8 + 13?" })
|
|
151
184
|
```
|
|
152
185
|
|
|
153
|
-
###
|
|
186
|
+
### Checkpointer mit `Agent`
|
|
187
|
+
|
|
188
|
+
Conversation State läuft über Checkpoint-Saver + `thread_id`:
|
|
154
189
|
|
|
155
190
|
```ts
|
|
156
|
-
import {
|
|
157
|
-
|
|
191
|
+
import { Agent, MemorySaver, SmartCheckpointSaver, getLLM } from "@delofarag/ai-utils"
|
|
192
|
+
|
|
193
|
+
const checkpointer = new SmartCheckpointSaver(new MemorySaver(), {
|
|
194
|
+
llm: getLLM({ provider: "openrouter" }),
|
|
195
|
+
messagesBeforeSummary: 12,
|
|
196
|
+
maxSummaries: 7
|
|
197
|
+
})
|
|
158
198
|
|
|
159
|
-
const
|
|
199
|
+
const agent = new Agent({
|
|
200
|
+
tools: [...],
|
|
201
|
+
prompt: "Du bist ein hilfreicher Assistent.",
|
|
202
|
+
checkpointer
|
|
203
|
+
})
|
|
160
204
|
|
|
161
|
-
|
|
162
|
-
|
|
205
|
+
await agent.invoke({ thread_id: "u1", input: "Ich heisse Max." })
|
|
206
|
+
const r2 = await agent.invoke({ thread_id: "u1", input: "Wie heisse ich?" })
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Strukturierter Output
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
const agent = new Agent({
|
|
213
|
+
tools,
|
|
214
|
+
output: z.object({
|
|
215
|
+
answer: z.string(),
|
|
216
|
+
confidence: z.number()
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
const result = await agent.invoke({ input: "Analysiere das..." })
|
|
221
|
+
// result: { answer: string, confidence: number }
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### RAG als Tool
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
import { Agent, createRAGTool, createFaissStore } from "@delofarag/ai-utils"
|
|
228
|
+
|
|
229
|
+
const vectorStore = await createFaissStore(["Release Notes 2026-04", "Known Issues"])
|
|
230
|
+
const ragTool = createRAGTool({
|
|
163
231
|
vectorStore,
|
|
164
|
-
|
|
232
|
+
name: "search_docs",
|
|
233
|
+
description: "Sucht relevante Produktdokumente"
|
|
165
234
|
})
|
|
166
235
|
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
236
|
+
const agent = new Agent({
|
|
237
|
+
prompt: "Nutze search_docs für faktenbasierte Antworten.",
|
|
238
|
+
tools: [ragTool]
|
|
170
239
|
})
|
|
171
240
|
```
|
|
172
241
|
|
|
242
|
+
### Streaming
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
for await (const chunk of agent.stream({ input: "Erkläre mir das.", thread_id: "u1" })) {
|
|
246
|
+
process.stdout.write(chunk)
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
173
250
|
---
|
|
174
251
|
|
|
175
|
-
## 3) `
|
|
252
|
+
## 3) `DeepAgent`
|
|
253
|
+
|
|
254
|
+
LangChain Deep Agent (`createDeepAgent()`) als typisierte Wrapper-Klasse. Für autonome Tasks mit Filesystem, Planning, Subagents und optional Sandboxes.
|
|
255
|
+
|
|
256
|
+
### DeepAgent auf einen Blick
|
|
257
|
+
|
|
258
|
+
| Feature | Kurz erklärt |
|
|
259
|
+
|---|---|
|
|
260
|
+
| **Filesystem-Tools** | `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep` (built-in) |
|
|
261
|
+
| **Custom Tools** | Eigene Tools via `ToolRegistry` / `DynamicStructuredTool` |
|
|
262
|
+
| **`execute`** | Shell-Befehle — nur mit Shell/Sandbox-Backend (`createLocalShellBackend`, Deno, Daytona) |
|
|
263
|
+
| **`backend`** | Wo Dateien physisch liegen + wie der Agent Pfade sieht |
|
|
264
|
+
| **`permissions`** | Statische FS-Regeln (allow/deny) — **kein** User-Dialog |
|
|
265
|
+
| **`interruptOn`** | Human-in-the-Loop — pausiert **vor** Tool-Ausführung, User entscheidet |
|
|
266
|
+
| **`checkpointer` + `thread_id`** | Conversation-State über Runs hinweg (Pflicht für HITL) |
|
|
267
|
+
| **`invoke` / `stream`** | Gleiche API für Message **und** HITL-Resume via `decision` |
|
|
268
|
+
| **`showToolCalls`** | Streamt `[tool:start]` / `[tool:end]` Events (nur `stream()`) |
|
|
269
|
+
| **`agentsMd`** | AGENTS.md-Dateien als Startup-Kontext |
|
|
270
|
+
| **`subagents`** | Delegation an spezialisierte Sub-Agents |
|
|
271
|
+
| **`skills`** | Skill-Dateien vom Backend laden |
|
|
272
|
+
| **`output`** | Optional strukturierter Zod-Output |
|
|
273
|
+
|
|
274
|
+
### Wann `DeepAgent` statt `Agent`?
|
|
275
|
+
|
|
276
|
+
| | `Agent` | `DeepAgent` |
|
|
277
|
+
|---|---|---|
|
|
278
|
+
| Tool-using ReAct | ja | ja |
|
|
279
|
+
| Built-in Filesystem | nein | ja |
|
|
280
|
+
| Backend / Sandbox | nein | ja |
|
|
281
|
+
| Subagents / Skills | nein | ja |
|
|
282
|
+
| HITL (`interruptOn`) | nein | ja |
|
|
283
|
+
| Typischer Use-Case | Chatbots, API-Tools | Coding Agents, autonome Tasks |
|
|
176
284
|
|
|
177
|
-
|
|
285
|
+
---
|
|
178
286
|
|
|
179
|
-
###
|
|
287
|
+
### Alle Props
|
|
288
|
+
|
|
289
|
+
`DeepAgent` wrappt intern LangChains `createDeepAgent()`. Die Tabelle unten listet die Props, die du über `@delofarag/ai-utils` setzt — für den **vollen Einblick aller nativen Parameter** (z. B. `cache`, `debug`, Middleware-Details, Backend-Protokolle) in die **LangChain Deep Agents Docs** schauen:
|
|
290
|
+
|
|
291
|
+
- [Deep Agents Overview](https://docs.langchain.com/oss/javascript/deepagents/overview)
|
|
292
|
+
- [`createDeepAgent()` Reference (JS)](https://reference.langchain.com/javascript/deepagents/agent/createDeepAgent)
|
|
293
|
+
|
|
294
|
+
Einige Namen weichen in ai-utils ab: `prompt` → `systemPrompt`, `agentsMd` → `memory`, `interruptOn` → `interrupt_on`.
|
|
180
295
|
|
|
181
296
|
```ts
|
|
182
|
-
|
|
183
|
-
|
|
297
|
+
new DeepAgent({
|
|
298
|
+
prompt, // string | string[] — System-Prompt(s)
|
|
299
|
+
llm, // BaseChatModel (Default: OpenRouter gpt-5.4-mini)
|
|
300
|
+
tools, // readonly DynamicStructuredTool[] (z.B. ToolRegistry.allTools)
|
|
301
|
+
output, // Zod-Schema für strukturierten Output
|
|
302
|
+
checkpointer, // BaseCheckpointSaver | boolean (Default: MemorySaver)
|
|
303
|
+
backend, // DeepAgentBackend — siehe Backend-Abschnitt
|
|
304
|
+
permissions, // FilesystemPermission[] — statische FS-Regeln
|
|
305
|
+
interruptOn, // pro Tool: decisions + question (HITL)
|
|
306
|
+
agentsMd, // string[] — Pfade zu AGENTS.md
|
|
307
|
+
subagents, // SubAgent[]
|
|
308
|
+
skills, // string[] — Skill-Pfade relativ zum Backend
|
|
309
|
+
middleware, // AgentMiddleware[]
|
|
310
|
+
store, // BaseStore — LangGraph Store
|
|
311
|
+
name, // Agent-Name
|
|
312
|
+
contextSchema, // Runtime-Context-Schema
|
|
313
|
+
})
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Wichtig:** `backend` erwartet eine **Instanz** (oder Factory) — kein `Promise`. Async Backends vorher mit `await` erstellen:
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
// ❌ backend: createLocalShellBackend({ ... })
|
|
320
|
+
// ✅ backend: await createLocalShellBackend({ ... })
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Typ: `DeepAgentBackend` (= native deepagents `AnyBackendProtocol | Factory`).
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
### Basis: Coding Agent mit isoliertem Workspace
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
import path from "node:path"
|
|
331
|
+
import {
|
|
332
|
+
DeepAgent,
|
|
333
|
+
createLocalShellBackend,
|
|
334
|
+
workspacePermissions,
|
|
335
|
+
ToolRegistry,
|
|
336
|
+
MemorySaver,
|
|
337
|
+
getLLM,
|
|
338
|
+
} from "@delofarag/ai-utils"
|
|
339
|
+
import { z } from "zod/v4"
|
|
184
340
|
|
|
185
341
|
const tools = new ToolRegistry([
|
|
186
342
|
{
|
|
187
|
-
name: "
|
|
188
|
-
description: "
|
|
189
|
-
schema: z.object({
|
|
190
|
-
func: ({
|
|
191
|
-
}
|
|
343
|
+
name: "get_weather",
|
|
344
|
+
description: "Wetter für eine Stadt",
|
|
345
|
+
schema: z.object({ city: z.string() }),
|
|
346
|
+
func: async ({ city }) => `${city}: sonnig`,
|
|
347
|
+
},
|
|
192
348
|
]).allTools
|
|
193
349
|
|
|
194
|
-
const agent = new
|
|
350
|
+
const agent = new DeepAgent({
|
|
195
351
|
llm: getLLM({ provider: "openrouter", model: "openai/gpt-5.4-mini" }),
|
|
196
|
-
prompt: "Du
|
|
197
|
-
tools
|
|
352
|
+
prompt: "Du bist ein Coding Agent. Arbeite nur im Workspace.",
|
|
353
|
+
tools,
|
|
354
|
+
checkpointer: new MemorySaver(),
|
|
355
|
+
backend: await createLocalShellBackend({
|
|
356
|
+
rootDir: path.join(process.cwd(), "coding_space"),
|
|
357
|
+
route: "/workspace/",
|
|
358
|
+
}),
|
|
359
|
+
permissions: workspacePermissions("/workspace/"),
|
|
198
360
|
})
|
|
199
361
|
|
|
200
|
-
const
|
|
362
|
+
const answer = await agent.invoke({
|
|
363
|
+
input: "Erstelle eine kleine HTML-Seite",
|
|
364
|
+
thread_id: "session-1",
|
|
365
|
+
})
|
|
201
366
|
```
|
|
202
367
|
|
|
203
|
-
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
### Backend: `rootDir` vs `route`
|
|
371
|
+
|
|
372
|
+
Zwei getrennte Konzepte — oft verwechselt:
|
|
373
|
+
|
|
374
|
+
| Prop | Bedeutung | Beispiel |
|
|
375
|
+
|---|---|---|
|
|
376
|
+
| **`rootDir`** | Echter Ordner auf der Festplatte | `"./coding_space"` oder absoluter Pfad |
|
|
377
|
+
| **`route`** | Virtueller Pfad, den der **Agent sieht** | `"/workspace/"` |
|
|
378
|
+
|
|
379
|
+
Mapping:
|
|
380
|
+
|
|
381
|
+
```
|
|
382
|
+
Agent schreibt: /workspace/index.html
|
|
383
|
+
↓
|
|
384
|
+
Physisch auf Disk: coding_space/index.html
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Der Agent kennt `coding_space` nicht — nur `/workspace/`. Du entscheidest mit `rootDir`, wo Dateien wirklich landen.
|
|
388
|
+
|
|
389
|
+
**Relative Pfade für `rootDir` funktionieren** — deepagents resolved sie via `path.resolve(rootDir)`.
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
### Backend-Helper
|
|
394
|
+
|
|
395
|
+
| Helper | Async? | `execute`? | Wofür |
|
|
396
|
+
|---|---|---|---|
|
|
397
|
+
| `createStateBackend()` | nein | nein | Ephemeral — Dateien nur im Agent-State |
|
|
398
|
+
| `createFilesystemBackend({ rootDir })` | nein | nein | Direktes FS ohne StateBackend |
|
|
399
|
+
| `createWorkspaceBackend({ rootDir, route? })` | nein | nein | StateBackend + FS unter `route` (empfohlen für reines Coden) |
|
|
400
|
+
| `createLocalShellBackend({ rootDir, route? })` | **ja** | **ja** | Wie Workspace + Shell auf Host (**nur Dev**) |
|
|
401
|
+
| `createDenoSandbox()` | **ja** | ja | Isolierte Deno-Sandbox |
|
|
402
|
+
| `createDaytonaSandbox()` | **ja** | ja | Isolierte Daytona-Sandbox |
|
|
204
403
|
|
|
205
404
|
```ts
|
|
206
|
-
|
|
405
|
+
// Reines Filesystem (kein Shell)
|
|
406
|
+
backend: createWorkspaceBackend({
|
|
407
|
+
rootDir: path.join(process.cwd(), "coding_space"),
|
|
408
|
+
route: "/workspace/",
|
|
409
|
+
})
|
|
207
410
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
description: "Sucht relevante Produktdokumente"
|
|
411
|
+
// Mit Shell (Dev only — execute läuft auf dem Host!)
|
|
412
|
+
backend: await createLocalShellBackend({
|
|
413
|
+
rootDir: path.join(process.cwd(), "coding_space"),
|
|
414
|
+
route: "/workspace/",
|
|
213
415
|
})
|
|
416
|
+
```
|
|
214
417
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
### `permissions` vs `interruptOn`
|
|
421
|
+
|
|
422
|
+
Zwei verschiedene Sicherheitsmechanismen:
|
|
423
|
+
|
|
424
|
+
| | `permissions` | `interruptOn` |
|
|
425
|
+
|---|---|---|
|
|
426
|
+
| **Was** | Statische Regeln | User-Dialog vor Tool-Ausführung |
|
|
427
|
+
| **Wann** | Sofort beim Tool-Call | Pause + warte auf Entscheidung |
|
|
428
|
+
| **Tools** | Nur FS: `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep` | Alle konfigurierten Tools inkl. Custom + `execute` |
|
|
429
|
+
| **`execute`** | Wird **nicht** enforced | Kann konfiguriert werden |
|
|
430
|
+
|
|
431
|
+
Helper für `permissions`:
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
import { workspacePermissions, allowRead, denyWrite } from "@delofarag/ai-utils"
|
|
435
|
+
|
|
436
|
+
permissions: workspacePermissions("/workspace/")
|
|
437
|
+
// = lesen + schreiben nur unter /workspace/**, sonst deny
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
### Built-in Filesystem-Tools
|
|
443
|
+
|
|
444
|
+
DeepAgent bringt diese Tools automatisch mit (via deepagents Middleware):
|
|
445
|
+
|
|
446
|
+
- `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep`
|
|
447
|
+
- `execute` — nur wenn Backend Shell/Sandbox unterstützt
|
|
448
|
+
|
|
449
|
+
Custom Tools aus `ToolRegistry` kommen dazu. Für `interruptOn`-Autocomplete werden alle verfügbaren Tool-Namen typisiert.
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
### `invoke()` — Message und HITL-Resume in einer API
|
|
454
|
+
|
|
455
|
+
**Normale Message:**
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
const result = await agent.invoke({
|
|
459
|
+
input: "Analysiere die Codebase",
|
|
460
|
+
thread_id: "u1",
|
|
461
|
+
})
|
|
462
|
+
// result: string (oder Zod-Output wenn output gesetzt)
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
**Nach Interrupt — Resume:**
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
let result = await agent.invoke({ input: "Schreib eine Datei", thread_id: "u1" })
|
|
469
|
+
|
|
470
|
+
if (typeof result === "object" && result.kind === "interrupt") {
|
|
471
|
+
result = await agent.invoke({
|
|
472
|
+
thread_id: "u1",
|
|
473
|
+
decision: "approve",
|
|
474
|
+
})
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
**Entscheidungs-Typen (`DeepAgentUserDecision`):**
|
|
479
|
+
|
|
480
|
+
```ts
|
|
481
|
+
"approve"
|
|
482
|
+
"reject"
|
|
483
|
+
{ type: "reject", message: "Zu gefährlich" }
|
|
484
|
+
{ type: "edit", args: { file_path: "/workspace/safe.txt", content: "..." } }
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
**Mehrere parallele Tool-Calls (Batch-Interrupt):**
|
|
488
|
+
|
|
489
|
+
```ts
|
|
490
|
+
await agent.invoke({
|
|
491
|
+
thread_id: "u1",
|
|
492
|
+
decisions: ["approve", "approve"],
|
|
493
|
+
})
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
Regeln:
|
|
497
|
+
- `thread_id` **Pflicht** wenn `checkpointer` gesetzt
|
|
498
|
+
- `decision`/`decisions` und `input` **nicht gleichzeitig**
|
|
499
|
+
- `checkpointer` **Pflicht** für HITL-Resume
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
### Human-in-the-Loop (`interruptOn`)
|
|
504
|
+
|
|
505
|
+
Pausiert den Agent **vor** Tool-Ausführung. Tools die **nicht** im Objekt stehen, laufen ohne Pause.
|
|
506
|
+
|
|
507
|
+
```ts
|
|
508
|
+
const agent = new DeepAgent({
|
|
509
|
+
checkpointer: new MemorySaver(),
|
|
510
|
+
tools: myTools,
|
|
511
|
+
interruptOn: {
|
|
512
|
+
write_file: {
|
|
513
|
+
decisions: ["approve", "reject"],
|
|
514
|
+
question: "Datei schreiben?",
|
|
515
|
+
},
|
|
516
|
+
edit_file: {
|
|
517
|
+
decisions: ["approve", "edit", "reject"],
|
|
518
|
+
question: (call) =>
|
|
519
|
+
`Editieren?\nPfad: ${call.args.file_path ?? "?"}`,
|
|
520
|
+
},
|
|
521
|
+
get_weather: {
|
|
522
|
+
decisions: ["approve", "reject"],
|
|
523
|
+
question: (call) => `Wetter für ${call.args.city} abrufen?`,
|
|
524
|
+
},
|
|
525
|
+
execute: {
|
|
526
|
+
decisions: ["approve", "reject"],
|
|
527
|
+
question: "Shell-Befehl ausführen?",
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
})
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
**Config pro Tool:**
|
|
534
|
+
|
|
535
|
+
| Feld | Typ | Beschreibung |
|
|
536
|
+
|---|---|---|
|
|
537
|
+
| `decisions` | `("approve" \| "edit" \| "reject")[]` | Erlaubte User-Antworten |
|
|
538
|
+
| `question` | `string \| (toolCall) => string` | Frage an den User (statisch oder dynamisch) |
|
|
539
|
+
|
|
540
|
+
**Typisierte Keys (Autocomplete):** alle FS-Tools + deine Custom-Tools + `execute` (wenn Shell-Backend). Mit `interruptOn` gesetzt liefert `invoke()`/`stream()` zusätzlich `DeepAgentInterrupt`-Chunks.
|
|
541
|
+
|
|
542
|
+
HITL-Helper (optional, low-level):
|
|
543
|
+
|
|
544
|
+
```ts
|
|
545
|
+
import {
|
|
546
|
+
approveDecision,
|
|
547
|
+
rejectDecision,
|
|
548
|
+
editDecision,
|
|
549
|
+
approveAll,
|
|
550
|
+
createResumeCommand,
|
|
551
|
+
isInterruptResult,
|
|
552
|
+
mapResultToInterrupt,
|
|
553
|
+
} from "@delofarag/ai-utils"
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
### `stream()` — Text, Interrupts und Tool-Events
|
|
559
|
+
|
|
560
|
+
Gleiche Input-API wie `invoke()` — plus optionale Tool-Events:
|
|
561
|
+
|
|
562
|
+
```ts
|
|
563
|
+
for await (const chunk of agent.stream({
|
|
564
|
+
input: "Baue eine Website",
|
|
565
|
+
thread_id: "u1",
|
|
566
|
+
showToolCalls: true,
|
|
567
|
+
})) {
|
|
568
|
+
if (typeof chunk === "string") {
|
|
569
|
+
process.stdout.write(chunk)
|
|
570
|
+
} else if (chunk.kind === "interrupt") {
|
|
571
|
+
console.log("Interrupt:", chunk.question)
|
|
572
|
+
console.log("Decisions:", chunk.decisions)
|
|
573
|
+
} else if (chunk.kind === "tool") {
|
|
574
|
+
console.log(`[tool:${chunk.phase}]`, chunk.toolName)
|
|
221
575
|
}
|
|
222
|
-
|
|
576
|
+
}
|
|
577
|
+
```
|
|
223
578
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
579
|
+
**Stream-Chunk-Typen:**
|
|
580
|
+
|
|
581
|
+
| Chunk | Wann | Shape |
|
|
582
|
+
|---|---|---|
|
|
583
|
+
| `string` | LLM-Text-Tokens | `"Hallo..."` |
|
|
584
|
+
| `DeepAgentInterrupt` | HITL-Pause | `{ kind: "interrupt", question, decisions, toolName?, args? }` |
|
|
585
|
+
| `DeepAgentInterruptBatch` | Mehrere Tools gleichzeitig | `{ kind: "interrupt", items: [...] }` |
|
|
586
|
+
| `DeepAgentToolEvent` | Mit `showToolCalls: true` | `{ kind: "tool", phase: "start"\|"end", toolName, args? }` |
|
|
587
|
+
|
|
588
|
+
**Resume im Stream:**
|
|
589
|
+
|
|
590
|
+
```ts
|
|
591
|
+
for await (const chunk of agent.stream({
|
|
592
|
+
thread_id: "u1",
|
|
593
|
+
decision: "approve",
|
|
594
|
+
showToolCalls: true,
|
|
595
|
+
})) { ... }
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
Frontend ohne LangChain-Bundle:
|
|
599
|
+
|
|
600
|
+
```ts
|
|
601
|
+
import { isInterrupt, isToolEvent } from "@delofarag/ai-utils/client"
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
### `agentsMd` vs `checkpointer`
|
|
607
|
+
|
|
608
|
+
| Prop | LangChain-Parameter | Bedeutung |
|
|
609
|
+
|---|---|---|
|
|
610
|
+
| `checkpointer` | `checkpointer` | Thread-State über `thread_id` (Chat-Verlauf) |
|
|
611
|
+
| `agentsMd` | `memory` | AGENTS.md-Pfade als Startup-Kontext (kein Chat-Memory) |
|
|
612
|
+
|
|
613
|
+
```ts
|
|
614
|
+
const agent = new DeepAgent({
|
|
615
|
+
agentsMd: ["./AGENTS.md", "./.deepagents/AGENTS.md"],
|
|
616
|
+
checkpointer: new MemorySaver(),
|
|
617
|
+
})
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
### Skills
|
|
623
|
+
|
|
624
|
+
Skill-Dateien vom Backend laden (deepagents native Feature):
|
|
625
|
+
|
|
626
|
+
```ts
|
|
627
|
+
const agent = new DeepAgent({
|
|
628
|
+
backend: await createLocalShellBackend({ rootDir: "./coding_space" }),
|
|
629
|
+
skills: ["./skills/refactor/SKILL.md", "./skills/test/SKILL.md"],
|
|
630
|
+
})
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
Pfade relativ zum Backend — der Agent kann Skills zur Laufzeit einlesen.
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
### Sandbox (Production)
|
|
638
|
+
|
|
639
|
+
```ts
|
|
640
|
+
import { DeepAgent, createDenoSandbox } from "@delofarag/ai-utils"
|
|
641
|
+
|
|
642
|
+
const sandbox = await createDenoSandbox()
|
|
643
|
+
const agent = new DeepAgent({
|
|
644
|
+
backend: sandbox,
|
|
645
|
+
permissions: workspacePermissions("/workspace/"),
|
|
227
646
|
})
|
|
647
|
+
|
|
648
|
+
// sandbox.close() wenn fertig
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
### Runtime-Methoden
|
|
654
|
+
|
|
655
|
+
```ts
|
|
656
|
+
agent.addTool(extraTool) // Tool nachträglich hinzufügen
|
|
657
|
+
agent.currentTools // string[] — alle registrierten Tool-Namen
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
### API-Route Pattern (NDJSON Stream)
|
|
663
|
+
|
|
664
|
+
```ts
|
|
665
|
+
import { StreamResponse } from "@delofarag/ai-utils"
|
|
666
|
+
import { isInterrupt } from "@delofarag/ai-utils/client"
|
|
667
|
+
|
|
668
|
+
// Gleiche stream()-Methode für Message und HITL-Resume
|
|
669
|
+
return StreamResponse(
|
|
670
|
+
agent.stream({
|
|
671
|
+
thread_id,
|
|
672
|
+
...(decision ? { decision } : { input: message }),
|
|
673
|
+
showToolCalls: true,
|
|
674
|
+
})
|
|
675
|
+
)
|
|
228
676
|
```
|
|
229
677
|
|
|
678
|
+
Response: `Content-Type: application/x-ndjson` — ein JSON-Objekt pro Zeile.
|
|
679
|
+
|
|
230
680
|
---
|
|
231
681
|
|
|
232
|
-
## Tool Registry
|
|
682
|
+
## Tool Registry
|
|
683
|
+
|
|
684
|
+
### `ToolRegistry`
|
|
685
|
+
|
|
686
|
+
Konvertiert einfache Tool-Definitionen zu `DynamicStructuredTool`:
|
|
233
687
|
|
|
234
|
-
`
|
|
688
|
+
- `getTool(name)` — wirft `Error` wenn Tool nicht existiert (kein `undefined`)
|
|
689
|
+
- `getTools(...names)` — mehrere Tools, wirft bei unbekanntem Namen
|
|
690
|
+
- `allTools` — alle Tools als Array
|
|
235
691
|
|
|
236
|
-
- `
|
|
237
|
-
- `getTools(...names)`
|
|
238
|
-
- `allTools`
|
|
692
|
+
Tool-Namen werden als Literal-Typen erhalten — wichtig für `interruptOn`-Autocomplete bei `new DeepAgent()`.
|
|
239
693
|
|
|
240
694
|
```ts
|
|
241
695
|
import { ToolRegistry } from "@delofarag/ai-utils"
|
|
242
|
-
import { z } from "zod/
|
|
696
|
+
import { z } from "zod/v4"
|
|
243
697
|
|
|
244
698
|
const registry = new ToolRegistry([
|
|
245
699
|
{
|
|
246
700
|
name: "get_weather",
|
|
247
|
-
description: "Liefert Wetter
|
|
701
|
+
description: "Liefert Wetter für eine Stadt",
|
|
248
702
|
schema: z.object({ city: z.string() }),
|
|
249
703
|
func: async ({ city }) => `${city}: sonnig`
|
|
250
704
|
},
|
|
@@ -260,7 +714,28 @@ const weatherTool = registry.getTool("get_weather")
|
|
|
260
714
|
const tools = registry.allTools
|
|
261
715
|
```
|
|
262
716
|
|
|
263
|
-
###
|
|
717
|
+
### `CombinedToolRegistry` + `ZodiosToolRegistry`
|
|
718
|
+
|
|
719
|
+
Kombiniert manuelle Tools mit einem Zodios-API-Client (max. 1 Client):
|
|
720
|
+
|
|
721
|
+
```ts
|
|
722
|
+
import { CombinedToolRegistry } from "@delofarag/ai-utils"
|
|
723
|
+
import { Zodios } from "zodios"
|
|
724
|
+
|
|
725
|
+
const registry = new CombinedToolRegistry([
|
|
726
|
+
{
|
|
727
|
+
name: "get_weather",
|
|
728
|
+
description: "Wetter abfragen",
|
|
729
|
+
schema: z.object({ city: z.string() }),
|
|
730
|
+
func: async ({ city }) => `${city}: sonnig`
|
|
731
|
+
},
|
|
732
|
+
myZodiosClient
|
|
733
|
+
] as const)
|
|
734
|
+
|
|
735
|
+
const agent = new Agent({ tools: registry.allTools })
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### Tavily
|
|
264
739
|
|
|
265
740
|
```ts
|
|
266
741
|
import { tavilySearchTool, TavilySearch } from "@delofarag/ai-utils"
|
|
@@ -277,14 +752,16 @@ const response = await tavily.invoke({ query: "latest AI regulation EU" })
|
|
|
277
752
|
|
|
278
753
|
---
|
|
279
754
|
|
|
280
|
-
## Memory
|
|
755
|
+
## Memory (Checkpoint-Saver)
|
|
281
756
|
|
|
282
|
-
|
|
757
|
+
Thread-State wird über LangGraph Checkpoint-Saver an `Agent` / `DeepAgent` gehängt — nicht über eine eigene Chain-Klasse.
|
|
758
|
+
|
|
759
|
+
### `MemorySaver` (in-memory, schnell für local/dev)
|
|
283
760
|
|
|
284
761
|
```ts
|
|
285
762
|
import { MemorySaver, SmartCheckpointSaver, getLLM } from "@delofarag/ai-utils"
|
|
286
763
|
|
|
287
|
-
const
|
|
764
|
+
const checkpointer = new SmartCheckpointSaver(new MemorySaver(), {
|
|
288
765
|
llm: getLLM({ provider: "openrouter", model: "openai/gpt-5.4-mini" }),
|
|
289
766
|
messagesBeforeSummary: 12,
|
|
290
767
|
maxSummaries: 7
|
|
@@ -293,13 +770,11 @@ const memory = new SmartCheckpointSaver(new MemorySaver(), {
|
|
|
293
770
|
|
|
294
771
|
### `SmartCheckpointSaver`
|
|
295
772
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
- fasst alte Chatverlaeufe zusammen
|
|
773
|
+
- fasst alte Chatverläufe zusammen
|
|
299
774
|
- reduziert Token-Kosten
|
|
300
|
-
-
|
|
775
|
+
- erhält wichtige Fakten über mehrere Sessions
|
|
301
776
|
|
|
302
|
-
|
|
777
|
+
Optionen:
|
|
303
778
|
|
|
304
779
|
- `messagesBeforeSummary` (default `12`)
|
|
305
780
|
- `maxSummaries` (default `7`)
|
|
@@ -308,7 +783,7 @@ Wichtige Optionen:
|
|
|
308
783
|
|
|
309
784
|
### `SupabaseCheckpointSaver`
|
|
310
785
|
|
|
311
|
-
Persistiert Checkpoints in Supabase
|
|
786
|
+
Persistiert Checkpoints in Supabase:
|
|
312
787
|
|
|
313
788
|
```ts
|
|
314
789
|
import { SupabaseCheckpointSaver, type SupabaseCheckpointRow } from "@delofarag/ai-utils"
|
|
@@ -319,22 +794,26 @@ const checkpointsTable = new SupabaseTable<SupabaseCheckpointRow>({
|
|
|
319
794
|
})
|
|
320
795
|
|
|
321
796
|
const saver = new SupabaseCheckpointSaver(checkpointsTable)
|
|
797
|
+
|
|
798
|
+
const agent = new Agent({
|
|
799
|
+
tools: [...],
|
|
800
|
+
checkpointer: saver
|
|
801
|
+
})
|
|
322
802
|
```
|
|
323
803
|
|
|
324
|
-
|
|
804
|
+
### Checkpoint-Helpers
|
|
325
805
|
|
|
326
806
|
```ts
|
|
327
|
-
import {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
})
|
|
807
|
+
import {
|
|
808
|
+
formatCheckpointMessagesForLLM,
|
|
809
|
+
getMessagesArrayFromCheckpoint,
|
|
810
|
+
chatSummarizer
|
|
811
|
+
} from "@delofarag/ai-utils"
|
|
333
812
|
```
|
|
334
813
|
|
|
335
814
|
---
|
|
336
815
|
|
|
337
|
-
## RAG Utilities
|
|
816
|
+
## RAG Utilities
|
|
338
817
|
|
|
339
818
|
### Vector Stores
|
|
340
819
|
|
|
@@ -369,20 +848,24 @@ const ragTool = createRAGTool({
|
|
|
369
848
|
## Magic-Funcs
|
|
370
849
|
|
|
371
850
|
### Answerers
|
|
851
|
+
|
|
372
852
|
- `ask({ question, llm? })`
|
|
373
853
|
- `websearch(query)` (braucht `TAVILY_API_KEY`)
|
|
374
854
|
|
|
375
855
|
### Evaluators
|
|
856
|
+
|
|
376
857
|
- `classify({ data, classes, context?, llm? })`
|
|
377
858
|
- `decide({ material, kriteria_to_decide, llm? })`
|
|
378
859
|
|
|
379
860
|
### Parsers
|
|
861
|
+
|
|
380
862
|
- `extract({ data, schema, goal?, llm? })`
|
|
381
863
|
- `structure({ data, into, retries?, llm? })`
|
|
382
864
|
- `rewrite({ data, instruction, llm? })`
|
|
383
865
|
- `summarize({ data, fokuss?, maxWords?, llm? })`
|
|
384
866
|
|
|
385
867
|
### Optimizers
|
|
868
|
+
|
|
386
869
|
- `promptify({ request, agentRole?, llm? })`
|
|
387
870
|
- `ragify({ data, llm? })`
|
|
388
871
|
|
|
@@ -390,7 +873,7 @@ Beispiel:
|
|
|
390
873
|
|
|
391
874
|
```ts
|
|
392
875
|
import { classify, extract, summarize } from "@delofarag/ai-utils"
|
|
393
|
-
import { z } from "zod/
|
|
876
|
+
import { z } from "zod/v4"
|
|
394
877
|
|
|
395
878
|
const sentiment = await classify({
|
|
396
879
|
data: "Das Produkt ist wirklich gut.",
|
|
@@ -419,7 +902,7 @@ const short = await summarize({
|
|
|
419
902
|
### STT
|
|
420
903
|
|
|
421
904
|
- `stt(...)`
|
|
422
|
-
- `createSTTPhoneSocketSession(...)`
|
|
905
|
+
- `createSTTPhoneSocketSession(...)` für live phone socket chunks (Twilio/Telnyx-style)
|
|
423
906
|
|
|
424
907
|
```ts
|
|
425
908
|
import { stt } from "@delofarag/ai-utils"
|
|
@@ -433,7 +916,7 @@ const result = await stt({
|
|
|
433
916
|
### TTS
|
|
434
917
|
|
|
435
918
|
- `tts(...)`
|
|
436
|
-
- `streamTTSOverPhoneSocket(...)`
|
|
919
|
+
- `streamTTSOverPhoneSocket(...)` für chunked outbound audio
|
|
437
920
|
|
|
438
921
|
```ts
|
|
439
922
|
import { tts, streamTTSOverPhoneSocket } from "@delofarag/ai-utils"
|
|
@@ -479,29 +962,97 @@ const generated = await generateImages({
|
|
|
479
962
|
## Session / Stream Helpers
|
|
480
963
|
|
|
481
964
|
```ts
|
|
482
|
-
import { session, StreamResponse } from "@delofarag/ai-utils"
|
|
965
|
+
import { session, StreamResponse, logChunk } from "@delofarag/ai-utils"
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
### `session()` — CLI-Testloop
|
|
969
|
+
|
|
970
|
+
Interaktive Konsole für `Agent.stream()` oder `DeepAgent.stream()`.
|
|
971
|
+
|
|
972
|
+
| Prop | Default | Beschreibung |
|
|
973
|
+
|---|---|---|
|
|
974
|
+
| `streamable` | — | Objekt mit `.stream({ input, thread_id, ... })` |
|
|
975
|
+
| `breakword` | `"exit"` | Beendet die Session |
|
|
976
|
+
| `id` | Timestamp | Wird als `thread_id` genutzt |
|
|
977
|
+
| `numberOfMessages` | `Infinity` | Max. User+Assistant-Runden |
|
|
978
|
+
| `isDeepAgent` | `false` | HITL-Modus für DeepAgent (siehe unten) |
|
|
979
|
+
|
|
980
|
+
**Normaler Agent:**
|
|
981
|
+
|
|
982
|
+
```ts
|
|
983
|
+
await session({
|
|
984
|
+
streamable: agent,
|
|
985
|
+
breakword: "exit",
|
|
986
|
+
id: "dev-session-1",
|
|
987
|
+
})
|
|
483
988
|
```
|
|
484
989
|
|
|
485
|
-
|
|
486
|
-
|
|
990
|
+
**DeepAgent mit HITL + Tool-Logs:**
|
|
991
|
+
|
|
992
|
+
```ts
|
|
993
|
+
await session({
|
|
994
|
+
streamable: deepAgent,
|
|
995
|
+
isDeepAgent: true,
|
|
996
|
+
id: "dev-session-1",
|
|
997
|
+
})
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
Mit `isDeepAgent: true` passiert automatisch:
|
|
1001
|
+
|
|
1002
|
+
- `showToolCalls: true` — `[tool:start]` / `[tool:end]` in der Konsole
|
|
1003
|
+
- Nach einem Interrupt wartet die Session auf deine Entscheidung statt neuer Message
|
|
1004
|
+
- Erkannte Antworten: `approve`, `ja`, `ok`, `reject`, `reject: Grund hier`
|
|
1005
|
+
- `logChunk()` formatiert Text, Interrupts und Tool-Events lesbar
|
|
1006
|
+
|
|
1007
|
+
Flow:
|
|
1008
|
+
|
|
1009
|
+
```
|
|
1010
|
+
You: Schreib eine Datei
|
|
1011
|
+
Assistant: [tool:start] write_file {...}
|
|
1012
|
+
[interrupt] Datei schreiben?
|
|
1013
|
+
decisions: approve, reject
|
|
1014
|
+
You: approve
|
|
1015
|
+
Assistant: [tool:end] write_file
|
|
1016
|
+
Fertig!
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
### `StreamResponse()` — NDJSON für HTTP
|
|
1020
|
+
|
|
1021
|
+
Wrappt einen `AsyncIterable` als HTTP-Response (`application/x-ndjson`). Jeder Chunk = eine JSON-Zeile. Ideal für `DeepAgent.stream()` in API-Routes.
|
|
1022
|
+
|
|
1023
|
+
### `logChunk()`
|
|
1024
|
+
|
|
1025
|
+
Hilfsfunktion für Stream-Output in der Konsole — nutzt intern `isInterrupt()` und `isToolEvent()`.
|
|
487
1026
|
|
|
488
1027
|
---
|
|
489
1028
|
|
|
490
|
-
## Export Overview
|
|
1029
|
+
## Export Overview
|
|
491
1030
|
|
|
492
|
-
Top-level Exports
|
|
1031
|
+
Top-level Exports (`@delofarag/ai-utils`):
|
|
493
1032
|
|
|
494
|
-
- Helpers (`helpers`, `memory`, `rag`, `llms`, `chatbot`)
|
|
495
|
-
- Core (`Agent`, `Chain`, `
|
|
496
|
-
-
|
|
1033
|
+
- Helpers (`helpers`, `memory`, `rag`, `llms`, `chatbot`, `logChunk`)
|
|
1034
|
+
- Core (`Agent`, `Chain`, `DeepAgent`)
|
|
1035
|
+
- DeepAgent (`createWorkspaceBackend`, `createLocalShellBackend`, `createDenoSandbox`, `workspacePermissions`, `interruptOn`-Helper, alle `DeepAgent*`-Types)
|
|
1036
|
+
- Memory (`MemorySaver`, `SmartCheckpointSaver`, `SupabaseCheckpointSaver`, `chatSummarizer`)
|
|
1037
|
+
- Tools (`ToolRegistry`, `CombinedToolRegistry`, `ZodiosToolRegistry`, `Tavily`, `RAGTool`)
|
|
497
1038
|
- Magic-Funcs (answerers/evaluators/parsers/optimizers)
|
|
498
1039
|
- Modalities (`stt`, `tts`, `vision`, `generateImages`)
|
|
499
1040
|
|
|
1041
|
+
Client-Subpath (`@delofarag/ai-utils/client`) — ohne LangChain-Bundle, für Frontend:
|
|
1042
|
+
|
|
1043
|
+
- Types: `DeepAgentInterrupt`, `DeepAgentToolEvent`, `DeepAgentStreamChunk`, ...
|
|
1044
|
+
- Guards: `isInterrupt()`, `isToolEvent()`
|
|
1045
|
+
|
|
500
1046
|
---
|
|
501
1047
|
|
|
502
1048
|
## Empfehlungen
|
|
503
1049
|
|
|
504
|
-
-
|
|
505
|
-
-
|
|
506
|
-
- Bei langen Chats `SmartCheckpointSaver` verwenden.
|
|
1050
|
+
- Für strukturierte Outputs immer `zod/v4` verwenden.
|
|
1051
|
+
- Für Produktion API-Keys als ENV setzen, nicht hardcoden.
|
|
1052
|
+
- Bei langen Chats `SmartCheckpointSaver` am `Agent` verwenden.
|
|
507
1053
|
- RAG als Tool im `Agent` ist in der Praxis oft robuster als RAG-only Prompting.
|
|
1054
|
+
- `Chain` für stateless Tasks, `Agent` für Tools und Conversation Memory, `DeepAgent` für autonome Coding-/Research-Agents.
|
|
1055
|
+
- DeepAgent: `rootDir` + `route` klar trennen — Agent sieht nur den virtuellen Pfad.
|
|
1056
|
+
- DeepAgent HITL: immer `checkpointer` + `thread_id`; `interruptOn` pro Tool konfigurieren.
|
|
1057
|
+
- DeepAgent Shell: `createLocalShellBackend` nur lokal/Dev — `execute` läuft auf dem Host.
|
|
1058
|
+
- DeepAgent testen: `session({ streamable: agent, isDeepAgent: true })`.
|