@agentionai/agents 1.6.0 → 1.7.0-beta.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/dist/agents/BaseAgent.d.ts +24 -2
- package/dist/agents/BaseAgent.js +17 -0
- package/dist/agents/anthropic/ClaudeAgent.d.ts +4 -3
- package/dist/agents/anthropic/ClaudeAgent.js +45 -15
- package/dist/agents/cancellation.d.ts +55 -0
- package/dist/agents/cancellation.js +72 -0
- package/dist/agents/errors/AgentError.d.ts +16 -0
- package/dist/agents/errors/AgentError.js +21 -1
- package/dist/agents/google/GeminiAgent.d.ts +3 -2
- package/dist/agents/google/GeminiAgent.js +27 -9
- package/dist/agents/mistral/MistralAgent.d.ts +3 -2
- package/dist/agents/mistral/MistralAgent.js +28 -11
- package/dist/agents/ollama/OllamaAgent.d.ts +17 -3
- package/dist/agents/ollama/OllamaAgent.js +69 -19
- package/dist/agents/openai/OpenAiAgent.d.ts +4 -3
- package/dist/agents/openai/OpenAiAgent.js +44 -15
- package/dist/agents/openai-compatible/OpenAICompatibleAgent.d.ts +4 -3
- package/dist/agents/openai-compatible/OpenAICompatibleAgent.js +46 -17
- package/dist/core.d.ts +1 -0
- package/dist/core.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/MCPClient.js +4 -2
- package/dist/tools/Tool.d.ts +13 -3
- package/dist/tools/Tool.js +18 -4
- package/package.json +1 -1
|
@@ -2,8 +2,11 @@ import EventEmitter from "events";
|
|
|
2
2
|
import { Tool } from "../tools/Tool";
|
|
3
3
|
import { History, HistoryEntry, MessageRole, MessageContent, ImageMimeType } from "../history/History";
|
|
4
4
|
import { AgentVendor, CommonAgentConfig, VendorSpecificConfig } from "./AgentConfig";
|
|
5
|
+
import { AbortError } from "./errors/AgentError";
|
|
6
|
+
import { ExecuteOptions } from "./cancellation";
|
|
5
7
|
export type { HistoryEntry, MessageRole, MessageContent, ImageMimeType };
|
|
6
8
|
export type { AgentVendor };
|
|
9
|
+
export type { ExecuteOptions };
|
|
7
10
|
/**
|
|
8
11
|
* Agent config as used across all agents
|
|
9
12
|
* @deprecated Use CommonAgentConfig with vendorConfig instead
|
|
@@ -173,9 +176,16 @@ export declare abstract class BaseAgent<TInput = unknown, TOutput = unknown> ext
|
|
|
173
176
|
*
|
|
174
177
|
*/
|
|
175
178
|
constructor(config: BaseAgentConfig, history?: History);
|
|
176
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Run the agent on `input`.
|
|
181
|
+
*
|
|
182
|
+
* @param options Per-run options. `options.signal` cancels the run: the
|
|
183
|
+
* in-flight provider request is aborted and the promise
|
|
184
|
+
* rejects with an {@link AbortError}.
|
|
185
|
+
*/
|
|
186
|
+
abstract execute(input: TInput, options?: ExecuteOptions): Promise<TOutput>;
|
|
177
187
|
protected abstract process(input: TInput): Promise<TOutput>;
|
|
178
|
-
protected abstract handleResponse(response: unknown): Promise<unknown>;
|
|
188
|
+
protected abstract handleResponse(response: unknown, options?: ExecuteOptions): Promise<unknown>;
|
|
179
189
|
protected getToolDefinitions(): unknown[];
|
|
180
190
|
/**
|
|
181
191
|
* List the models the provider currently offers, straight from its models
|
|
@@ -220,6 +230,18 @@ export declare abstract class BaseAgent<TInput = unknown, TOutput = unknown> ext
|
|
|
220
230
|
getHistoryEntries(): HistoryEntry[];
|
|
221
231
|
getTools(): Tool<unknown>[];
|
|
222
232
|
clearHistory(): void;
|
|
233
|
+
/**
|
|
234
|
+
* Build the {@link AbortError} for a cancelled run and emit it as an
|
|
235
|
+
* `AgentEvent.ERROR`, so a cancellation reaches error listeners the same way
|
|
236
|
+
* every other failure does.
|
|
237
|
+
*
|
|
238
|
+
* Returns the error rather than throwing it, leaving the caller to report it
|
|
239
|
+
* to whatever visualization event is open before rethrowing.
|
|
240
|
+
*
|
|
241
|
+
* @param error What the provider threw once the signal fired.
|
|
242
|
+
* @param signal The signal supplied to this run, if any.
|
|
243
|
+
*/
|
|
244
|
+
protected abortError(error: unknown, signal?: AbortSignal): AbortError;
|
|
223
245
|
protected abstract parseUsage(input: unknown): TokenUsage;
|
|
224
246
|
/**
|
|
225
247
|
* Clear accumulated usage. Called at the start of every `execute()` /
|
package/dist/agents/BaseAgent.js
CHANGED
|
@@ -8,6 +8,7 @@ const events_1 = __importDefault(require("events"));
|
|
|
8
8
|
const Tool_1 = require("../tools/Tool");
|
|
9
9
|
const History_1 = require("../history/History");
|
|
10
10
|
const AgentError_1 = require("./errors/AgentError");
|
|
11
|
+
const AgentEvent_1 = require("./AgentEvent");
|
|
11
12
|
/**
|
|
12
13
|
* The base agent is what the other agents are inheriting from
|
|
13
14
|
* Handles the BaseConfig
|
|
@@ -136,6 +137,22 @@ class BaseAgent extends events_1.default {
|
|
|
136
137
|
clearHistory() {
|
|
137
138
|
this.history.clear();
|
|
138
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Build the {@link AbortError} for a cancelled run and emit it as an
|
|
142
|
+
* `AgentEvent.ERROR`, so a cancellation reaches error listeners the same way
|
|
143
|
+
* every other failure does.
|
|
144
|
+
*
|
|
145
|
+
* Returns the error rather than throwing it, leaving the caller to report it
|
|
146
|
+
* to whatever visualization event is open before rethrowing.
|
|
147
|
+
*
|
|
148
|
+
* @param error What the provider threw once the signal fired.
|
|
149
|
+
* @param signal The signal supplied to this run, if any.
|
|
150
|
+
*/
|
|
151
|
+
abortError(error, signal) {
|
|
152
|
+
const abortError = new AgentError_1.AbortError(`Execution of agent ${this.getName()} was aborted`, signal?.reason ?? error);
|
|
153
|
+
this.emit(AgentEvent_1.AgentEvent.ERROR, abortError);
|
|
154
|
+
return abortError;
|
|
155
|
+
}
|
|
139
156
|
/**
|
|
140
157
|
* Clear accumulated usage. Called at the start of every `execute()` /
|
|
141
158
|
* `executeStream()` so `lastTokenUsage` describes a single execution.
|
|
@@ -3,6 +3,7 @@ import { Message, type ModelInfo as AnthropicModelInfo, ToolUnion, Usage } from
|
|
|
3
3
|
import { type ToolDefinition } from "../../tools/Tool";
|
|
4
4
|
import { type BuiltInTool } from "../../tools/BuiltInTool";
|
|
5
5
|
import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
|
|
6
|
+
import { ExecuteOptions } from "../cancellation";
|
|
6
7
|
import { History, MessageContent } from "../../history/History";
|
|
7
8
|
import { StreamChunk } from "../openai-compatible/OpenAICompatibleAgent";
|
|
8
9
|
import { ClaudeModel } from "../model-types";
|
|
@@ -151,8 +152,8 @@ export declare class ClaudeAgent extends BaseAgent {
|
|
|
151
152
|
metadata: Record<string, string> | undefined;
|
|
152
153
|
};
|
|
153
154
|
protected process(_input: string): Promise<string>;
|
|
154
|
-
execute(input: string | MessageContent[]): Promise<string>;
|
|
155
|
-
protected handleResponse(response: Message): Promise<string>;
|
|
155
|
+
execute(input: string | MessageContent[], options?: ExecuteOptions): Promise<string>;
|
|
156
|
+
protected handleResponse(response: Message, options?: ExecuteOptions): Promise<string>;
|
|
156
157
|
private handleToolUse;
|
|
157
158
|
/**
|
|
158
159
|
* Stream a response as an async generator of `StreamChunk` objects.
|
|
@@ -168,7 +169,7 @@ export declare class ClaudeAgent extends BaseAgent {
|
|
|
168
169
|
* }
|
|
169
170
|
* ```
|
|
170
171
|
*/
|
|
171
|
-
executeStream(input: string | MessageContent[]): AsyncGenerator<StreamChunk>;
|
|
172
|
+
executeStream(input: string | MessageContent[], options?: ExecuteOptions): AsyncGenerator<StreamChunk>;
|
|
172
173
|
private streamTurn;
|
|
173
174
|
protected parseUsage(input: Usage): TokenUsage;
|
|
174
175
|
}
|
|
@@ -4,6 +4,7 @@ exports.ClaudeAgent = void 0;
|
|
|
4
4
|
const sdk_1 = require("@anthropic-ai/sdk");
|
|
5
5
|
const BaseAgent_1 = require("../BaseAgent");
|
|
6
6
|
const AgentEvent_1 = require("../AgentEvent");
|
|
7
|
+
const cancellation_1 = require("../cancellation");
|
|
7
8
|
const AgentError_1 = require("../errors/AgentError");
|
|
8
9
|
const History_1 = require("../../history/History");
|
|
9
10
|
const transformers_1 = require("../../history/transformers");
|
|
@@ -143,7 +144,7 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
143
144
|
async process(_input) {
|
|
144
145
|
return "";
|
|
145
146
|
}
|
|
146
|
-
async execute(input) {
|
|
147
|
+
async execute(input, options) {
|
|
147
148
|
this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
|
|
148
149
|
// Reset token usage for this execution
|
|
149
150
|
this.resetTokenUsage();
|
|
@@ -173,11 +174,19 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
173
174
|
this.history.beginExecution();
|
|
174
175
|
try {
|
|
175
176
|
this.startTurnTimer();
|
|
176
|
-
const response = (await this.client.messages.create(this.buildMessageParams()));
|
|
177
|
+
const response = (await this.client.messages.create(this.buildMessageParams(), { signal: options?.signal }));
|
|
177
178
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, response);
|
|
178
|
-
return await this.handleResponse(response);
|
|
179
|
+
return await this.handleResponse(response, options);
|
|
179
180
|
}
|
|
180
181
|
catch (error) {
|
|
182
|
+
if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
|
|
183
|
+
const abortError = this.abortError(error, options?.signal);
|
|
184
|
+
if (this.vizEventId) {
|
|
185
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "AbortError", abortError.message, false);
|
|
186
|
+
this.vizEventId = undefined;
|
|
187
|
+
}
|
|
188
|
+
throw abortError;
|
|
189
|
+
}
|
|
181
190
|
if (error instanceof sdk_1.APIError) {
|
|
182
191
|
const apiError = new AgentError_1.ApiError(`Anthropic API error: ${error.message}`, error.status, error);
|
|
183
192
|
this.emit(AgentEvent_1.AgentEvent.ERROR, apiError);
|
|
@@ -204,7 +213,7 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
204
213
|
this.history.endExecution();
|
|
205
214
|
}
|
|
206
215
|
}
|
|
207
|
-
async handleResponse(response) {
|
|
216
|
+
async handleResponse(response, options) {
|
|
208
217
|
// Store token usage for metrics tracking
|
|
209
218
|
const usage = this.accumulateUsage(this.parseUsage(response.usage));
|
|
210
219
|
if (response.stop_reason === "max_tokens") {
|
|
@@ -253,19 +262,24 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
253
262
|
}
|
|
254
263
|
else if (response.stop_reason === "tool_use") {
|
|
255
264
|
try {
|
|
265
|
+
// Stop before the assistant turn is written: nothing else would notice
|
|
266
|
+
// a cancellation until the next provider call, and bailing out here
|
|
267
|
+
// avoids both running the tools' side effects and leaving a tool_use
|
|
268
|
+
// in history with no tool_result to answer it.
|
|
269
|
+
(0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
|
|
256
270
|
this.emit(AgentEvent_1.AgentEvent.TOOL_USE, response.content);
|
|
257
271
|
// Add assistant response to history (normalized format)
|
|
258
272
|
const assistantEntry = transformers_1.anthropicTransformer.fromProviderContent("assistant", response.content);
|
|
259
273
|
this.addToHistory(assistantEntry);
|
|
260
|
-
const toolResults = await this.handleToolUse(response.content);
|
|
274
|
+
const toolResults = await this.handleToolUse(response.content, options);
|
|
261
275
|
// Add tool results to history (normalized format)
|
|
262
276
|
this.addMessageToHistory("user", toolResults);
|
|
263
277
|
// Continue conversation with tool results
|
|
264
278
|
try {
|
|
265
279
|
this.startTurnTimer();
|
|
266
|
-
const newResponse = (await this.client.messages.create(this.buildMessageParams()));
|
|
280
|
+
const newResponse = (await this.client.messages.create(this.buildMessageParams(), { signal: options?.signal }));
|
|
267
281
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, newResponse);
|
|
268
|
-
return this.handleResponse(newResponse);
|
|
282
|
+
return this.handleResponse(newResponse, options);
|
|
269
283
|
}
|
|
270
284
|
catch (error) {
|
|
271
285
|
if (error instanceof sdk_1.APIError) {
|
|
@@ -292,7 +306,7 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
292
306
|
this.emit(AgentEvent_1.AgentEvent.ERROR, error);
|
|
293
307
|
throw error;
|
|
294
308
|
}
|
|
295
|
-
async handleToolUse(content) {
|
|
309
|
+
async handleToolUse(content, options) {
|
|
296
310
|
const toolUseBlocks = content.filter((block) => block.type === "tool_use");
|
|
297
311
|
if (!toolUseBlocks.length) {
|
|
298
312
|
throw new AgentError_1.ExecutionError("No tool use blocks found in content");
|
|
@@ -323,7 +337,7 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
323
337
|
? VizReporter_1.vizReporter.toolStart(block.name, block.id, block.input, agentSource)
|
|
324
338
|
: undefined;
|
|
325
339
|
try {
|
|
326
|
-
const result = await tool.execute(this.getId(), this.getName(), block.input, block.id, this.config.model, "anthropic");
|
|
340
|
+
const result = await tool.execute(this.getId(), this.getName(), block.input, block.id, this.config.model, "anthropic", { signal: options?.signal });
|
|
327
341
|
if (vizEventId) {
|
|
328
342
|
VizReporter_1.vizReporter.toolComplete(vizEventId, block.name, block.id, true, result);
|
|
329
343
|
}
|
|
@@ -358,7 +372,7 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
358
372
|
* }
|
|
359
373
|
* ```
|
|
360
374
|
*/
|
|
361
|
-
async *executeStream(input) {
|
|
375
|
+
async *executeStream(input, options) {
|
|
362
376
|
this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
|
|
363
377
|
this.resetTokenUsage();
|
|
364
378
|
this.currentToolCallCount = 0;
|
|
@@ -379,9 +393,17 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
379
393
|
this.history.setSessionAnchor();
|
|
380
394
|
this.history.beginExecution();
|
|
381
395
|
try {
|
|
382
|
-
yield* this.streamTurn();
|
|
396
|
+
yield* this.streamTurn(options);
|
|
383
397
|
}
|
|
384
398
|
catch (error) {
|
|
399
|
+
if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
|
|
400
|
+
const abortError = this.abortError(error, options?.signal);
|
|
401
|
+
if (this.vizEventId) {
|
|
402
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "AbortError", abortError.message, false);
|
|
403
|
+
this.vizEventId = undefined;
|
|
404
|
+
}
|
|
405
|
+
throw abortError;
|
|
406
|
+
}
|
|
385
407
|
if (error instanceof sdk_1.APIError) {
|
|
386
408
|
const apiError = new AgentError_1.ApiError(`Anthropic API error: ${error.message}`, error.status, error);
|
|
387
409
|
this.emit(AgentEvent_1.AgentEvent.ERROR, apiError);
|
|
@@ -409,12 +431,12 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
409
431
|
this.history.endExecution();
|
|
410
432
|
}
|
|
411
433
|
}
|
|
412
|
-
async *streamTurn() {
|
|
434
|
+
async *streamTurn(options) {
|
|
413
435
|
this.startTurnTimer();
|
|
414
436
|
const stream = await this.client.messages.create({
|
|
415
437
|
...this.buildMessageParams(),
|
|
416
438
|
stream: true,
|
|
417
|
-
});
|
|
439
|
+
}, { signal: options?.signal });
|
|
418
440
|
const blocks = new Map();
|
|
419
441
|
let textContent = "";
|
|
420
442
|
let stopReason = null;
|
|
@@ -482,6 +504,11 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
482
504
|
output_tokens: outputTokens,
|
|
483
505
|
total_tokens: inputTokens + outputTokens,
|
|
484
506
|
});
|
|
507
|
+
// The SDK's stream iterator swallows the abort and simply stops yielding,
|
|
508
|
+
// so without this an interrupted stream would look like a short but
|
|
509
|
+
// complete turn — writing partial text to history and emitting DONE.
|
|
510
|
+
// Checked after accumulateUsage() so the tokens already spent are reported.
|
|
511
|
+
(0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
|
|
485
512
|
if (stopReason === "max_tokens") {
|
|
486
513
|
const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens || 1024);
|
|
487
514
|
this.emit(AgentEvent_1.AgentEvent.MAX_TOKENS_EXCEEDED, error);
|
|
@@ -518,13 +545,16 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
518
545
|
}
|
|
519
546
|
const toolUseBlocks = orderedBlocks.filter((b) => b.type === "tool_use");
|
|
520
547
|
if (stopReason === "tool_use" && toolUseBlocks.length > 0) {
|
|
548
|
+
// As in handleResponse(): bail out before the assistant turn is written,
|
|
549
|
+
// so a cancelled run leaves no unanswered tool_use in history.
|
|
550
|
+
(0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
|
|
521
551
|
this.emit(AgentEvent_1.AgentEvent.TOOL_USE, orderedBlocks);
|
|
522
552
|
this.currentToolCallCount += toolUseBlocks.length;
|
|
523
553
|
const assistantEntry = transformers_1.anthropicTransformer.fromProviderContent("assistant", orderedBlocks);
|
|
524
554
|
this.addToHistory(assistantEntry);
|
|
525
|
-
const toolResults = await this.handleToolUse(orderedBlocks);
|
|
555
|
+
const toolResults = await this.handleToolUse(orderedBlocks, options);
|
|
526
556
|
this.addMessageToHistory("user", toolResults);
|
|
527
|
-
yield* this.streamTurn();
|
|
557
|
+
yield* this.streamTurn(options);
|
|
528
558
|
}
|
|
529
559
|
else {
|
|
530
560
|
const assistantEntry = transformers_1.anthropicTransformer.fromProviderContent("assistant", orderedBlocks);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options accepted by every agent's `execute()` and `executeStream()`.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* const controller = new AbortController();
|
|
7
|
+
* setTimeout(() => controller.abort(), 5_000);
|
|
8
|
+
*
|
|
9
|
+
* try {
|
|
10
|
+
* await agent.execute("Write an essay", { signal: controller.signal });
|
|
11
|
+
* } catch (error) {
|
|
12
|
+
* if (error instanceof AbortError) console.log("cancelled");
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export interface ExecuteOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Cancels the run. The in-flight provider request is aborted, no further
|
|
19
|
+
* requests or tools are started, and the call rejects with an
|
|
20
|
+
* {@link AbortError}. History already written by the run is left in place, so
|
|
21
|
+
* a non-transient agent keeps whatever turns completed before the abort.
|
|
22
|
+
*/
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
/** Options passed to a tool's `execute`, carrying the current run's signal. */
|
|
26
|
+
export interface ToolExecuteOptions {
|
|
27
|
+
/** The `AbortSignal` of the agent run this tool call belongs to. */
|
|
28
|
+
signal?: AbortSignal;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Whether an error represents a cancellation rather than a genuine failure.
|
|
32
|
+
*
|
|
33
|
+
* The signal is authoritative: once it has fired, whatever the provider threw
|
|
34
|
+
* on the way out is a consequence of the abort — including errors an agent
|
|
35
|
+
* wrapped in `ExecutionError` before it reached the top-level handler. The
|
|
36
|
+
* error's own `name` is only consulted when no aborted signal was supplied.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isAbortError(error: unknown, signal?: AbortSignal): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Throw an {@link AbortError} if the signal has already fired.
|
|
41
|
+
*
|
|
42
|
+
* Used at the points between provider calls where nothing would otherwise
|
|
43
|
+
* notice the cancellation — before starting tool execution in particular, so a
|
|
44
|
+
* cancelled run does not fire off side effects it will never use.
|
|
45
|
+
*/
|
|
46
|
+
export declare function throwIfAborted(signal: AbortSignal | undefined, context: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Combine two optional signals into one that fires as soon as either does.
|
|
49
|
+
*
|
|
50
|
+
* Returns the other signal unchanged when only one is present, and `undefined`
|
|
51
|
+
* when neither is. Written by hand rather than with `AbortSignal.any`, which
|
|
52
|
+
* needs Node 20.
|
|
53
|
+
*/
|
|
54
|
+
export declare function combineSignals(a?: AbortSignal, b?: AbortSignal): AbortSignal | undefined;
|
|
55
|
+
//# sourceMappingURL=cancellation.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isAbortError = isAbortError;
|
|
4
|
+
exports.throwIfAborted = throwIfAborted;
|
|
5
|
+
exports.combineSignals = combineSignals;
|
|
6
|
+
const AgentError_1 = require("./errors/AgentError");
|
|
7
|
+
/**
|
|
8
|
+
* The `name` each provider SDK gives its cancellation error. Checked as a
|
|
9
|
+
* fallback for the case where the caller's own signal is not the one that
|
|
10
|
+
* fired — a client-level timeout, for instance.
|
|
11
|
+
*/
|
|
12
|
+
const ABORT_ERROR_NAMES = new Set([
|
|
13
|
+
"AbortError", // fetch / DOMException, ollama, and our own AbortError
|
|
14
|
+
"APIUserAbortError", // @anthropic-ai/sdk, openai
|
|
15
|
+
"RequestAbortedError", // @mistralai/mistralai
|
|
16
|
+
"GoogleGenerativeAIAbortError", // @google/generative-ai
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Whether an error represents a cancellation rather than a genuine failure.
|
|
20
|
+
*
|
|
21
|
+
* The signal is authoritative: once it has fired, whatever the provider threw
|
|
22
|
+
* on the way out is a consequence of the abort — including errors an agent
|
|
23
|
+
* wrapped in `ExecutionError` before it reached the top-level handler. The
|
|
24
|
+
* error's own `name` is only consulted when no aborted signal was supplied.
|
|
25
|
+
*/
|
|
26
|
+
function isAbortError(error, signal) {
|
|
27
|
+
if (signal?.aborted)
|
|
28
|
+
return true;
|
|
29
|
+
const name = error?.name;
|
|
30
|
+
return typeof name === "string" && ABORT_ERROR_NAMES.has(name);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Throw an {@link AbortError} if the signal has already fired.
|
|
34
|
+
*
|
|
35
|
+
* Used at the points between provider calls where nothing would otherwise
|
|
36
|
+
* notice the cancellation — before starting tool execution in particular, so a
|
|
37
|
+
* cancelled run does not fire off side effects it will never use.
|
|
38
|
+
*/
|
|
39
|
+
function throwIfAborted(signal, context) {
|
|
40
|
+
if (!signal?.aborted)
|
|
41
|
+
return;
|
|
42
|
+
throw new AgentError_1.AbortError(`${context} was aborted`, signal.reason);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Combine two optional signals into one that fires as soon as either does.
|
|
46
|
+
*
|
|
47
|
+
* Returns the other signal unchanged when only one is present, and `undefined`
|
|
48
|
+
* when neither is. Written by hand rather than with `AbortSignal.any`, which
|
|
49
|
+
* needs Node 20.
|
|
50
|
+
*/
|
|
51
|
+
function combineSignals(a, b) {
|
|
52
|
+
if (!a)
|
|
53
|
+
return b;
|
|
54
|
+
if (!b)
|
|
55
|
+
return a;
|
|
56
|
+
if (a.aborted)
|
|
57
|
+
return a;
|
|
58
|
+
if (b.aborted)
|
|
59
|
+
return b;
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
const abort = (source) => () => controller.abort(source.reason);
|
|
62
|
+
const onA = abort(a);
|
|
63
|
+
const onB = abort(b);
|
|
64
|
+
a.addEventListener("abort", onA, { once: true });
|
|
65
|
+
b.addEventListener("abort", onB, { once: true });
|
|
66
|
+
controller.signal.addEventListener("abort", () => {
|
|
67
|
+
a.removeEventListener("abort", onA);
|
|
68
|
+
b.removeEventListener("abort", onB);
|
|
69
|
+
}, { once: true });
|
|
70
|
+
return controller.signal;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=cancellation.js.map
|
|
@@ -36,6 +36,22 @@ export declare class MaxRetriesExceededError extends AgentError {
|
|
|
36
36
|
maxRetries: number;
|
|
37
37
|
constructor(message: string, maxRetries: number);
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Error thrown when an execution is cancelled through the `AbortSignal` passed
|
|
41
|
+
* to `execute()` / `executeStream()`.
|
|
42
|
+
*
|
|
43
|
+
* `name` is `"AbortError"`, matching the platform convention, so callers that
|
|
44
|
+
* already check `err.name === "AbortError"` keep working.
|
|
45
|
+
*/
|
|
46
|
+
export declare class AbortError extends AgentError {
|
|
47
|
+
reason?: unknown | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* @param message Error message
|
|
50
|
+
* @param reason The signal's `reason`, or the underlying provider error when
|
|
51
|
+
* the signal did not carry one.
|
|
52
|
+
*/
|
|
53
|
+
constructor(message: string, reason?: unknown | undefined);
|
|
54
|
+
}
|
|
39
55
|
/**
|
|
40
56
|
* Error thrown when tool execution fails
|
|
41
57
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ToolExecutionError = exports.MaxRetriesExceededError = exports.MaxTokensExceededError = exports.ApiError = exports.ExecutionError = exports.AgentError = void 0;
|
|
3
|
+
exports.ToolExecutionError = exports.AbortError = exports.MaxRetriesExceededError = exports.MaxTokensExceededError = exports.ApiError = exports.ExecutionError = exports.AgentError = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Base error class for all agent-related errors
|
|
6
6
|
*/
|
|
@@ -59,6 +59,26 @@ class MaxRetriesExceededError extends AgentError {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
exports.MaxRetriesExceededError = MaxRetriesExceededError;
|
|
62
|
+
/**
|
|
63
|
+
* Error thrown when an execution is cancelled through the `AbortSignal` passed
|
|
64
|
+
* to `execute()` / `executeStream()`.
|
|
65
|
+
*
|
|
66
|
+
* `name` is `"AbortError"`, matching the platform convention, so callers that
|
|
67
|
+
* already check `err.name === "AbortError"` keep working.
|
|
68
|
+
*/
|
|
69
|
+
class AbortError extends AgentError {
|
|
70
|
+
/**
|
|
71
|
+
* @param message Error message
|
|
72
|
+
* @param reason The signal's `reason`, or the underlying provider error when
|
|
73
|
+
* the signal did not carry one.
|
|
74
|
+
*/
|
|
75
|
+
constructor(message, reason) {
|
|
76
|
+
super(message);
|
|
77
|
+
this.reason = reason;
|
|
78
|
+
this.name = "AbortError";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.AbortError = AbortError;
|
|
62
82
|
/**
|
|
63
83
|
* Error thrown when tool execution fails
|
|
64
84
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FunctionDeclarationsTool, GenerateContentResult, Schema } from "@google/generative-ai";
|
|
2
2
|
import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
|
|
3
|
+
import { ExecuteOptions } from "../cancellation";
|
|
3
4
|
import { History, MessageContent } from "../../history/History";
|
|
4
5
|
import { GeminiModel } from "../model-types";
|
|
5
6
|
/**
|
|
@@ -123,8 +124,8 @@ export declare class GeminiAgent extends BaseAgent {
|
|
|
123
124
|
*/
|
|
124
125
|
private mapJsonSchemaTypeToGemini;
|
|
125
126
|
protected process(_input: string): Promise<string>;
|
|
126
|
-
execute(input: string | MessageContent[]): Promise<string>;
|
|
127
|
-
protected handleResponse(response: GenerateContentResult): Promise<string>;
|
|
127
|
+
execute(input: string | MessageContent[], options?: ExecuteOptions): Promise<string>;
|
|
128
|
+
protected handleResponse(response: GenerateContentResult, options?: ExecuteOptions): Promise<string>;
|
|
128
129
|
private handleFunctionCalls;
|
|
129
130
|
protected parseUsage(input: {
|
|
130
131
|
promptTokenCount?: number;
|
|
@@ -4,6 +4,7 @@ exports.GeminiAgent = exports.GEMINI_RETIRED_MODELS = void 0;
|
|
|
4
4
|
const generative_ai_1 = require("@google/generative-ai");
|
|
5
5
|
const BaseAgent_1 = require("../BaseAgent");
|
|
6
6
|
const AgentEvent_1 = require("../AgentEvent");
|
|
7
|
+
const cancellation_1 = require("../cancellation");
|
|
7
8
|
const AgentError_1 = require("../errors/AgentError");
|
|
8
9
|
const transformers_1 = require("../../history/transformers");
|
|
9
10
|
const VizReporter_1 = require("../../viz/VizReporter");
|
|
@@ -251,7 +252,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
251
252
|
async process(_input) {
|
|
252
253
|
return "";
|
|
253
254
|
}
|
|
254
|
-
async execute(input) {
|
|
255
|
+
async execute(input, options) {
|
|
255
256
|
this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
|
|
256
257
|
// Reset token usage for this execution
|
|
257
258
|
this.resetTokenUsage();
|
|
@@ -297,11 +298,23 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
297
298
|
responseMimeType: this.config.responseMimeType,
|
|
298
299
|
responseSchema: this.config.responseSchema,
|
|
299
300
|
},
|
|
300
|
-
}
|
|
301
|
+
},
|
|
302
|
+
// Note: an abort only stops the client from waiting — Google still
|
|
303
|
+
// runs and bills the request. Merged over the model's own request
|
|
304
|
+
// options, so `customHeaders` set at construction survive.
|
|
305
|
+
{ signal: options?.signal });
|
|
301
306
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, response);
|
|
302
|
-
return await this.handleResponse(response);
|
|
307
|
+
return await this.handleResponse(response, options);
|
|
303
308
|
}
|
|
304
309
|
catch (error) {
|
|
310
|
+
if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
|
|
311
|
+
const abortError = this.abortError(error, options?.signal);
|
|
312
|
+
if (this.vizEventId) {
|
|
313
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "AbortError", abortError.message, false);
|
|
314
|
+
this.vizEventId = undefined;
|
|
315
|
+
}
|
|
316
|
+
throw abortError;
|
|
317
|
+
}
|
|
305
318
|
const err = error;
|
|
306
319
|
if (err.status) {
|
|
307
320
|
const apiError = new AgentError_1.ApiError(`Gemini API error: ${err.message || "Unknown error"}`, err.status, error);
|
|
@@ -328,7 +341,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
328
341
|
this.history.endExecution();
|
|
329
342
|
}
|
|
330
343
|
}
|
|
331
|
-
async handleResponse(response) {
|
|
344
|
+
async handleResponse(response, options) {
|
|
332
345
|
const result = response.response;
|
|
333
346
|
// Parse and track usage
|
|
334
347
|
if (result.usageMetadata) {
|
|
@@ -377,11 +390,16 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
377
390
|
}
|
|
378
391
|
// Handle function calls
|
|
379
392
|
try {
|
|
393
|
+
// Stop before the assistant turn is written: nothing else would notice a
|
|
394
|
+
// cancellation until the next provider call, and bailing out here avoids
|
|
395
|
+
// both running the tools' side effects and leaving a functionCall in
|
|
396
|
+
// history with no functionResponse to answer it.
|
|
397
|
+
(0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
|
|
380
398
|
this.emit(AgentEvent_1.AgentEvent.TOOL_USE, functionCalls);
|
|
381
399
|
// Add assistant response with function calls to history (normalized)
|
|
382
400
|
const assistantEntry = transformers_1.geminiTransformer.fromProviderContent("assistant", parts);
|
|
383
401
|
this.addToHistory(assistantEntry);
|
|
384
|
-
const toolResults = await this.handleFunctionCalls(functionCalls);
|
|
402
|
+
const toolResults = await this.handleFunctionCalls(functionCalls, options);
|
|
385
403
|
// Add tool results to history (normalized)
|
|
386
404
|
for (const tr of toolResults) {
|
|
387
405
|
const resultEntry = transformers_1.geminiTransformer.toolResultEntry(tr.name, tr.response);
|
|
@@ -407,9 +425,9 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
407
425
|
responseMimeType: this.config.responseMimeType,
|
|
408
426
|
responseSchema: this.config.responseSchema,
|
|
409
427
|
},
|
|
410
|
-
});
|
|
428
|
+
}, { signal: options?.signal });
|
|
411
429
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, newResponse);
|
|
412
|
-
return this.handleResponse(newResponse);
|
|
430
|
+
return this.handleResponse(newResponse, options);
|
|
413
431
|
}
|
|
414
432
|
catch (error) {
|
|
415
433
|
const err = error;
|
|
@@ -438,7 +456,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
438
456
|
throw toolError;
|
|
439
457
|
}
|
|
440
458
|
}
|
|
441
|
-
async handleFunctionCalls(functionCalls) {
|
|
459
|
+
async handleFunctionCalls(functionCalls, options) {
|
|
442
460
|
if (!functionCalls.length) {
|
|
443
461
|
throw new AgentError_1.ExecutionError("No function calls found in response");
|
|
444
462
|
}
|
|
@@ -462,7 +480,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
462
480
|
try {
|
|
463
481
|
const args = (fc.args || {});
|
|
464
482
|
const result = await tool.execute(this.getId(), this.getName(), args, toolName, // Gemini uses function name as ID
|
|
465
|
-
this.config.model, "gemini");
|
|
483
|
+
this.config.model, "gemini", { signal: options?.signal });
|
|
466
484
|
return {
|
|
467
485
|
name: toolName,
|
|
468
486
|
response: JSON.stringify(result),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
|
|
2
|
+
import { ExecuteOptions } from "../cancellation";
|
|
2
3
|
import { History, MessageContent } from "../../history/History";
|
|
3
4
|
import { ChatCompletionResponse, ModelList, Tool, UsageInfo } from "@mistralai/mistralai/models/components";
|
|
4
5
|
import { MistralModel } from "../model-types";
|
|
@@ -66,8 +67,8 @@ export declare class MistralAgent extends BaseAgent {
|
|
|
66
67
|
listModels(): Promise<ModelInfo<MistralModelCard>[]>;
|
|
67
68
|
protected getToolDefinitions(): Tool[];
|
|
68
69
|
protected process(_input: string): Promise<string>;
|
|
69
|
-
execute(input: string | MessageContent[]): Promise<string>;
|
|
70
|
-
protected handleResponse(response: ChatCompletionResponse): Promise<string>;
|
|
70
|
+
execute(input: string | MessageContent[], options?: ExecuteOptions): Promise<string>;
|
|
71
|
+
protected handleResponse(response: ChatCompletionResponse, options?: ExecuteOptions): Promise<string>;
|
|
71
72
|
private handleToolCalls;
|
|
72
73
|
protected parseUsage(input: UsageInfo): TokenUsage;
|
|
73
74
|
}
|