@agentionai/agents 0.14.0 → 1.0.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 +73 -13
- package/dist/agents/AgentConfig.d.ts +10 -0
- package/dist/agents/AgentEvent.d.ts +2 -0
- package/dist/agents/AgentEvent.js +2 -0
- package/dist/agents/anthropic/ClaudeAgent.d.ts +54 -0
- package/dist/agents/anthropic/ClaudeAgent.js +235 -27
- package/dist/agents/google/GeminiAgent.js +2 -1
- package/dist/agents/openai/OpenAiAgent.d.ts +15 -0
- package/dist/agents/openai/OpenAiAgent.js +150 -0
- package/dist/agents/openai-compatible/OpenAICompatibleAgent.d.ts +27 -0
- package/dist/agents/openai-compatible/OpenAICompatibleAgent.js +195 -0
- package/dist/history/History.d.ts +2 -2
- package/dist/history/History.js +3 -1
- package/dist/history/index.d.ts +2 -2
- package/dist/history/index.js +3 -1
- package/dist/history/transformers.js +20 -1
- package/dist/history/types.d.ts +20 -1
- package/dist/history/types.js +11 -0
- package/dist/index.d.ts +1 -1
- package/dist/ingestion/IngestionPipeline.js +1 -1
- package/dist/llamacpp.d.ts +1 -1
- package/dist/team/Team.js +1 -10
- package/dist/vectorstore/LanceDBVectorStore.js +0 -1
- package/package.json +4 -7
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
A comprehensive TypeScript toolkit for building LLM-powered agents with RAG, and multi-agent workflows. No hidden state machines, no forced abstractions—just typed agents, composable graphs, and complete control in a complete toolkit.
|
|
8
8
|
|
|
9
|
-
**[Documentation](https://docs.agention.ai/)** • **[Examples](https://docs.agention.ai/guide/examples)** • **[GitHub](https://github.com/laurentzuijdwijk/agention-lib)**
|
|
9
|
+
**[Documentation](https://docs.agention.ai/)** • **[Examples](https://docs.agention.ai/guide/examples)** • **[GitHub](https://github.com/laurentzuijdwijk/agention-lib)** • **[npm](https://www.npmjs.com/package/@agentionai/agents)**
|
|
10
10
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
@@ -42,10 +42,11 @@ export ANTHROPIC_API_KEY=your-key-here
|
|
|
42
42
|
import { ClaudeAgent } from '@agentionai/agents/claude';
|
|
43
43
|
|
|
44
44
|
const agent = new ClaudeAgent({
|
|
45
|
-
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
46
|
-
|
|
45
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
46
|
+
id: 'assistant',
|
|
47
47
|
name: 'Assistant',
|
|
48
48
|
description: 'You are a helpful assistant.',
|
|
49
|
+
model: 'claude-sonnet-4-5',
|
|
49
50
|
});
|
|
50
51
|
|
|
51
52
|
const response = await agent.execute('What can you help me with?');
|
|
@@ -75,6 +76,8 @@ import { ClaudeAgent, OpenAiAgent } from '@agentionai/agents';
|
|
|
75
76
|
## Features
|
|
76
77
|
|
|
77
78
|
- **Multi-Provider, No Lock-in** - Claude, OpenAI, Gemini, Mistral, plus local models via Ollama and llama.cpp—same interface. Switch models with one line.
|
|
79
|
+
- **Composable Context Management** - Tool result masking (lossless, free) + rolling summarization (auto-firing) + sub-agent delegation (token isolation by architecture).
|
|
80
|
+
- **Streaming** - `executeStream()` on Claude, OpenAI, and all OpenAI-compatible agents. Yields `{ type: "text" | "reasoning" }` chunks; tool calls handled transparently.
|
|
78
81
|
- **Built-In Tools** - Use provider-defined server-side tools (e.g. Anthropic's web search, bash, text editor) alongside your own.
|
|
79
82
|
- **Composable, Not Magical** - Agents are objects. Pipelines are arrays. No hidden state, no surprises.
|
|
80
83
|
- **Multimodal / Vision** - Send images alongside text with a unified `MessageContent[]` API across all providers.
|
|
@@ -110,15 +113,50 @@ const weatherTool = new Tool({
|
|
|
110
113
|
|
|
111
114
|
const agent = new GeminiAgent({
|
|
112
115
|
apiKey: process.env.GEMINI_API_KEY,
|
|
113
|
-
|
|
116
|
+
id: 'weather-agent',
|
|
114
117
|
name: 'Weather Agent',
|
|
115
118
|
description: 'You are a weather assistant.',
|
|
119
|
+
model: 'gemini-flash-lite-latest',
|
|
116
120
|
tools: [weatherTool],
|
|
117
121
|
});
|
|
118
122
|
|
|
119
123
|
const response = await agent.execute("What's the weather in Paris?");
|
|
120
124
|
```
|
|
121
125
|
|
|
126
|
+
### Context Management
|
|
127
|
+
|
|
128
|
+
Every agent conversation grows — tool results pile up, turns accumulate, tokens compound. Agention's history plugins keep the context window lean automatically, without manual bookkeeping.
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { toolResultMaskingPlugin, compressionPlugin } from '@agentionai/agents/history/plugins';
|
|
132
|
+
import { History } from '@agentionai/agents/history';
|
|
133
|
+
|
|
134
|
+
// Mask old tool results at read time — sync, free, lossless
|
|
135
|
+
const maskingPlugin = toolResultMaskingPlugin({ keepRecentResults: 2 });
|
|
136
|
+
|
|
137
|
+
// Compress old turns into a rolling summary — auto-fires past a token budget
|
|
138
|
+
const history = new History([], { maxTokens: 50000 })
|
|
139
|
+
.use(maskingPlugin)
|
|
140
|
+
.use(compressionPlugin(summaryAgent, { autoReduceWhen: { maxTokens: 8000 } }));
|
|
141
|
+
|
|
142
|
+
const agent = new ClaudeAgent({
|
|
143
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
144
|
+
id: 'researcher',
|
|
145
|
+
name: 'Researcher',
|
|
146
|
+
description: 'Research topics thoroughly.',
|
|
147
|
+
model: 'claude-sonnet-4-6',
|
|
148
|
+
tools: [searchTool, maskingPlugin.retrieveTool],
|
|
149
|
+
}, history);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
| Strategy | Cost | Data loss | Trigger |
|
|
153
|
+
|---|---|---|---|
|
|
154
|
+
| `toolResultMaskingPlugin` | Zero — sync, no LLM calls | None — full content always retrievable | Every `getEntries()` call |
|
|
155
|
+
| `compressionPlugin` | LLM tokens (use a cheap model) | Yes — detail traded for brevity | `autoReduceWhen` threshold or manual `history.reduce()` |
|
|
156
|
+
| `Tool.fromAgent()` | None — structural choice | None in main context (sub-agent history is independent) | Automatic — just wrap an agent as a tool |
|
|
157
|
+
|
|
158
|
+
[Context Management guide →](https://docs.agention.ai/guide/context-management) · [History API →](https://docs.agention.ai/guide/history)
|
|
159
|
+
|
|
122
160
|
### Local Models (Ollama / llama.cpp / OpenAI-compatible servers)
|
|
123
161
|
|
|
124
162
|
Run models on your own machine — no API key required. Same agent interface as every other provider:
|
|
@@ -171,8 +209,7 @@ class VLLMAgent extends OpenAICompatibleAgent {
|
|
|
171
209
|
Use a provider's own server-side tools (executed by the provider, not locally) alongside your custom tools:
|
|
172
210
|
|
|
173
211
|
```typescript
|
|
174
|
-
import { ClaudeAgent } from '@agentionai/agents/claude';
|
|
175
|
-
import { webSearchTool } from '@agentionai/agents/claude';
|
|
212
|
+
import { ClaudeAgent, webSearchTool } from '@agentionai/agents/claude';
|
|
176
213
|
|
|
177
214
|
const agent = new ClaudeAgent({
|
|
178
215
|
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
@@ -186,6 +223,27 @@ const agent = new ClaudeAgent({
|
|
|
186
223
|
const response = await agent.execute('What happened in the news today?');
|
|
187
224
|
```
|
|
188
225
|
|
|
226
|
+
### Streaming
|
|
227
|
+
|
|
228
|
+
All three major providers support streaming via `executeStream()`, which returns an `AsyncGenerator<StreamChunk>`. Each chunk is `{ type: "text" | "reasoning"; content: string }` — text for visible output, reasoning for internal thinking tokens (DeepSeek R1, Claude extended thinking, OpenAI o-series).
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import { ClaudeAgent } from '@agentionai/agents/claude';
|
|
232
|
+
|
|
233
|
+
const agent = new ClaudeAgent({
|
|
234
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
235
|
+
id: 'assistant',
|
|
236
|
+
name: 'Assistant',
|
|
237
|
+
description: 'You are a helpful assistant.',
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
for await (const chunk of agent.executeStream('Tell me a story')) {
|
|
241
|
+
if (chunk.type === 'text') process.stdout.write(chunk.content);
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Tool calls are handled transparently — the generator continues streaming after each round-trip. The same API works across `ClaudeAgent`, `OpenAiAgent`, and `LlamaCppAgent` / any `OpenAICompatibleAgent` subclass.
|
|
246
|
+
|
|
189
247
|
### Multi-Agent Pipeline
|
|
190
248
|
|
|
191
249
|
Chain agents together with different providers and models:
|
|
@@ -258,9 +316,10 @@ import * as fs from 'fs';
|
|
|
258
316
|
|
|
259
317
|
const agent = new ClaudeAgent({
|
|
260
318
|
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
261
|
-
|
|
319
|
+
id: 'vision-agent',
|
|
262
320
|
name: 'VisionAgent',
|
|
263
321
|
description: 'You analyze images.',
|
|
322
|
+
model: 'claude-opus-4-6',
|
|
264
323
|
});
|
|
265
324
|
|
|
266
325
|
// Remote image by URL
|
|
@@ -296,16 +355,16 @@ JSON Schema + handler pattern. Unique capability: wrap any agent as a tool for d
|
|
|
296
355
|
|
|
297
356
|
[Learn more →](https://docs.agention.ai/guide/tools)
|
|
298
357
|
|
|
358
|
+
### Context Management
|
|
359
|
+
Tool result masking (lossless, free) + rolling summarization (auto-firing) + sub-agent delegation (token isolation by architecture). Composable plugins keep the context window lean automatically.
|
|
360
|
+
|
|
361
|
+
[Learn more →](https://docs.agention.ai/guide/context-management) · [History API →](https://docs.agention.ai/guide/history)
|
|
362
|
+
|
|
299
363
|
### Multimodal / Vision
|
|
300
364
|
Unified `MessageContent[]` interface for images across all providers. URL and base64 images, mix text and images freely in a single call.
|
|
301
365
|
|
|
302
366
|
[Learn more →](https://docs.agention.ai/guide/multimodal)
|
|
303
367
|
|
|
304
|
-
### History
|
|
305
|
-
Provider-agnostic, persistent (Redis, file, custom), shareable across agents of different providers.
|
|
306
|
-
|
|
307
|
-
[Learn more →](https://docs.agention.ai/guide/history)
|
|
308
|
-
|
|
309
368
|
### Graph Pipelines
|
|
310
369
|
Compose sequential, parallel, voting, routing, and nested graphs. Mix models and providers freely.
|
|
311
370
|
|
|
@@ -324,10 +383,11 @@ Per-call and per-node token counts, duration metrics, full execution visibility.
|
|
|
324
383
|
## Documentation
|
|
325
384
|
|
|
326
385
|
- **[Getting Started](https://docs.agention.ai/guide/getting-started)** - Installation and first agent
|
|
327
|
-
- **[
|
|
386
|
+
- **[Context Management](https://docs.agention.ai/guide/context-management)** - Token budgets, masking, and compression
|
|
328
387
|
- **[Agents](https://docs.agention.ai/guide/agents)** - Agent configuration and providers
|
|
329
388
|
- **[Tools](https://docs.agention.ai/guide/tools)** - Adding capabilities and agent delegation
|
|
330
389
|
- **[Multimodal / Vision](https://docs.agention.ai/guide/multimodal)** - Sending images across all providers
|
|
390
|
+
- **[History](https://docs.agention.ai/guide/history)** - Conversation persistence and plugins
|
|
331
391
|
- **[Graph Pipelines](https://docs.agention.ai/guide/graph-pipelines)** - Multi-agent workflows
|
|
332
392
|
- **[Vector Stores](https://docs.agention.ai/guide/vector-stores)** - RAG and semantic search
|
|
333
393
|
- **[Examples](https://docs.agention.ai/guide/examples)** - Real-world implementations
|
|
@@ -73,6 +73,16 @@ export interface ClaudeSpecificConfig {
|
|
|
73
73
|
* implementation detail that can change.
|
|
74
74
|
*/
|
|
75
75
|
authType?: "apiKey" | "oauth";
|
|
76
|
+
/**
|
|
77
|
+
* Enable extended thinking by setting a thinking token budget. When set (> 0), the
|
|
78
|
+
* agent requests `thinking: { type: "enabled", budget_tokens }` and surfaces thinking
|
|
79
|
+
* tokens as `"reasoning"` chunks from `executeStream()`.
|
|
80
|
+
*
|
|
81
|
+
* Constraints (enforced by Anthropic): `budget_tokens` must be ≥ 1024 and strictly less
|
|
82
|
+
* than `maxTokens`. When enabled, `temperature`/`topP`/`topK` are not sent (the API
|
|
83
|
+
* requires default sampling with thinking).
|
|
84
|
+
*/
|
|
85
|
+
thinkingBudgetTokens?: number;
|
|
76
86
|
}
|
|
77
87
|
/**
|
|
78
88
|
* Vendor-specific configuration for OpenAI
|
|
@@ -10,6 +10,8 @@ export declare class AgentEvent {
|
|
|
10
10
|
static MAX_RETRIES_EXCEEDED: string;
|
|
11
11
|
static MAX_TOKENS_EXCEEDED: string;
|
|
12
12
|
static TOOL_ERROR: string;
|
|
13
|
+
static CHUNK: string;
|
|
14
|
+
static REASONING_CHUNK: string;
|
|
13
15
|
private defaultPrevented;
|
|
14
16
|
constructor(target: BaseAgent<any>);
|
|
15
17
|
preventDefault(): void;
|
|
@@ -23,4 +23,6 @@ AgentEvent.RETRY = "retry";
|
|
|
23
23
|
AgentEvent.MAX_RETRIES_EXCEEDED = "max_retries_exceeded";
|
|
24
24
|
AgentEvent.MAX_TOKENS_EXCEEDED = "max_tokens_exceeded";
|
|
25
25
|
AgentEvent.TOOL_ERROR = "tool_error";
|
|
26
|
+
AgentEvent.CHUNK = "chunk";
|
|
27
|
+
AgentEvent.REASONING_CHUNK = "reasoning_chunk";
|
|
26
28
|
//# sourceMappingURL=AgentEvent.js.map
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { Anthropic } from "@anthropic-ai/sdk";
|
|
1
2
|
import { Message, ToolUnion, Usage } from "@anthropic-ai/sdk/resources";
|
|
2
3
|
import { type ToolDefinition } from "../../tools/Tool";
|
|
3
4
|
import { type BuiltInTool } from "../../tools/BuiltInTool";
|
|
4
5
|
import { BaseAgent, BaseAgentConfig, TokenUsage } from "../BaseAgent";
|
|
5
6
|
import { History, MessageContent } from "../../history/History";
|
|
7
|
+
import { StreamChunk } from "../openai-compatible/OpenAICompatibleAgent";
|
|
6
8
|
import { ClaudeModel } from "../model-types";
|
|
7
9
|
type AgentConfig = BaseAgentConfig & {
|
|
8
10
|
apiKey: string;
|
|
@@ -22,6 +24,12 @@ type AgentConfig = BaseAgentConfig & {
|
|
|
22
24
|
* @see lib/tools/BuiltInTool.ts
|
|
23
25
|
*/
|
|
24
26
|
builtInTools?: BuiltInTool[];
|
|
27
|
+
/**
|
|
28
|
+
* Enable extended thinking with this token budget (≥ 1024, and strictly less than
|
|
29
|
+
* `maxTokens`). When set, thinking tokens are streamed as `"reasoning"` chunks and
|
|
30
|
+
* `temperature`/`topP`/`topK` are omitted (required by the API when thinking is on).
|
|
31
|
+
*/
|
|
32
|
+
thinkingBudgetTokens?: number;
|
|
25
33
|
};
|
|
26
34
|
/**
|
|
27
35
|
* Agent for Anthropic models.
|
|
@@ -54,10 +62,56 @@ export declare class ClaudeAgent extends BaseAgent {
|
|
|
54
62
|
* (server-side) built-in tools, in the shape Anthropic's API expects.
|
|
55
63
|
*/
|
|
56
64
|
protected getAllToolDefinitions(): ToolUnion[];
|
|
65
|
+
/**
|
|
66
|
+
* Build the common `messages.create` params (shared by `execute()` and
|
|
67
|
+
* `executeStream()`), excluding `stream`. When extended thinking is enabled
|
|
68
|
+
* (`thinkingBudgetTokens > 0`) the API requires default sampling, so
|
|
69
|
+
* `temperature`/`top_p`/`top_k` are omitted in favour of the `thinking` block.
|
|
70
|
+
*/
|
|
71
|
+
protected buildMessageParams(): {
|
|
72
|
+
thinking: {
|
|
73
|
+
type: "enabled";
|
|
74
|
+
budget_tokens: number;
|
|
75
|
+
};
|
|
76
|
+
model: string & ClaudeModel;
|
|
77
|
+
system: string | undefined;
|
|
78
|
+
max_tokens: number;
|
|
79
|
+
messages: Anthropic.Messages.MessageParam[];
|
|
80
|
+
tools: Anthropic.Messages.ToolUnion[];
|
|
81
|
+
stop_sequences: string[] | undefined;
|
|
82
|
+
metadata: Record<string, string> | undefined;
|
|
83
|
+
} | {
|
|
84
|
+
temperature: number | undefined;
|
|
85
|
+
top_p: number | undefined;
|
|
86
|
+
top_k: number | undefined;
|
|
87
|
+
model: string & ClaudeModel;
|
|
88
|
+
system: string | undefined;
|
|
89
|
+
max_tokens: number;
|
|
90
|
+
messages: Anthropic.Messages.MessageParam[];
|
|
91
|
+
tools: Anthropic.Messages.ToolUnion[];
|
|
92
|
+
stop_sequences: string[] | undefined;
|
|
93
|
+
metadata: Record<string, string> | undefined;
|
|
94
|
+
};
|
|
57
95
|
protected process(_input: string): Promise<string>;
|
|
58
96
|
execute(input: string | MessageContent[]): Promise<string>;
|
|
59
97
|
protected handleResponse(response: Message): Promise<string>;
|
|
60
98
|
private handleToolUse;
|
|
99
|
+
/**
|
|
100
|
+
* Stream a response as an async generator of `StreamChunk` objects.
|
|
101
|
+
* Yields `{ type: "text" }` for visible output and `{ type: "reasoning" }` for
|
|
102
|
+
* extended thinking tokens (models with thinking enabled). Tool calls are handled
|
|
103
|
+
* transparently — the generator continues streaming after each round-trip.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* for await (const chunk of agent.executeStream("Explain recursion")) {
|
|
108
|
+
* if (chunk.type === "text") process.stdout.write(chunk.content);
|
|
109
|
+
* else process.stderr.write(`[thinking] ${chunk.content}`);
|
|
110
|
+
* }
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
executeStream(input: string | MessageContent[]): AsyncGenerator<StreamChunk>;
|
|
114
|
+
private streamTurn;
|
|
61
115
|
protected parseUsage(input: Usage): TokenUsage;
|
|
62
116
|
}
|
|
63
117
|
export {};
|
|
@@ -38,6 +38,7 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
38
38
|
const metadata = config.metadata ?? vendorConfig.metadata;
|
|
39
39
|
const builtInTools = config.builtInTools ?? vendorConfig.builtInTools;
|
|
40
40
|
const authType = config.authType ?? vendorConfig.authType ?? "apiKey";
|
|
41
|
+
const thinkingBudgetTokens = config.thinkingBudgetTokens ?? vendorConfig.thinkingBudgetTokens;
|
|
41
42
|
this.client = new sdk_1.Anthropic(authType === "oauth"
|
|
42
43
|
? { authToken: config.apiKey }
|
|
43
44
|
: { apiKey: config.apiKey });
|
|
@@ -48,6 +49,7 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
48
49
|
metadata,
|
|
49
50
|
builtInTools,
|
|
50
51
|
authType,
|
|
52
|
+
thinkingBudgetTokens,
|
|
51
53
|
apiKey: config.apiKey,
|
|
52
54
|
temperature: config.temperature,
|
|
53
55
|
topP: config.topP,
|
|
@@ -70,6 +72,37 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
70
72
|
...(this.config.builtInTools ?? []),
|
|
71
73
|
];
|
|
72
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Build the common `messages.create` params (shared by `execute()` and
|
|
77
|
+
* `executeStream()`), excluding `stream`. When extended thinking is enabled
|
|
78
|
+
* (`thinkingBudgetTokens > 0`) the API requires default sampling, so
|
|
79
|
+
* `temperature`/`top_p`/`top_k` are omitted in favour of the `thinking` block.
|
|
80
|
+
*/
|
|
81
|
+
buildMessageParams() {
|
|
82
|
+
const messages = transformers_1.anthropicTransformer.toProvider(this.history.getEntries());
|
|
83
|
+
const thinkingEnabled = (this.config.thinkingBudgetTokens ?? 0) > 0;
|
|
84
|
+
return {
|
|
85
|
+
model: this.config.model,
|
|
86
|
+
system: this.history.getSystemMessage(),
|
|
87
|
+
max_tokens: this.config.maxTokens,
|
|
88
|
+
messages,
|
|
89
|
+
tools: this.getAllToolDefinitions(),
|
|
90
|
+
stop_sequences: this.config.stopSequences,
|
|
91
|
+
metadata: this.config.metadata,
|
|
92
|
+
...(thinkingEnabled
|
|
93
|
+
? {
|
|
94
|
+
thinking: {
|
|
95
|
+
type: "enabled",
|
|
96
|
+
budget_tokens: this.config.thinkingBudgetTokens,
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
: {
|
|
100
|
+
temperature: this.config.temperature,
|
|
101
|
+
top_p: this.config.topP,
|
|
102
|
+
top_k: this.config.topK,
|
|
103
|
+
}),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
73
106
|
async process(_input) {
|
|
74
107
|
return "";
|
|
75
108
|
}
|
|
@@ -102,20 +135,7 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
102
135
|
// mid-loop. endExecution() in the finally block enforces limits once.
|
|
103
136
|
this.history.beginExecution();
|
|
104
137
|
try {
|
|
105
|
-
const
|
|
106
|
-
const systemMessage = this.history.getSystemMessage();
|
|
107
|
-
const response = await this.client.messages.create({
|
|
108
|
-
model: this.config.model,
|
|
109
|
-
system: systemMessage,
|
|
110
|
-
max_tokens: this.config.maxTokens,
|
|
111
|
-
messages,
|
|
112
|
-
tools: this.getAllToolDefinitions(),
|
|
113
|
-
temperature: this.config.temperature,
|
|
114
|
-
top_p: this.config.topP,
|
|
115
|
-
top_k: this.config.topK,
|
|
116
|
-
stop_sequences: this.config.stopSequences,
|
|
117
|
-
metadata: this.config.metadata,
|
|
118
|
-
});
|
|
138
|
+
const response = (await this.client.messages.create(this.buildMessageParams()));
|
|
119
139
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, response);
|
|
120
140
|
return await this.handleResponse(response);
|
|
121
141
|
}
|
|
@@ -212,19 +232,7 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
212
232
|
this.addMessageToHistory("user", toolResults);
|
|
213
233
|
// Continue conversation with tool results
|
|
214
234
|
try {
|
|
215
|
-
const
|
|
216
|
-
const newResponse = await this.client.messages.create({
|
|
217
|
-
model: this.config.model,
|
|
218
|
-
system: this.history.getSystemMessage(),
|
|
219
|
-
max_tokens: this.config.maxTokens,
|
|
220
|
-
messages,
|
|
221
|
-
tools: this.getAllToolDefinitions(),
|
|
222
|
-
temperature: this.config.temperature,
|
|
223
|
-
top_p: this.config.topP,
|
|
224
|
-
top_k: this.config.topK,
|
|
225
|
-
stop_sequences: this.config.stopSequences,
|
|
226
|
-
metadata: this.config.metadata,
|
|
227
|
-
});
|
|
235
|
+
const newResponse = (await this.client.messages.create(this.buildMessageParams()));
|
|
228
236
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, newResponse);
|
|
229
237
|
return this.handleResponse(newResponse);
|
|
230
238
|
}
|
|
@@ -305,6 +313,206 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
305
313
|
}));
|
|
306
314
|
return results;
|
|
307
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Stream a response as an async generator of `StreamChunk` objects.
|
|
318
|
+
* Yields `{ type: "text" }` for visible output and `{ type: "reasoning" }` for
|
|
319
|
+
* extended thinking tokens (models with thinking enabled). Tool calls are handled
|
|
320
|
+
* transparently — the generator continues streaming after each round-trip.
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```typescript
|
|
324
|
+
* for await (const chunk of agent.executeStream("Explain recursion")) {
|
|
325
|
+
* if (chunk.type === "text") process.stdout.write(chunk.content);
|
|
326
|
+
* else process.stderr.write(`[thinking] ${chunk.content}`);
|
|
327
|
+
* }
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
async *executeStream(input) {
|
|
331
|
+
this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
|
|
332
|
+
this.lastTokenUsage = undefined;
|
|
333
|
+
this.currentToolCallCount = 0;
|
|
334
|
+
const inputPreview = typeof input === "string" ? input : JSON.stringify(input);
|
|
335
|
+
if (VizConfig_1.vizConfig.isEnabled()) {
|
|
336
|
+
this.vizEventId = VizReporter_1.vizReporter.agentStart(this.id, this.name, this.config.model, "anthropic", inputPreview);
|
|
337
|
+
}
|
|
338
|
+
if (this.history.transient) {
|
|
339
|
+
this.history.clear();
|
|
340
|
+
this.addSystemMessage(this.getSystemMessage());
|
|
341
|
+
}
|
|
342
|
+
if (typeof input === "string") {
|
|
343
|
+
this.addTextToHistory("user", input);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
this.addMessageToHistory("user", input);
|
|
347
|
+
}
|
|
348
|
+
this.history.setSessionAnchor();
|
|
349
|
+
this.history.beginExecution();
|
|
350
|
+
try {
|
|
351
|
+
yield* this.streamTurn();
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
if (error instanceof sdk_1.APIError) {
|
|
355
|
+
const apiError = new AgentError_1.ApiError(`Anthropic API error: ${error.message}`, error.status, error);
|
|
356
|
+
this.emit(AgentEvent_1.AgentEvent.ERROR, apiError);
|
|
357
|
+
if (this.vizEventId) {
|
|
358
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "ApiError", apiError.message, error.status === 429);
|
|
359
|
+
this.vizEventId = undefined;
|
|
360
|
+
}
|
|
361
|
+
throw apiError;
|
|
362
|
+
}
|
|
363
|
+
// Errors raised inside streamTurn() (e.g. MaxTokensExceededError) are
|
|
364
|
+
// already emitted and viz-reported at the throw site — preserve their
|
|
365
|
+
// type rather than re-wrapping them in a generic ExecutionError.
|
|
366
|
+
if (error instanceof AgentError_1.AgentError) {
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
const executionError = new AgentError_1.ExecutionError(`Anthropic error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
370
|
+
this.emit(AgentEvent_1.AgentEvent.ERROR, executionError);
|
|
371
|
+
if (this.vizEventId) {
|
|
372
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "ExecutionError", executionError.message, false);
|
|
373
|
+
this.vizEventId = undefined;
|
|
374
|
+
}
|
|
375
|
+
throw executionError;
|
|
376
|
+
}
|
|
377
|
+
finally {
|
|
378
|
+
this.history.endExecution();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
async *streamTurn() {
|
|
382
|
+
const stream = await this.client.messages.create({
|
|
383
|
+
...this.buildMessageParams(),
|
|
384
|
+
stream: true,
|
|
385
|
+
});
|
|
386
|
+
const blocks = new Map();
|
|
387
|
+
let textContent = "";
|
|
388
|
+
let stopReason = null;
|
|
389
|
+
let inputTokens = 0;
|
|
390
|
+
let outputTokens = 0;
|
|
391
|
+
for await (const event of stream) {
|
|
392
|
+
if (event.type === "message_start") {
|
|
393
|
+
const e = event;
|
|
394
|
+
inputTokens = e.message.usage.input_tokens;
|
|
395
|
+
outputTokens = e.message.usage.output_tokens;
|
|
396
|
+
}
|
|
397
|
+
if (event.type === "message_delta") {
|
|
398
|
+
const e = event;
|
|
399
|
+
stopReason = e.delta.stop_reason ?? stopReason;
|
|
400
|
+
outputTokens += e.usage?.output_tokens ?? 0;
|
|
401
|
+
}
|
|
402
|
+
if (event.type === "content_block_start") {
|
|
403
|
+
const e = event;
|
|
404
|
+
const block = e.content_block;
|
|
405
|
+
if (block.type === "tool_use") {
|
|
406
|
+
blocks.set(e.index, { kind: "tool_use", id: block.id, name: block.name, inputJson: "" });
|
|
407
|
+
}
|
|
408
|
+
else if (block.type === "text") {
|
|
409
|
+
blocks.set(e.index, { kind: "text", text: "" });
|
|
410
|
+
}
|
|
411
|
+
else if (block.type === "thinking") {
|
|
412
|
+
blocks.set(e.index, { kind: "thinking", thinking: "", signature: "" });
|
|
413
|
+
}
|
|
414
|
+
else if (block.type === "redacted_thinking") {
|
|
415
|
+
blocks.set(e.index, { kind: "redacted_thinking", data: block.data });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (event.type === "content_block_delta") {
|
|
419
|
+
const e = event;
|
|
420
|
+
const delta = e.delta;
|
|
421
|
+
const acc = blocks.get(e.index);
|
|
422
|
+
if (delta.type === "text_delta") {
|
|
423
|
+
textContent += delta.text;
|
|
424
|
+
if (acc?.kind === "text")
|
|
425
|
+
acc.text += delta.text;
|
|
426
|
+
this.emit(AgentEvent_1.AgentEvent.CHUNK, delta.text);
|
|
427
|
+
yield { type: "text", content: delta.text };
|
|
428
|
+
}
|
|
429
|
+
else if (delta.type === "thinking_delta") {
|
|
430
|
+
if (acc?.kind === "thinking")
|
|
431
|
+
acc.thinking += delta.thinking;
|
|
432
|
+
this.emit(AgentEvent_1.AgentEvent.REASONING_CHUNK, delta.thinking);
|
|
433
|
+
yield { type: "reasoning", content: delta.thinking };
|
|
434
|
+
}
|
|
435
|
+
else if (delta.type === "signature_delta") {
|
|
436
|
+
if (acc?.kind === "thinking")
|
|
437
|
+
acc.signature += delta.signature;
|
|
438
|
+
}
|
|
439
|
+
else if (delta.type === "input_json_delta") {
|
|
440
|
+
if (acc?.kind === "tool_use")
|
|
441
|
+
acc.inputJson += delta.partial_json;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const usage = {
|
|
446
|
+
input_tokens: inputTokens,
|
|
447
|
+
output_tokens: outputTokens,
|
|
448
|
+
total_tokens: inputTokens + outputTokens,
|
|
449
|
+
};
|
|
450
|
+
if (this.lastTokenUsage) {
|
|
451
|
+
this.lastTokenUsage.input_tokens += usage.input_tokens;
|
|
452
|
+
this.lastTokenUsage.output_tokens += usage.output_tokens;
|
|
453
|
+
this.lastTokenUsage.total_tokens += usage.total_tokens;
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
this.lastTokenUsage = { ...usage };
|
|
457
|
+
}
|
|
458
|
+
if (stopReason === "max_tokens") {
|
|
459
|
+
const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens || 1024);
|
|
460
|
+
this.emit(AgentEvent_1.AgentEvent.MAX_TOKENS_EXCEEDED, error);
|
|
461
|
+
this.emit(AgentEvent_1.AgentEvent.ERROR, error);
|
|
462
|
+
if (this.vizEventId) {
|
|
463
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "MaxTokensExceededError", error.message, false);
|
|
464
|
+
this.vizEventId = undefined;
|
|
465
|
+
}
|
|
466
|
+
throw error;
|
|
467
|
+
}
|
|
468
|
+
// Rebuild the assistant turn in stream order (thinking → text → tool_use).
|
|
469
|
+
const orderedBlocks = Array.from(blocks.entries())
|
|
470
|
+
.sort(([a], [b]) => a - b)
|
|
471
|
+
.map(([, b]) => {
|
|
472
|
+
switch (b.kind) {
|
|
473
|
+
case "thinking":
|
|
474
|
+
return { type: "thinking", thinking: b.thinking, signature: b.signature };
|
|
475
|
+
case "redacted_thinking":
|
|
476
|
+
return { type: "redacted_thinking", data: b.data };
|
|
477
|
+
case "tool_use":
|
|
478
|
+
return {
|
|
479
|
+
type: "tool_use",
|
|
480
|
+
id: b.id,
|
|
481
|
+
name: b.name,
|
|
482
|
+
input: JSON.parse(b.inputJson || "{}"),
|
|
483
|
+
};
|
|
484
|
+
case "text":
|
|
485
|
+
return { type: "text", text: b.text, citations: [] };
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
// Fallback: preserve streamed text even if no text block start was observed.
|
|
489
|
+
if (textContent && !orderedBlocks.some((b) => b.type === "text")) {
|
|
490
|
+
orderedBlocks.push({ type: "text", text: textContent, citations: [] });
|
|
491
|
+
}
|
|
492
|
+
const toolUseBlocks = orderedBlocks.filter((b) => b.type === "tool_use");
|
|
493
|
+
if (stopReason === "tool_use" && toolUseBlocks.length > 0) {
|
|
494
|
+
this.emit(AgentEvent_1.AgentEvent.TOOL_USE, orderedBlocks);
|
|
495
|
+
this.currentToolCallCount += toolUseBlocks.length;
|
|
496
|
+
const assistantEntry = transformers_1.anthropicTransformer.fromProviderContent("assistant", orderedBlocks);
|
|
497
|
+
this.addToHistory(assistantEntry);
|
|
498
|
+
const toolResults = await this.handleToolUse(orderedBlocks);
|
|
499
|
+
this.addMessageToHistory("user", toolResults);
|
|
500
|
+
yield* this.streamTurn();
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
const assistantEntry = transformers_1.anthropicTransformer.fromProviderContent("assistant", orderedBlocks);
|
|
504
|
+
this.addToHistory(assistantEntry);
|
|
505
|
+
this.emit(AgentEvent_1.AgentEvent.DONE, { content: textContent }, this.lastTokenUsage);
|
|
506
|
+
if (this.vizEventId) {
|
|
507
|
+
VizReporter_1.vizReporter.agentComplete(this.vizEventId, {
|
|
508
|
+
input: this.lastTokenUsage?.input_tokens || 0,
|
|
509
|
+
output: this.lastTokenUsage?.output_tokens || 0,
|
|
510
|
+
total: this.lastTokenUsage?.total_tokens || 0,
|
|
511
|
+
}, "end_turn", this.currentToolCallCount > 0, this.currentToolCallCount, textContent);
|
|
512
|
+
this.vizEventId = undefined;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
308
516
|
parseUsage(input) {
|
|
309
517
|
return {
|
|
310
518
|
input_tokens: input.input_tokens,
|
|
@@ -118,7 +118,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
118
118
|
items: this.convertPropertyToSchema(prop.items),
|
|
119
119
|
description,
|
|
120
120
|
};
|
|
121
|
-
case "object":
|
|
121
|
+
case "object": {
|
|
122
122
|
const objProps = {};
|
|
123
123
|
if (prop.properties) {
|
|
124
124
|
const subProps = prop.properties;
|
|
@@ -132,6 +132,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
132
132
|
description,
|
|
133
133
|
required: prop.required,
|
|
134
134
|
};
|
|
135
|
+
}
|
|
135
136
|
default:
|
|
136
137
|
return { type: generative_ai_1.SchemaType.STRING, description };
|
|
137
138
|
}
|
|
@@ -2,6 +2,7 @@ import { BaseAgent, BaseAgentConfig, TokenUsage } from "../BaseAgent";
|
|
|
2
2
|
import { History, MessageContent } from "../../history/History";
|
|
3
3
|
import { Tool, Response, ResponseUsage } from "openai/resources/responses/responses";
|
|
4
4
|
import { OpenAIModel } from "../model-types";
|
|
5
|
+
import { StreamChunk } from "../openai-compatible/OpenAICompatibleAgent";
|
|
5
6
|
type AgentConfig = BaseAgentConfig & {
|
|
6
7
|
apiKey: string;
|
|
7
8
|
model?: OpenAIModel;
|
|
@@ -42,6 +43,20 @@ export declare class OpenAiAgent extends BaseAgent {
|
|
|
42
43
|
execute(input: string | MessageContent[]): Promise<string>;
|
|
43
44
|
protected handleResponse(response: Response): Promise<string>;
|
|
44
45
|
private handleToolUse;
|
|
46
|
+
/**
|
|
47
|
+
* Stream a response as an async generator of `StreamChunk` objects.
|
|
48
|
+
* Yields `{ type: "text" }` for visible output and `{ type: "reasoning" }` for
|
|
49
|
+
* reasoning summary tokens (o-series models). Tool calls are handled transparently.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* for await (const chunk of agent.executeStream("Explain recursion")) {
|
|
54
|
+
* if (chunk.type === "text") process.stdout.write(chunk.content);
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
executeStream(input: string | MessageContent[]): AsyncGenerator<StreamChunk>;
|
|
59
|
+
private streamTurn;
|
|
45
60
|
protected parseUsage(input: ResponseUsage): TokenUsage;
|
|
46
61
|
}
|
|
47
62
|
export {};
|