@agentionai/agents 1.6.0 → 1.8.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/README.md +4 -2
- package/dist/agents/Agent.d.ts +9 -2
- package/dist/agents/Agent.js +4 -0
- package/dist/agents/AgentConfig.d.ts +76 -2
- 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 +47 -17
- package/dist/agents/cancellation.d.ts +55 -0
- package/dist/agents/cancellation.js +72 -0
- package/dist/agents/errors/AgentError.d.ts +50 -2
- package/dist/agents/errors/AgentError.js +57 -1
- package/dist/agents/google/GeminiAgent.d.ts +3 -2
- package/dist/agents/google/GeminiAgent.js +34 -11
- package/dist/agents/mistral/MistralAgent.d.ts +3 -2
- package/dist/agents/mistral/MistralAgent.js +33 -13
- 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 +52 -17
- package/dist/agents/openai-compatible/OpenAICompatibleAgent.d.ts +4 -3
- package/dist/agents/openai-compatible/OpenAICompatibleAgent.js +48 -19
- package/dist/agents/openrouter/OpenRouterAgent.d.ts +234 -0
- package/dist/agents/openrouter/OpenRouterAgent.js +711 -0
- package/dist/agents/openrouter/types.d.ts +164 -0
- package/dist/agents/openrouter/types.js +15 -0
- package/dist/core.d.ts +1 -0
- package/dist/core.js +1 -0
- package/dist/history/transformers.d.ts +80 -0
- package/dist/history/transformers.js +156 -1
- package/dist/history/types.d.ts +22 -2
- package/dist/history/types.js +13 -2
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/mcp/MCPClient.js +4 -2
- package/dist/openrouter.d.ts +6 -0
- package/dist/openrouter.js +24 -0
- package/dist/tools/Tool.d.ts +13 -3
- package/dist/tools/Tool.js +18 -4
- package/dist/viz/types.d.ts +1 -1
- package/package.json +10 -1
|
@@ -22,12 +22,44 @@ export declare class ApiError extends AgentError {
|
|
|
22
22
|
response?: any | undefined;
|
|
23
23
|
constructor(message: string, statusCode?: number | undefined, response?: any | undefined);
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Error thrown when the provider rate limits a request and the agent's retry
|
|
27
|
+
* policy did not manage to get past it.
|
|
28
|
+
*
|
|
29
|
+
* Extends {@link ApiError}, so existing `catch (e) { if (e instanceof ApiError) }`
|
|
30
|
+
* handling keeps working; the extra fields carry what the provider said about
|
|
31
|
+
* when to come back.
|
|
32
|
+
*/
|
|
33
|
+
export declare class RateLimitError extends ApiError {
|
|
34
|
+
retryAfterMs?: number | undefined;
|
|
35
|
+
limit?: number | undefined;
|
|
36
|
+
remaining?: number | undefined;
|
|
37
|
+
resetAt?: Date | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* @param message Error message
|
|
40
|
+
* @param retryAfterMs How long the provider asked you to wait, in
|
|
41
|
+
* milliseconds, from its `Retry-After` header. Undefined
|
|
42
|
+
* when it sent no hint.
|
|
43
|
+
* @param limit Requests permitted in the window (`X-RateLimit-Limit`).
|
|
44
|
+
* @param remaining Requests left in the window (`X-RateLimit-Remaining`).
|
|
45
|
+
* @param resetAt When the window resets (`X-RateLimit-Reset`).
|
|
46
|
+
* @param response The underlying provider error.
|
|
47
|
+
*/
|
|
48
|
+
constructor(message: string, retryAfterMs?: number | undefined, limit?: number | undefined, remaining?: number | undefined, resetAt?: Date | undefined, response?: unknown);
|
|
49
|
+
}
|
|
25
50
|
/**
|
|
26
51
|
* Error thrown when maximum token limit is exceeded
|
|
27
52
|
*/
|
|
28
53
|
export declare class MaxTokensExceededError extends AgentError {
|
|
29
|
-
tokenLimit
|
|
30
|
-
|
|
54
|
+
tokenLimit?: number | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* @param message Error message
|
|
57
|
+
* @param tokenLimit The configured `maxTokens` that was hit. Undefined when
|
|
58
|
+
* no limit was configured and the model's own output cap
|
|
59
|
+
* is what ended the response — reporting a number the
|
|
60
|
+
* caller never set would just be misleading.
|
|
61
|
+
*/
|
|
62
|
+
constructor(message: string, tokenLimit?: number | undefined);
|
|
31
63
|
}
|
|
32
64
|
/**
|
|
33
65
|
* Error thrown when maximum retries are exceeded
|
|
@@ -36,6 +68,22 @@ export declare class MaxRetriesExceededError extends AgentError {
|
|
|
36
68
|
maxRetries: number;
|
|
37
69
|
constructor(message: string, maxRetries: number);
|
|
38
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Error thrown when an execution is cancelled through the `AbortSignal` passed
|
|
73
|
+
* to `execute()` / `executeStream()`.
|
|
74
|
+
*
|
|
75
|
+
* `name` is `"AbortError"`, matching the platform convention, so callers that
|
|
76
|
+
* already check `err.name === "AbortError"` keep working.
|
|
77
|
+
*/
|
|
78
|
+
export declare class AbortError extends AgentError {
|
|
79
|
+
reason?: unknown | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* @param message Error message
|
|
82
|
+
* @param reason The signal's `reason`, or the underlying provider error when
|
|
83
|
+
* the signal did not carry one.
|
|
84
|
+
*/
|
|
85
|
+
constructor(message: string, reason?: unknown | undefined);
|
|
86
|
+
}
|
|
39
87
|
/**
|
|
40
88
|
* Error thrown when tool execution fails
|
|
41
89
|
*/
|
|
@@ -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.RateLimitError = exports.ApiError = exports.ExecutionError = exports.AgentError = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Base error class for all agent-related errors
|
|
6
6
|
*/
|
|
@@ -37,10 +37,46 @@ class ApiError extends AgentError {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
exports.ApiError = ApiError;
|
|
40
|
+
/**
|
|
41
|
+
* Error thrown when the provider rate limits a request and the agent's retry
|
|
42
|
+
* policy did not manage to get past it.
|
|
43
|
+
*
|
|
44
|
+
* Extends {@link ApiError}, so existing `catch (e) { if (e instanceof ApiError) }`
|
|
45
|
+
* handling keeps working; the extra fields carry what the provider said about
|
|
46
|
+
* when to come back.
|
|
47
|
+
*/
|
|
48
|
+
class RateLimitError extends ApiError {
|
|
49
|
+
/**
|
|
50
|
+
* @param message Error message
|
|
51
|
+
* @param retryAfterMs How long the provider asked you to wait, in
|
|
52
|
+
* milliseconds, from its `Retry-After` header. Undefined
|
|
53
|
+
* when it sent no hint.
|
|
54
|
+
* @param limit Requests permitted in the window (`X-RateLimit-Limit`).
|
|
55
|
+
* @param remaining Requests left in the window (`X-RateLimit-Remaining`).
|
|
56
|
+
* @param resetAt When the window resets (`X-RateLimit-Reset`).
|
|
57
|
+
* @param response The underlying provider error.
|
|
58
|
+
*/
|
|
59
|
+
constructor(message, retryAfterMs, limit, remaining, resetAt, response) {
|
|
60
|
+
super(message, 429, response);
|
|
61
|
+
this.retryAfterMs = retryAfterMs;
|
|
62
|
+
this.limit = limit;
|
|
63
|
+
this.remaining = remaining;
|
|
64
|
+
this.resetAt = resetAt;
|
|
65
|
+
this.name = "RateLimitError";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.RateLimitError = RateLimitError;
|
|
40
69
|
/**
|
|
41
70
|
* Error thrown when maximum token limit is exceeded
|
|
42
71
|
*/
|
|
43
72
|
class MaxTokensExceededError extends AgentError {
|
|
73
|
+
/**
|
|
74
|
+
* @param message Error message
|
|
75
|
+
* @param tokenLimit The configured `maxTokens` that was hit. Undefined when
|
|
76
|
+
* no limit was configured and the model's own output cap
|
|
77
|
+
* is what ended the response — reporting a number the
|
|
78
|
+
* caller never set would just be misleading.
|
|
79
|
+
*/
|
|
44
80
|
constructor(message, tokenLimit) {
|
|
45
81
|
super(message);
|
|
46
82
|
this.tokenLimit = tokenLimit;
|
|
@@ -59,6 +95,26 @@ class MaxRetriesExceededError extends AgentError {
|
|
|
59
95
|
}
|
|
60
96
|
}
|
|
61
97
|
exports.MaxRetriesExceededError = MaxRetriesExceededError;
|
|
98
|
+
/**
|
|
99
|
+
* Error thrown when an execution is cancelled through the `AbortSignal` passed
|
|
100
|
+
* to `execute()` / `executeStream()`.
|
|
101
|
+
*
|
|
102
|
+
* `name` is `"AbortError"`, matching the platform convention, so callers that
|
|
103
|
+
* already check `err.name === "AbortError"` keep working.
|
|
104
|
+
*/
|
|
105
|
+
class AbortError extends AgentError {
|
|
106
|
+
/**
|
|
107
|
+
* @param message Error message
|
|
108
|
+
* @param reason The signal's `reason`, or the underlying provider error when
|
|
109
|
+
* the signal did not carry one.
|
|
110
|
+
*/
|
|
111
|
+
constructor(message, reason) {
|
|
112
|
+
super(message);
|
|
113
|
+
this.reason = reason;
|
|
114
|
+
this.name = "AbortError";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.AbortError = AbortError;
|
|
62
118
|
/**
|
|
63
119
|
* Error thrown when tool execution fails
|
|
64
120
|
*/
|
|
@@ -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");
|
|
@@ -66,7 +67,12 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
66
67
|
const responseSchema = config.responseSchema ?? vendorConfig.responseSchema;
|
|
67
68
|
this.config = {
|
|
68
69
|
model: config.model || "gemini-flash-latest",
|
|
69
|
-
|
|
70
|
+
// No default: `maxOutputTokens` is optional on Gemini, and omitting it
|
|
71
|
+
// lets the model use its full output budget. A default here silently
|
|
72
|
+
// truncated every response — and on thinking models it was worse, since
|
|
73
|
+
// thoughts count against the same budget and a small cap could be spent
|
|
74
|
+
// entirely on them.
|
|
75
|
+
maxTokens: config.maxTokens,
|
|
70
76
|
apiKey: config.apiKey,
|
|
71
77
|
temperature: config.temperature,
|
|
72
78
|
topP: config.topP,
|
|
@@ -251,7 +257,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
251
257
|
async process(_input) {
|
|
252
258
|
return "";
|
|
253
259
|
}
|
|
254
|
-
async execute(input) {
|
|
260
|
+
async execute(input, options) {
|
|
255
261
|
this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
|
|
256
262
|
// Reset token usage for this execution
|
|
257
263
|
this.resetTokenUsage();
|
|
@@ -297,11 +303,23 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
297
303
|
responseMimeType: this.config.responseMimeType,
|
|
298
304
|
responseSchema: this.config.responseSchema,
|
|
299
305
|
},
|
|
300
|
-
}
|
|
306
|
+
},
|
|
307
|
+
// Note: an abort only stops the client from waiting — Google still
|
|
308
|
+
// runs and bills the request. Merged over the model's own request
|
|
309
|
+
// options, so `customHeaders` set at construction survive.
|
|
310
|
+
{ signal: options?.signal });
|
|
301
311
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, response);
|
|
302
|
-
return await this.handleResponse(response);
|
|
312
|
+
return await this.handleResponse(response, options);
|
|
303
313
|
}
|
|
304
314
|
catch (error) {
|
|
315
|
+
if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
|
|
316
|
+
const abortError = this.abortError(error, options?.signal);
|
|
317
|
+
if (this.vizEventId) {
|
|
318
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "AbortError", abortError.message, false);
|
|
319
|
+
this.vizEventId = undefined;
|
|
320
|
+
}
|
|
321
|
+
throw abortError;
|
|
322
|
+
}
|
|
305
323
|
const err = error;
|
|
306
324
|
if (err.status) {
|
|
307
325
|
const apiError = new AgentError_1.ApiError(`Gemini API error: ${err.message || "Unknown error"}`, err.status, error);
|
|
@@ -328,7 +346,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
328
346
|
this.history.endExecution();
|
|
329
347
|
}
|
|
330
348
|
}
|
|
331
|
-
async handleResponse(response) {
|
|
349
|
+
async handleResponse(response, options) {
|
|
332
350
|
const result = response.response;
|
|
333
351
|
// Parse and track usage
|
|
334
352
|
if (result.usageMetadata) {
|
|
@@ -342,7 +360,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
342
360
|
throw error;
|
|
343
361
|
}
|
|
344
362
|
if (candidate.finishReason === "MAX_TOKENS") {
|
|
345
|
-
const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens
|
|
363
|
+
const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens);
|
|
346
364
|
this.emit(AgentEvent_1.AgentEvent.MAX_TOKENS_EXCEEDED, error);
|
|
347
365
|
this.emit(AgentEvent_1.AgentEvent.ERROR, error);
|
|
348
366
|
// Report error to viz
|
|
@@ -377,11 +395,16 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
377
395
|
}
|
|
378
396
|
// Handle function calls
|
|
379
397
|
try {
|
|
398
|
+
// Stop before the assistant turn is written: nothing else would notice a
|
|
399
|
+
// cancellation until the next provider call, and bailing out here avoids
|
|
400
|
+
// both running the tools' side effects and leaving a functionCall in
|
|
401
|
+
// history with no functionResponse to answer it.
|
|
402
|
+
(0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
|
|
380
403
|
this.emit(AgentEvent_1.AgentEvent.TOOL_USE, functionCalls);
|
|
381
404
|
// Add assistant response with function calls to history (normalized)
|
|
382
405
|
const assistantEntry = transformers_1.geminiTransformer.fromProviderContent("assistant", parts);
|
|
383
406
|
this.addToHistory(assistantEntry);
|
|
384
|
-
const toolResults = await this.handleFunctionCalls(functionCalls);
|
|
407
|
+
const toolResults = await this.handleFunctionCalls(functionCalls, options);
|
|
385
408
|
// Add tool results to history (normalized)
|
|
386
409
|
for (const tr of toolResults) {
|
|
387
410
|
const resultEntry = transformers_1.geminiTransformer.toolResultEntry(tr.name, tr.response);
|
|
@@ -407,9 +430,9 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
407
430
|
responseMimeType: this.config.responseMimeType,
|
|
408
431
|
responseSchema: this.config.responseSchema,
|
|
409
432
|
},
|
|
410
|
-
});
|
|
433
|
+
}, { signal: options?.signal });
|
|
411
434
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, newResponse);
|
|
412
|
-
return this.handleResponse(newResponse);
|
|
435
|
+
return this.handleResponse(newResponse, options);
|
|
413
436
|
}
|
|
414
437
|
catch (error) {
|
|
415
438
|
const err = error;
|
|
@@ -438,7 +461,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
438
461
|
throw toolError;
|
|
439
462
|
}
|
|
440
463
|
}
|
|
441
|
-
async handleFunctionCalls(functionCalls) {
|
|
464
|
+
async handleFunctionCalls(functionCalls, options) {
|
|
442
465
|
if (!functionCalls.length) {
|
|
443
466
|
throw new AgentError_1.ExecutionError("No function calls found in response");
|
|
444
467
|
}
|
|
@@ -462,7 +485,7 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
462
485
|
try {
|
|
463
486
|
const args = (fc.args || {});
|
|
464
487
|
const result = await tool.execute(this.getId(), this.getName(), args, toolName, // Gemini uses function name as ID
|
|
465
|
-
this.config.model, "gemini");
|
|
488
|
+
this.config.model, "gemini", { signal: options?.signal });
|
|
466
489
|
return {
|
|
467
490
|
name: toolName,
|
|
468
491
|
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
|
}
|
|
@@ -6,6 +6,7 @@ const mistralai_1 = require("@mistralai/mistralai");
|
|
|
6
6
|
const http_1 = require("@mistralai/mistralai/lib/http");
|
|
7
7
|
const BaseAgent_1 = require("../BaseAgent");
|
|
8
8
|
const AgentEvent_1 = require("../AgentEvent");
|
|
9
|
+
const cancellation_1 = require("../cancellation");
|
|
9
10
|
const AgentError_1 = require("../errors/AgentError");
|
|
10
11
|
const transformers_1 = require("../../history/transformers");
|
|
11
12
|
const components_1 = require("@mistralai/mistralai/models/components");
|
|
@@ -67,7 +68,10 @@ class MistralAgent extends BaseAgent_1.BaseAgent {
|
|
|
67
68
|
const rateLimitDelay = config.rateLimitDelay ?? vendorConfig.rateLimitDelay ?? 1500;
|
|
68
69
|
this.config = {
|
|
69
70
|
model: config.model || "mistral-small-latest",
|
|
70
|
-
|
|
71
|
+
// No default: `maxTokens` is optional on Mistral's API, and omitting it
|
|
72
|
+
// lets the model use its full output budget. A default here silently
|
|
73
|
+
// truncated every response.
|
|
74
|
+
maxTokens: config.maxTokens,
|
|
71
75
|
disableParallelToolUse,
|
|
72
76
|
safePrompt,
|
|
73
77
|
randomSeed,
|
|
@@ -129,7 +133,7 @@ class MistralAgent extends BaseAgent_1.BaseAgent {
|
|
|
129
133
|
async process(_input) {
|
|
130
134
|
return "";
|
|
131
135
|
}
|
|
132
|
-
async execute(input) {
|
|
136
|
+
async execute(input, options) {
|
|
133
137
|
this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
|
|
134
138
|
// Reset token usage for this execution
|
|
135
139
|
this.resetTokenUsage();
|
|
@@ -169,11 +173,19 @@ class MistralAgent extends BaseAgent_1.BaseAgent {
|
|
|
169
173
|
randomSeed: this.config.randomSeed,
|
|
170
174
|
safePrompt: this.config.safePrompt,
|
|
171
175
|
stop: this.config.stopSequences,
|
|
172
|
-
});
|
|
176
|
+
}, { signal: options?.signal });
|
|
173
177
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, response);
|
|
174
|
-
return await this.handleResponse(response);
|
|
178
|
+
return await this.handleResponse(response, options);
|
|
175
179
|
}
|
|
176
180
|
catch (error) {
|
|
181
|
+
if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
|
|
182
|
+
const abortError = this.abortError(error, options?.signal);
|
|
183
|
+
if (this.vizEventId) {
|
|
184
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "AbortError", abortError.message, false);
|
|
185
|
+
this.vizEventId = undefined;
|
|
186
|
+
}
|
|
187
|
+
throw abortError;
|
|
188
|
+
}
|
|
177
189
|
const err = error;
|
|
178
190
|
if (err.status) {
|
|
179
191
|
const apiError = new AgentError_1.ApiError(`Mistral API error: ${err.message || "Unknown error"}`, err.status, error);
|
|
@@ -200,7 +212,7 @@ class MistralAgent extends BaseAgent_1.BaseAgent {
|
|
|
200
212
|
this.history.endExecution();
|
|
201
213
|
}
|
|
202
214
|
}
|
|
203
|
-
async handleResponse(response) {
|
|
215
|
+
async handleResponse(response, options) {
|
|
204
216
|
if (!response.choices || response.choices.length === 0) {
|
|
205
217
|
const error = new AgentError_1.ExecutionError("Empty response from Mistral API");
|
|
206
218
|
this.emit(AgentEvent_1.AgentEvent.ERROR, error);
|
|
@@ -210,7 +222,7 @@ class MistralAgent extends BaseAgent_1.BaseAgent {
|
|
|
210
222
|
// Track token usage
|
|
211
223
|
const usage = this.accumulateUsage(this.parseUsage(response.usage));
|
|
212
224
|
if (choice.finishReason === "length") {
|
|
213
|
-
const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens
|
|
225
|
+
const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens);
|
|
214
226
|
this.emit(AgentEvent_1.AgentEvent.MAX_TOKENS_EXCEEDED, error);
|
|
215
227
|
this.emit(AgentEvent_1.AgentEvent.ERROR, error);
|
|
216
228
|
// Report error to viz
|
|
@@ -255,18 +267,26 @@ class MistralAgent extends BaseAgent_1.BaseAgent {
|
|
|
255
267
|
}
|
|
256
268
|
else if (choice.finishReason === "tool_calls" || message.toolCalls) {
|
|
257
269
|
try {
|
|
270
|
+
// Stop before the assistant turn is written: nothing else would notice
|
|
271
|
+
// a cancellation until the next provider call, and bailing out here
|
|
272
|
+
// avoids both running the tools' side effects and leaving a tool call
|
|
273
|
+
// in history with no tool message to answer it.
|
|
274
|
+
(0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
|
|
258
275
|
this.emit(AgentEvent_1.AgentEvent.TOOL_USE, message.toolCalls);
|
|
259
276
|
// Add assistant message with tool calls to history (normalized)
|
|
260
277
|
const assistantEntry = transformers_1.mistralTransformer.fromProviderMessage(message);
|
|
261
278
|
this.addToHistory(assistantEntry);
|
|
262
|
-
const toolResults = await this.handleToolCalls(message.toolCalls || []);
|
|
279
|
+
const toolResults = await this.handleToolCalls(message.toolCalls || [], options);
|
|
263
280
|
// Add tool results to history (normalized)
|
|
264
281
|
for (const result of toolResults) {
|
|
265
282
|
const resultEntry = transformers_1.mistralTransformer.toolResultEntry(result.toolCallId, result.name, result.content);
|
|
266
283
|
this.addToHistory(resultEntry);
|
|
267
284
|
}
|
|
268
|
-
// Rate limiting delay for Mistral
|
|
269
|
-
|
|
285
|
+
// Rate limiting delay for Mistral. Aborting during the wait rejects
|
|
286
|
+
// immediately rather than sitting out the full delay first.
|
|
287
|
+
await (0, promises_1.setTimeout)(this.config.rateLimitDelay || 1500, undefined, {
|
|
288
|
+
signal: options?.signal,
|
|
289
|
+
});
|
|
270
290
|
// Continue conversation
|
|
271
291
|
try {
|
|
272
292
|
const messages = transformers_1.mistralTransformer.toProvider(this.history.getEntries());
|
|
@@ -281,9 +301,9 @@ class MistralAgent extends BaseAgent_1.BaseAgent {
|
|
|
281
301
|
randomSeed: this.config.randomSeed,
|
|
282
302
|
safePrompt: this.config.safePrompt,
|
|
283
303
|
stop: this.config.stopSequences,
|
|
284
|
-
});
|
|
304
|
+
}, { signal: options?.signal });
|
|
285
305
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, newResponse);
|
|
286
|
-
return this.handleResponse(newResponse);
|
|
306
|
+
return this.handleResponse(newResponse, options);
|
|
287
307
|
}
|
|
288
308
|
catch (error) {
|
|
289
309
|
const err = error;
|
|
@@ -316,7 +336,7 @@ class MistralAgent extends BaseAgent_1.BaseAgent {
|
|
|
316
336
|
}
|
|
317
337
|
throw error;
|
|
318
338
|
}
|
|
319
|
-
async handleToolCalls(toolCalls) {
|
|
339
|
+
async handleToolCalls(toolCalls, options) {
|
|
320
340
|
if (!toolCalls.length) {
|
|
321
341
|
throw new AgentError_1.ExecutionError("No tool calls found in response");
|
|
322
342
|
}
|
|
@@ -346,7 +366,7 @@ class MistralAgent extends BaseAgent_1.BaseAgent {
|
|
|
346
366
|
else {
|
|
347
367
|
args = toolCall.function.arguments;
|
|
348
368
|
}
|
|
349
|
-
const result = await tool.execute(this.getId(), this.getName(), args, toolCallId, this.config.model, "mistral");
|
|
369
|
+
const result = await tool.execute(this.getId(), this.getName(), args, toolCallId, this.config.model, "mistral", { signal: options?.signal });
|
|
350
370
|
return {
|
|
351
371
|
name: toolName,
|
|
352
372
|
toolCallId,
|
|
@@ -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 { OllamaModel } from "../model-types";
|
|
4
5
|
type AgentConfig = BaseAgentConfig & {
|
|
@@ -50,9 +51,22 @@ export declare class OllamaAgent extends BaseAgent {
|
|
|
50
51
|
private vizEventId?;
|
|
51
52
|
/** Count of tool calls in current execution */
|
|
52
53
|
private currentToolCallCount;
|
|
53
|
-
/** Cached Ollama client instance */
|
|
54
|
+
/** Cached signal-less Ollama client instance */
|
|
54
55
|
private _client;
|
|
56
|
+
/** Cached constructor from the optional `ollama` peer dependency */
|
|
57
|
+
private _clientClass;
|
|
55
58
|
constructor(config: Omit<AgentConfig, "vendor">, history?: History);
|
|
59
|
+
private loadClientClass;
|
|
60
|
+
/**
|
|
61
|
+
* Get an Ollama client.
|
|
62
|
+
*
|
|
63
|
+
* The `ollama` package takes no per-request options — its own `abort()`
|
|
64
|
+
* cancels every streamed request on the client at once, which is too blunt
|
|
65
|
+
* for a per-run signal. A client does accept a `fetch` implementation
|
|
66
|
+
* though, so a run with a signal gets its own client whose `fetch` attaches
|
|
67
|
+
* that signal to each request. Clients are cheap (they open no connection),
|
|
68
|
+
* and the signal-less one is still cached and shared.
|
|
69
|
+
*/
|
|
56
70
|
private getClient;
|
|
57
71
|
/**
|
|
58
72
|
* List the models currently available on the Ollama server.
|
|
@@ -64,10 +78,10 @@ export declare class OllamaAgent extends BaseAgent {
|
|
|
64
78
|
listModels(): Promise<ModelInfo<OllamaModelInfo>[]>;
|
|
65
79
|
protected getToolDefinitions(): OllamaToolDefinition[];
|
|
66
80
|
protected process(_input: string): Promise<string>;
|
|
67
|
-
execute(input: string | MessageContent[]): Promise<string>;
|
|
81
|
+
execute(input: string | MessageContent[], options?: ExecuteOptions): Promise<string>;
|
|
68
82
|
private buildOptions;
|
|
69
83
|
private callOllama;
|
|
70
|
-
protected handleResponse(response: unknown): Promise<string>;
|
|
84
|
+
protected handleResponse(response: unknown, options?: ExecuteOptions): Promise<string>;
|
|
71
85
|
private handleToolCalls;
|
|
72
86
|
protected parseUsage(input: unknown): TokenUsage;
|
|
73
87
|
}
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.OllamaAgent = void 0;
|
|
37
37
|
const BaseAgent_1 = require("../BaseAgent");
|
|
38
38
|
const AgentEvent_1 = require("../AgentEvent");
|
|
39
|
+
const cancellation_1 = require("../cancellation");
|
|
39
40
|
const AgentError_1 = require("../errors/AgentError");
|
|
40
41
|
const transformers_1 = require("../../history/transformers");
|
|
41
42
|
const VizReporter_1 = require("../../viz/VizReporter");
|
|
@@ -73,8 +74,10 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
73
74
|
super({ ...config, vendor: "ollama" }, history);
|
|
74
75
|
/** Count of tool calls in current execution */
|
|
75
76
|
this.currentToolCallCount = 0;
|
|
76
|
-
/** Cached Ollama client instance */
|
|
77
|
+
/** Cached signal-less Ollama client instance */
|
|
77
78
|
this._client = null;
|
|
79
|
+
/** Cached constructor from the optional `ollama` peer dependency */
|
|
80
|
+
this._clientClass = null;
|
|
78
81
|
const vendorConfig = config.vendorConfig?.ollama || {};
|
|
79
82
|
const host = config.host ?? vendorConfig.host;
|
|
80
83
|
this.config = {
|
|
@@ -91,8 +94,8 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
91
94
|
};
|
|
92
95
|
this.addSystemMessage(this.getSystemMessage());
|
|
93
96
|
}
|
|
94
|
-
async
|
|
95
|
-
if (!this.
|
|
97
|
+
async loadClientClass() {
|
|
98
|
+
if (!this._clientClass) {
|
|
96
99
|
const pkg = "ollama";
|
|
97
100
|
try {
|
|
98
101
|
const mod = (await Promise.resolve(`${pkg}`).then(s => __importStar(require(s))));
|
|
@@ -100,15 +103,39 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
100
103
|
if (!OllamaClass) {
|
|
101
104
|
throw new Error("Could not find Ollama class in ollama package");
|
|
102
105
|
}
|
|
103
|
-
this.
|
|
104
|
-
host: this.config.host,
|
|
105
|
-
headers: this.config.defaultHeaders,
|
|
106
|
-
});
|
|
106
|
+
this._clientClass = OllamaClass;
|
|
107
107
|
}
|
|
108
108
|
catch (err) {
|
|
109
109
|
throw new AgentError_1.ExecutionError(`Failed to load 'ollama' package. Install it with: npm install ollama\n${err instanceof Error ? err.message : String(err)}`);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
+
return this._clientClass;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get an Ollama client.
|
|
116
|
+
*
|
|
117
|
+
* The `ollama` package takes no per-request options — its own `abort()`
|
|
118
|
+
* cancels every streamed request on the client at once, which is too blunt
|
|
119
|
+
* for a per-run signal. A client does accept a `fetch` implementation
|
|
120
|
+
* though, so a run with a signal gets its own client whose `fetch` attaches
|
|
121
|
+
* that signal to each request. Clients are cheap (they open no connection),
|
|
122
|
+
* and the signal-less one is still cached and shared.
|
|
123
|
+
*/
|
|
124
|
+
async getClient(signal) {
|
|
125
|
+
if (!signal && this._client)
|
|
126
|
+
return this._client;
|
|
127
|
+
const OllamaClass = await this.loadClientClass();
|
|
128
|
+
if (signal) {
|
|
129
|
+
return new OllamaClass({
|
|
130
|
+
host: this.config.host,
|
|
131
|
+
headers: this.config.defaultHeaders,
|
|
132
|
+
fetch: fetchWithSignal(signal),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
this._client ?? (this._client = new OllamaClass({
|
|
136
|
+
host: this.config.host,
|
|
137
|
+
headers: this.config.defaultHeaders,
|
|
138
|
+
}));
|
|
112
139
|
return this._client;
|
|
113
140
|
}
|
|
114
141
|
/**
|
|
@@ -146,7 +173,7 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
146
173
|
async process(_input) {
|
|
147
174
|
return "";
|
|
148
175
|
}
|
|
149
|
-
async execute(input) {
|
|
176
|
+
async execute(input, options) {
|
|
150
177
|
this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
|
|
151
178
|
this.resetTokenUsage();
|
|
152
179
|
this.currentToolCallCount = 0;
|
|
@@ -167,12 +194,19 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
167
194
|
this.history.setSessionAnchor();
|
|
168
195
|
this.history.beginExecution();
|
|
169
196
|
try {
|
|
170
|
-
await this.
|
|
171
|
-
const response = await this.callOllama();
|
|
197
|
+
const response = await this.callOllama(options);
|
|
172
198
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, response);
|
|
173
|
-
return await this.handleResponse(response);
|
|
199
|
+
return await this.handleResponse(response, options);
|
|
174
200
|
}
|
|
175
201
|
catch (error) {
|
|
202
|
+
if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
|
|
203
|
+
const abortError = this.abortError(error, options?.signal);
|
|
204
|
+
if (this.vizEventId) {
|
|
205
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "AbortError", abortError.message, false);
|
|
206
|
+
this.vizEventId = undefined;
|
|
207
|
+
}
|
|
208
|
+
throw abortError;
|
|
209
|
+
}
|
|
176
210
|
if (error instanceof AgentError_1.ExecutionError || error instanceof AgentError_1.ApiError) {
|
|
177
211
|
this.emit(AgentEvent_1.AgentEvent.ERROR, error);
|
|
178
212
|
if (this.vizEventId) {
|
|
@@ -211,8 +245,8 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
211
245
|
opts.think = this.config.think;
|
|
212
246
|
return opts;
|
|
213
247
|
}
|
|
214
|
-
async callOllama() {
|
|
215
|
-
const client = await this.getClient();
|
|
248
|
+
async callOllama(executeOptions) {
|
|
249
|
+
const client = await this.getClient(executeOptions?.signal);
|
|
216
250
|
const messages = transformers_1.ollamaTransformer.toProvider(this.history.getEntries());
|
|
217
251
|
const tools = this.tools.size > 0 ? this.getToolDefinitions() : undefined;
|
|
218
252
|
const options = this.buildOptions();
|
|
@@ -226,7 +260,7 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
226
260
|
options: Object.keys(options).length > 0 ? options : undefined,
|
|
227
261
|
});
|
|
228
262
|
}
|
|
229
|
-
async handleResponse(response) {
|
|
263
|
+
async handleResponse(response, options) {
|
|
230
264
|
const ollamaResponse = response;
|
|
231
265
|
const usage = this.accumulateUsage(this.parseUsage(ollamaResponse));
|
|
232
266
|
if (ollamaResponse.done_reason === "length") {
|
|
@@ -258,22 +292,27 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
258
292
|
}
|
|
259
293
|
// Tool calls detected
|
|
260
294
|
const toolCalls = message.tool_calls;
|
|
295
|
+
// Stop before the assistant turn is written: nothing else would notice a
|
|
296
|
+
// cancellation until the next provider call, and bailing out here avoids
|
|
297
|
+
// both running the tools' side effects and leaving a tool call in history
|
|
298
|
+
// with no tool message to answer it.
|
|
299
|
+
(0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
|
|
261
300
|
this.emit(AgentEvent_1.AgentEvent.TOOL_USE, toolCalls);
|
|
262
301
|
this.currentToolCallCount += toolCalls.length;
|
|
263
302
|
// Generate IDs — Ollama doesn't provide tool call IDs
|
|
264
303
|
const generatedIds = toolCalls.map((_, i) => `ollama_${Date.now()}_${i}`);
|
|
265
304
|
const assistantEntry = transformers_1.ollamaTransformer.fromProviderMessage(message, generatedIds);
|
|
266
305
|
this.addToHistory(assistantEntry);
|
|
267
|
-
const toolResults = await this.handleToolCalls(toolCalls, generatedIds);
|
|
306
|
+
const toolResults = await this.handleToolCalls(toolCalls, generatedIds, options);
|
|
268
307
|
for (const result of toolResults) {
|
|
269
308
|
const resultEntry = transformers_1.ollamaTransformer.toolResultEntry(result.toolCallId, result.content);
|
|
270
309
|
this.addToHistory(resultEntry);
|
|
271
310
|
}
|
|
272
311
|
// Continue conversation with tool results
|
|
273
312
|
try {
|
|
274
|
-
const newResponse = await this.callOllama();
|
|
313
|
+
const newResponse = await this.callOllama(options);
|
|
275
314
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, newResponse);
|
|
276
|
-
return this.handleResponse(newResponse);
|
|
315
|
+
return this.handleResponse(newResponse, options);
|
|
277
316
|
}
|
|
278
317
|
catch (error) {
|
|
279
318
|
const executionError = new AgentError_1.ExecutionError(`Ollama error during tool response: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -281,7 +320,7 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
281
320
|
throw executionError;
|
|
282
321
|
}
|
|
283
322
|
}
|
|
284
|
-
async handleToolCalls(toolCalls, generatedIds) {
|
|
323
|
+
async handleToolCalls(toolCalls, generatedIds, options) {
|
|
285
324
|
return Promise.all(toolCalls.map(async (toolCall, idx) => {
|
|
286
325
|
const toolName = toolCall.function.name;
|
|
287
326
|
const tool = this.tools.get(toolName);
|
|
@@ -296,7 +335,7 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
296
335
|
const args = typeof toolCall.function.arguments === "string"
|
|
297
336
|
? JSON.parse(toolCall.function.arguments)
|
|
298
337
|
: toolCall.function.arguments;
|
|
299
|
-
const result = await tool.execute(this.getId(), this.getName(), args, toolCallId, this.config.model, "ollama");
|
|
338
|
+
const result = await tool.execute(this.getId(), this.getName(), args, toolCallId, this.config.model, "ollama", { signal: options?.signal });
|
|
300
339
|
return { toolCallId, content: JSON.stringify(result) };
|
|
301
340
|
}
|
|
302
341
|
catch (error) {
|
|
@@ -329,4 +368,15 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
329
368
|
}
|
|
330
369
|
}
|
|
331
370
|
exports.OllamaAgent = OllamaAgent;
|
|
371
|
+
/**
|
|
372
|
+
* Wrap the global `fetch` so every request carries `signal`, keeping any
|
|
373
|
+
* signal the caller already set — the `ollama` package attaches its own to
|
|
374
|
+
* streamed requests so that its `abort()` keeps working.
|
|
375
|
+
*/
|
|
376
|
+
function fetchWithSignal(signal) {
|
|
377
|
+
return (input, init) => fetch(input, {
|
|
378
|
+
...init,
|
|
379
|
+
signal: (0, cancellation_1.combineSignals)(init?.signal ?? undefined, signal),
|
|
380
|
+
});
|
|
381
|
+
}
|
|
332
382
|
//# sourceMappingURL=OllamaAgent.js.map
|