@agentionai/agents 1.4.0 → 1.6.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 +1 -1
- package/dist/agents/BaseAgent.d.ts +83 -0
- package/dist/agents/BaseAgent.js +16 -0
- package/dist/agents/anthropic/ClaudeAgent.d.ts +62 -2
- package/dist/agents/anthropic/ClaudeAgent.js +37 -0
- package/dist/agents/google/GeminiAgent.d.ts +76 -1
- package/dist/agents/google/GeminiAgent.js +93 -1
- package/dist/agents/llamacpp/LlamaCppAgent.d.ts +67 -0
- package/dist/agents/llamacpp/LlamaCppAgent.js +29 -0
- package/dist/agents/mistral/MistralAgent.d.ts +22 -2
- package/dist/agents/mistral/MistralAgent.js +36 -0
- package/dist/agents/ollama/OllamaAgent.d.ts +6 -2
- package/dist/agents/ollama/OllamaAgent.js +10 -1
- package/dist/agents/openai/OpenAiAgent.d.ts +10 -1
- package/dist/agents/openai/OpenAiAgent.js +32 -7
- package/dist/agents/openai/openai-strict.d.ts +27 -0
- package/dist/agents/openai/openai-strict.js +50 -0
- package/dist/agents/openai-compatible/OpenAICompatibleAgent.d.ts +5 -2
- package/dist/agents/openai-compatible/OpenAICompatibleAgent.js +9 -1
- package/dist/gemini.d.ts +2 -1
- package/dist/gemini.js +2 -1
- package/dist/history/transformers.js +27 -4
- package/dist/history/types.d.ts +10 -1
- package/dist/history/types.js +10 -2
- package/dist/index.d.ts +5 -1
- package/dist/index.js +2 -1
- package/dist/llamacpp.d.ts +1 -0
- package/dist/mistral.d.ts +1 -0
- package/dist/ollama.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ import { ClaudeAgent, OpenAiAgent } from '@agentionai/agents';
|
|
|
75
75
|
|
|
76
76
|
## Features
|
|
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.
|
|
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. `listModels()` asks any provider what it currently offers, in one shared shape.
|
|
79
79
|
- **Composable Context Management** - Tool result masking (lossless, free) + rolling summarization (auto-firing) + sub-agent delegation (token isolation by architecture).
|
|
80
80
|
- **Streaming** - `executeStream()` on Claude, OpenAI, and all OpenAI-compatible agents. Yields `{ type: "text" | "reasoning" }` chunks; tool calls handled transparently.
|
|
81
81
|
- **Built-In Tools** - Use provider-defined server-side tools (e.g. Anthropic's web search, bash, text editor) alongside your own.
|
|
@@ -67,6 +67,76 @@ export type TokenUsage = {
|
|
|
67
67
|
*/
|
|
68
68
|
outputTokensPerSecond?: number;
|
|
69
69
|
};
|
|
70
|
+
/**
|
|
71
|
+
* What a model can do, as reported by its provider.
|
|
72
|
+
*
|
|
73
|
+
* Every flag is three-valued: `true` and `false` are the provider's answer,
|
|
74
|
+
* `undefined` means it does not report on that capability at all — which is the
|
|
75
|
+
* common case, since no provider covers all four. Filter with `!== false` when
|
|
76
|
+
* you want "not known to be unsupported", and with `=== true` when you need
|
|
77
|
+
* positive confirmation.
|
|
78
|
+
*/
|
|
79
|
+
export type ModelCapabilities = {
|
|
80
|
+
/** Conversational generation — what an agent needs to run at all. */
|
|
81
|
+
chat?: boolean;
|
|
82
|
+
/** Function / tool calling. */
|
|
83
|
+
tools?: boolean;
|
|
84
|
+
/** Image input. */
|
|
85
|
+
vision?: boolean;
|
|
86
|
+
/** Extended thinking / reasoning. */
|
|
87
|
+
thinking?: boolean;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* A model as reported by a provider's models endpoint, in a shape that is the
|
|
91
|
+
* same on every provider.
|
|
92
|
+
*
|
|
93
|
+
* Only `id` — the value you pass as `model` in an agent config — is guaranteed.
|
|
94
|
+
* Every other field is optional because no two providers report the same set:
|
|
95
|
+
* Anthropic gives a display name and release date but no context window,
|
|
96
|
+
* OpenAI gives an owner and a creation timestamp, Mistral and Gemini give
|
|
97
|
+
* context limits. The provider's own untouched entry is always available on
|
|
98
|
+
* `raw` for anything not covered here.
|
|
99
|
+
*/
|
|
100
|
+
export type ModelInfo<TRaw = unknown> = {
|
|
101
|
+
/** Model identifier, as passed to the API in the `model` field. */
|
|
102
|
+
id: string;
|
|
103
|
+
/** Human-readable name, where the provider reports one. */
|
|
104
|
+
displayName?: string;
|
|
105
|
+
/** Release or creation date, where the provider reports one. */
|
|
106
|
+
created?: Date;
|
|
107
|
+
/** Owning organisation, where the provider reports one. */
|
|
108
|
+
ownedBy?: string;
|
|
109
|
+
/** Maximum input context in tokens, where the provider reports one. */
|
|
110
|
+
contextLength?: number;
|
|
111
|
+
/** Maximum tokens in a single response, where the provider reports one. */
|
|
112
|
+
maxOutputTokens?: number;
|
|
113
|
+
/**
|
|
114
|
+
* What the provider says this model supports. Absent flags mean "not
|
|
115
|
+
* reported", never "unsupported" — see {@link ModelCapabilities}.
|
|
116
|
+
*/
|
|
117
|
+
capabilities?: ModelCapabilities;
|
|
118
|
+
/**
|
|
119
|
+
* When the provider plans to retire the model, where it publishes a date.
|
|
120
|
+
* Only Mistral does today; note that a model can stop working before any
|
|
121
|
+
* announced date, and Google in particular retires models that its listing
|
|
122
|
+
* still advertises.
|
|
123
|
+
*/
|
|
124
|
+
deprecatedAt?: Date;
|
|
125
|
+
/** Model the provider recommends in its place, where it names one. */
|
|
126
|
+
replacedBy?: string;
|
|
127
|
+
/**
|
|
128
|
+
* Whether the model is currently held in memory, on servers that distinguish
|
|
129
|
+
* "offered" from "loaded" — llama.cpp's model router being the case in point,
|
|
130
|
+
* where an unloaded model is listed but has to be loaded before it answers.
|
|
131
|
+
*
|
|
132
|
+
* Undefined wherever the distinction does not exist or is not reported: every
|
|
133
|
+
* hosted provider, and a single-model `llama-server`, where the one model
|
|
134
|
+
* listed is by definition the loaded one.
|
|
135
|
+
*/
|
|
136
|
+
loaded?: boolean;
|
|
137
|
+
/** The provider's unmodified entry for this model. */
|
|
138
|
+
raw: TRaw;
|
|
139
|
+
};
|
|
70
140
|
/**
|
|
71
141
|
* The base agent is what the other agents are inheriting from
|
|
72
142
|
* Handles the BaseConfig
|
|
@@ -107,6 +177,19 @@ export declare abstract class BaseAgent<TInput = unknown, TOutput = unknown> ext
|
|
|
107
177
|
protected abstract process(input: TInput): Promise<TOutput>;
|
|
108
178
|
protected abstract handleResponse(response: unknown): Promise<unknown>;
|
|
109
179
|
protected getToolDefinitions(): unknown[];
|
|
180
|
+
/**
|
|
181
|
+
* List the models the provider currently offers, straight from its models
|
|
182
|
+
* endpoint — the live answer, as opposed to the hand-maintained unions in
|
|
183
|
+
* `model-types.ts`.
|
|
184
|
+
*
|
|
185
|
+
* Overridden by every built-in agent; the base implementation throws so that
|
|
186
|
+
* a custom agent without a models endpoint fails with a clear message rather
|
|
187
|
+
* than silently returning nothing.
|
|
188
|
+
*
|
|
189
|
+
* @throws {ExecutionError} If the provider does not support listing, or the
|
|
190
|
+
* request fails.
|
|
191
|
+
*/
|
|
192
|
+
listModels(): Promise<ModelInfo[]>;
|
|
110
193
|
/**
|
|
111
194
|
* Add an entry to history
|
|
112
195
|
*/
|
package/dist/agents/BaseAgent.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.BaseAgent = void 0;
|
|
|
7
7
|
const events_1 = __importDefault(require("events"));
|
|
8
8
|
const Tool_1 = require("../tools/Tool");
|
|
9
9
|
const History_1 = require("../history/History");
|
|
10
|
+
const AgentError_1 = require("./errors/AgentError");
|
|
10
11
|
/**
|
|
11
12
|
* The base agent is what the other agents are inheriting from
|
|
12
13
|
* Handles the BaseConfig
|
|
@@ -49,6 +50,21 @@ class BaseAgent extends events_1.default {
|
|
|
49
50
|
getToolDefinitions() {
|
|
50
51
|
return Array.from(this.tools.values()).map((tool) => tool.getPrompt());
|
|
51
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* List the models the provider currently offers, straight from its models
|
|
55
|
+
* endpoint — the live answer, as opposed to the hand-maintained unions in
|
|
56
|
+
* `model-types.ts`.
|
|
57
|
+
*
|
|
58
|
+
* Overridden by every built-in agent; the base implementation throws so that
|
|
59
|
+
* a custom agent without a models endpoint fails with a clear message rather
|
|
60
|
+
* than silently returning nothing.
|
|
61
|
+
*
|
|
62
|
+
* @throws {ExecutionError} If the provider does not support listing, or the
|
|
63
|
+
* request fails.
|
|
64
|
+
*/
|
|
65
|
+
async listModels() {
|
|
66
|
+
throw new AgentError_1.ExecutionError(`listModels() is not implemented for the '${this.vendor}' agent`);
|
|
67
|
+
}
|
|
52
68
|
/**
|
|
53
69
|
* Add an entry to history
|
|
54
70
|
*/
|
|
@@ -1,11 +1,57 @@
|
|
|
1
1
|
import { Anthropic } from "@anthropic-ai/sdk";
|
|
2
|
-
import { Message, ToolUnion, Usage } from "@anthropic-ai/sdk/resources";
|
|
2
|
+
import { Message, type ModelInfo as AnthropicModelInfo, ToolUnion, Usage } from "@anthropic-ai/sdk/resources";
|
|
3
3
|
import { type ToolDefinition } from "../../tools/Tool";
|
|
4
4
|
import { type BuiltInTool } from "../../tools/BuiltInTool";
|
|
5
|
-
import { BaseAgent, BaseAgentConfig, TokenUsage } from "../BaseAgent";
|
|
5
|
+
import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
|
|
6
6
|
import { History, MessageContent } from "../../history/History";
|
|
7
7
|
import { StreamChunk } from "../openai-compatible/OpenAICompatibleAgent";
|
|
8
8
|
import { ClaudeModel } from "../model-types";
|
|
9
|
+
/** A capability node in Anthropic's model card — always at least `supported`. */
|
|
10
|
+
type AnthropicSupported = {
|
|
11
|
+
supported: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* One entry from Anthropic's `/v1/models`.
|
|
15
|
+
*
|
|
16
|
+
* Declared here rather than taken from the SDK, whose `ModelInfo` still covers
|
|
17
|
+
* only `id`/`type`/`display_name`/`created_at`. The API also returns token
|
|
18
|
+
* limits and a capability tree — verified on the wire on 2026-08-11 — and those
|
|
19
|
+
* are what `contextLength`, `maxOutputTokens` and `capabilities` are read from.
|
|
20
|
+
* Everything past the four SDK fields is optional so that an older API version,
|
|
21
|
+
* or a gateway that trims the response, still typechecks.
|
|
22
|
+
*/
|
|
23
|
+
export type AnthropicModelCard = AnthropicModelInfo & {
|
|
24
|
+
/** Context window in tokens. */
|
|
25
|
+
max_input_tokens?: number;
|
|
26
|
+
/** Largest `max_tokens` the model accepts for a response. */
|
|
27
|
+
max_tokens?: number;
|
|
28
|
+
capabilities?: {
|
|
29
|
+
batch?: AnthropicSupported;
|
|
30
|
+
citations?: AnthropicSupported;
|
|
31
|
+
code_execution?: AnthropicSupported;
|
|
32
|
+
/** Server-side context editing; the dated keys are individual strategies. */
|
|
33
|
+
context_management?: AnthropicSupported & {
|
|
34
|
+
[strategy: string]: AnthropicSupported | boolean | undefined;
|
|
35
|
+
};
|
|
36
|
+
/** Which effort levels the model accepts — the live answer to what `model-types.ts` hardcodes. */
|
|
37
|
+
effort?: AnthropicSupported & {
|
|
38
|
+
low?: AnthropicSupported;
|
|
39
|
+
medium?: AnthropicSupported;
|
|
40
|
+
high?: AnthropicSupported;
|
|
41
|
+
xhigh?: AnthropicSupported;
|
|
42
|
+
max?: AnthropicSupported;
|
|
43
|
+
};
|
|
44
|
+
image_input?: AnthropicSupported;
|
|
45
|
+
pdf_input?: AnthropicSupported;
|
|
46
|
+
structured_outputs?: AnthropicSupported;
|
|
47
|
+
thinking?: AnthropicSupported & {
|
|
48
|
+
types?: {
|
|
49
|
+
enabled?: AnthropicSupported;
|
|
50
|
+
adaptive?: AnthropicSupported;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
};
|
|
9
55
|
type AgentConfig = BaseAgentConfig & {
|
|
10
56
|
apiKey: string;
|
|
11
57
|
model?: ClaudeModel;
|
|
@@ -55,6 +101,20 @@ export declare class ClaudeAgent extends BaseAgent {
|
|
|
55
101
|
private currentToolCallCount;
|
|
56
102
|
constructor(config: Omit<AgentConfig, "vendor">, history?: History);
|
|
57
103
|
protected getToolDefinitions(): ToolDefinition[];
|
|
104
|
+
/**
|
|
105
|
+
* List the models available to this API key, newest first.
|
|
106
|
+
*
|
|
107
|
+
* Anthropic reports token limits and a capability tree that the SDK's own
|
|
108
|
+
* type omits, so `raw` is typed as {@link AnthropicModelCard} — which is also
|
|
109
|
+
* where `contextLength` (`max_input_tokens`), `maxOutputTokens` and the
|
|
110
|
+
* vision/thinking flags come from. `capabilities.effort` on `raw` states which
|
|
111
|
+
* effort levels each model accepts. Tool support is not reported; every model
|
|
112
|
+
* the endpoint lists supports tools, so `capabilities.tools` stays undefined
|
|
113
|
+
* rather than being asserted.
|
|
114
|
+
*
|
|
115
|
+
* The result is fully paginated — the endpoint pages at 1000 models.
|
|
116
|
+
*/
|
|
117
|
+
listModels(): Promise<ModelInfo<AnthropicModelCard>[]>;
|
|
58
118
|
/**
|
|
59
119
|
* Combine locally-executed tool definitions with provider-defined
|
|
60
120
|
* (server-side) built-in tools, in the shape Anthropic's API expects.
|
|
@@ -62,6 +62,43 @@ class ClaudeAgent extends BaseAgent_1.BaseAgent {
|
|
|
62
62
|
getToolDefinitions() {
|
|
63
63
|
return Array.from(this.tools.values()).map((tool) => tool.getPrompt());
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* List the models available to this API key, newest first.
|
|
67
|
+
*
|
|
68
|
+
* Anthropic reports token limits and a capability tree that the SDK's own
|
|
69
|
+
* type omits, so `raw` is typed as {@link AnthropicModelCard} — which is also
|
|
70
|
+
* where `contextLength` (`max_input_tokens`), `maxOutputTokens` and the
|
|
71
|
+
* vision/thinking flags come from. `capabilities.effort` on `raw` states which
|
|
72
|
+
* effort levels each model accepts. Tool support is not reported; every model
|
|
73
|
+
* the endpoint lists supports tools, so `capabilities.tools` stays undefined
|
|
74
|
+
* rather than being asserted.
|
|
75
|
+
*
|
|
76
|
+
* The result is fully paginated — the endpoint pages at 1000 models.
|
|
77
|
+
*/
|
|
78
|
+
async listModels() {
|
|
79
|
+
try {
|
|
80
|
+
const models = [];
|
|
81
|
+
for await (const model of this.client.models.list({ limit: 1000 })) {
|
|
82
|
+
const card = model;
|
|
83
|
+
models.push({
|
|
84
|
+
id: card.id,
|
|
85
|
+
displayName: card.display_name,
|
|
86
|
+
created: new Date(card.created_at),
|
|
87
|
+
contextLength: card.max_input_tokens,
|
|
88
|
+
maxOutputTokens: card.max_tokens,
|
|
89
|
+
capabilities: {
|
|
90
|
+
vision: card.capabilities?.image_input?.supported,
|
|
91
|
+
thinking: card.capabilities?.thinking?.supported,
|
|
92
|
+
},
|
|
93
|
+
raw: card,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return models;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw new AgentError_1.ExecutionError(`Failed to list Anthropic models: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
65
102
|
/**
|
|
66
103
|
* Combine locally-executed tool definitions with provider-defined
|
|
67
104
|
* (server-side) built-in tools, in the shape Anthropic's API expects.
|
|
@@ -1,7 +1,65 @@
|
|
|
1
1
|
import { FunctionDeclarationsTool, GenerateContentResult, Schema } from "@google/generative-ai";
|
|
2
|
-
import { BaseAgent, BaseAgentConfig, TokenUsage } from "../BaseAgent";
|
|
2
|
+
import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
|
|
3
3
|
import { History, MessageContent } from "../../history/History";
|
|
4
4
|
import { GeminiModel } from "../model-types";
|
|
5
|
+
/**
|
|
6
|
+
* Models that `models.list` still advertises but the API no longer serves.
|
|
7
|
+
*
|
|
8
|
+
* Google leaves retired models in the listing, fully described and claiming
|
|
9
|
+
* `generateContent`; calling one fails with `404 — "This model is no longer
|
|
10
|
+
* available to new users"`. Nothing in the listing distinguishes them, and the
|
|
11
|
+
* stable `v1` endpoint carries them too, so the only way to keep them out of
|
|
12
|
+
* `listModels()` is to name them.
|
|
13
|
+
*
|
|
14
|
+
* A retirement is permanent, so this list only ever grows — an entry never
|
|
15
|
+
* needs revisiting, and one that disappears from the API's listing costs
|
|
16
|
+
* nothing to keep.
|
|
17
|
+
*
|
|
18
|
+
* Every entry is confirmed by probing `countTokens` (free, and it 404s the same
|
|
19
|
+
* way), most recently on 2026-08-11. Note that retirement is per-model, not per
|
|
20
|
+
* family: `gemini-2.5-flash-image`, `gemini-2.5-*-preview-tts` and
|
|
21
|
+
* `gemini-2.5-computer-use-preview-10-2025` were all still live at that date,
|
|
22
|
+
* which is why these are listed individually rather than matched by prefix.
|
|
23
|
+
*
|
|
24
|
+
* Pass `{ includeRetired: true }` to `listModels()` to see them anyway.
|
|
25
|
+
*/
|
|
26
|
+
export declare const GEMINI_RETIRED_MODELS: readonly string[];
|
|
27
|
+
/** Options for {@link GeminiAgent.listModels}. */
|
|
28
|
+
export type GeminiListModelsOptions = {
|
|
29
|
+
/**
|
|
30
|
+
* Include models known to have been retired. Off by default: they are listed
|
|
31
|
+
* by the API but fail at call time.
|
|
32
|
+
*/
|
|
33
|
+
includeRetired?: boolean;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* One entry from the Generative Language API's `models.list` response.
|
|
37
|
+
*
|
|
38
|
+
* Declared here rather than imported: `@google/generative-ai` only covers
|
|
39
|
+
* content generation and ships no type for the models endpoint.
|
|
40
|
+
*/
|
|
41
|
+
export type GeminiModelCard = {
|
|
42
|
+
/** Resource name, e.g. `"models/gemini-flash-latest"`. */
|
|
43
|
+
name: string;
|
|
44
|
+
baseModelId?: string;
|
|
45
|
+
version?: string;
|
|
46
|
+
displayName?: string;
|
|
47
|
+
description?: string;
|
|
48
|
+
inputTokenLimit?: number;
|
|
49
|
+
outputTokenLimit?: number;
|
|
50
|
+
/**
|
|
51
|
+
* e.g. `["generateContent", "countTokens"]`. Embedding models have
|
|
52
|
+
* `embedContent`, Imagen `predict`, Veo `predictLongRunning`, and the live
|
|
53
|
+
* models only `bidiGenerateContent` — none of which an agent can drive.
|
|
54
|
+
*/
|
|
55
|
+
supportedGenerationMethods?: string[];
|
|
56
|
+
/** Whether the model reasons before answering. */
|
|
57
|
+
thinking?: boolean;
|
|
58
|
+
temperature?: number;
|
|
59
|
+
maxTemperature?: number;
|
|
60
|
+
topP?: number;
|
|
61
|
+
topK?: number;
|
|
62
|
+
};
|
|
5
63
|
type AgentConfig = BaseAgentConfig & {
|
|
6
64
|
apiKey: string;
|
|
7
65
|
model?: GeminiModel;
|
|
@@ -34,6 +92,23 @@ export declare class GeminiAgent extends BaseAgent {
|
|
|
34
92
|
/** Count of tool calls in current execution */
|
|
35
93
|
private currentToolCallCount;
|
|
36
94
|
constructor(config: Omit<AgentConfig, "vendor">, history?: History);
|
|
95
|
+
/**
|
|
96
|
+
* List the models available to this API key.
|
|
97
|
+
*
|
|
98
|
+
* Issued as a direct request to `/v1beta/models`: `@google/generative-ai`
|
|
99
|
+
* exposes no models endpoint, so there is no client method to call. Every
|
|
100
|
+
* page is followed, and the `"models/"` prefix is stripped from `id` so the
|
|
101
|
+
* value can be passed straight back as an agent's `model`.
|
|
102
|
+
*
|
|
103
|
+
* The list covers everything the key can reach, including embedding, image
|
|
104
|
+
* and live-audio models — `capabilities.chat` marks the ones an agent can
|
|
105
|
+
* actually drive.
|
|
106
|
+
*
|
|
107
|
+
* Models known to have been retired are left out, since the API lists them
|
|
108
|
+
* but no longer serves them — see {@link GEMINI_RETIRED_MODELS}. Pass
|
|
109
|
+
* `{ includeRetired: true }` for the listing exactly as Google returns it.
|
|
110
|
+
*/
|
|
111
|
+
listModels(options?: GeminiListModelsOptions): Promise<ModelInfo<GeminiModelCard>[]>;
|
|
37
112
|
protected getToolDefinitionsForGemini(): FunctionDeclarationsTool | undefined;
|
|
38
113
|
/**
|
|
39
114
|
* Convert JSON Schema to Gemini's FunctionDeclarationSchema format
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GeminiAgent = void 0;
|
|
3
|
+
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");
|
|
@@ -8,6 +8,36 @@ const AgentError_1 = require("../errors/AgentError");
|
|
|
8
8
|
const transformers_1 = require("../../history/transformers");
|
|
9
9
|
const VizReporter_1 = require("../../viz/VizReporter");
|
|
10
10
|
const VizConfig_1 = require("../../viz/VizConfig");
|
|
11
|
+
/** Base URL of the Generative Language API, matching the SDK's own default. */
|
|
12
|
+
const GEMINI_API_BASE = "https://generativelanguage.googleapis.com";
|
|
13
|
+
/**
|
|
14
|
+
* Models that `models.list` still advertises but the API no longer serves.
|
|
15
|
+
*
|
|
16
|
+
* Google leaves retired models in the listing, fully described and claiming
|
|
17
|
+
* `generateContent`; calling one fails with `404 — "This model is no longer
|
|
18
|
+
* available to new users"`. Nothing in the listing distinguishes them, and the
|
|
19
|
+
* stable `v1` endpoint carries them too, so the only way to keep them out of
|
|
20
|
+
* `listModels()` is to name them.
|
|
21
|
+
*
|
|
22
|
+
* A retirement is permanent, so this list only ever grows — an entry never
|
|
23
|
+
* needs revisiting, and one that disappears from the API's listing costs
|
|
24
|
+
* nothing to keep.
|
|
25
|
+
*
|
|
26
|
+
* Every entry is confirmed by probing `countTokens` (free, and it 404s the same
|
|
27
|
+
* way), most recently on 2026-08-11. Note that retirement is per-model, not per
|
|
28
|
+
* family: `gemini-2.5-flash-image`, `gemini-2.5-*-preview-tts` and
|
|
29
|
+
* `gemini-2.5-computer-use-preview-10-2025` were all still live at that date,
|
|
30
|
+
* which is why these are listed individually rather than matched by prefix.
|
|
31
|
+
*
|
|
32
|
+
* Pass `{ includeRetired: true }` to `listModels()` to see them anyway.
|
|
33
|
+
*/
|
|
34
|
+
exports.GEMINI_RETIRED_MODELS = [
|
|
35
|
+
// Retired for new users some time before 2026-08-11
|
|
36
|
+
"gemini-2.5-flash",
|
|
37
|
+
"gemini-2.5-pro",
|
|
38
|
+
"gemini-2.5-flash-lite",
|
|
39
|
+
];
|
|
40
|
+
const GEMINI_RETIRED = new Set(exports.GEMINI_RETIRED_MODELS);
|
|
11
41
|
/**
|
|
12
42
|
* Agent for Google Gemini models.
|
|
13
43
|
*
|
|
@@ -45,12 +75,74 @@ class GeminiAgent extends BaseAgent_1.BaseAgent {
|
|
|
45
75
|
candidateCount,
|
|
46
76
|
responseMimeType,
|
|
47
77
|
responseSchema,
|
|
78
|
+
defaultHeaders: config.defaultHeaders,
|
|
48
79
|
};
|
|
49
80
|
// Initialize the model
|
|
50
81
|
this.generativeModel = this.client.getGenerativeModel({ model: this.config.model }, config.defaultHeaders ? { customHeaders: config.defaultHeaders } : undefined);
|
|
51
82
|
// Add system message to history (skips if already exists with same content)
|
|
52
83
|
this.addSystemMessage(this.getSystemMessage());
|
|
53
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* List the models available to this API key.
|
|
87
|
+
*
|
|
88
|
+
* Issued as a direct request to `/v1beta/models`: `@google/generative-ai`
|
|
89
|
+
* exposes no models endpoint, so there is no client method to call. Every
|
|
90
|
+
* page is followed, and the `"models/"` prefix is stripped from `id` so the
|
|
91
|
+
* value can be passed straight back as an agent's `model`.
|
|
92
|
+
*
|
|
93
|
+
* The list covers everything the key can reach, including embedding, image
|
|
94
|
+
* and live-audio models — `capabilities.chat` marks the ones an agent can
|
|
95
|
+
* actually drive.
|
|
96
|
+
*
|
|
97
|
+
* Models known to have been retired are left out, since the API lists them
|
|
98
|
+
* but no longer serves them — see {@link GEMINI_RETIRED_MODELS}. Pass
|
|
99
|
+
* `{ includeRetired: true }` for the listing exactly as Google returns it.
|
|
100
|
+
*/
|
|
101
|
+
async listModels(options) {
|
|
102
|
+
try {
|
|
103
|
+
const models = [];
|
|
104
|
+
let pageToken;
|
|
105
|
+
do {
|
|
106
|
+
const url = new URL(`${GEMINI_API_BASE}/v1beta/models`);
|
|
107
|
+
url.searchParams.set("pageSize", "1000");
|
|
108
|
+
if (pageToken) {
|
|
109
|
+
url.searchParams.set("pageToken", pageToken);
|
|
110
|
+
}
|
|
111
|
+
const response = await fetch(url, {
|
|
112
|
+
headers: {
|
|
113
|
+
"x-goog-api-key": this.config.apiKey,
|
|
114
|
+
...this.config.defaultHeaders,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
throw new Error(`${response.status} ${response.statusText}: ${await response.text()}`);
|
|
119
|
+
}
|
|
120
|
+
const body = (await response.json());
|
|
121
|
+
for (const model of body.models ?? []) {
|
|
122
|
+
const id = model.name.replace(/^models\//, "");
|
|
123
|
+
if (!options?.includeRetired && GEMINI_RETIRED.has(id)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
models.push({
|
|
127
|
+
id,
|
|
128
|
+
displayName: model.displayName,
|
|
129
|
+
contextLength: model.inputTokenLimit,
|
|
130
|
+
maxOutputTokens: model.outputTokenLimit,
|
|
131
|
+
capabilities: {
|
|
132
|
+
chat: model.supportedGenerationMethods?.includes("generateContent"),
|
|
133
|
+
thinking: model.thinking,
|
|
134
|
+
},
|
|
135
|
+
raw: model,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
pageToken = body.nextPageToken;
|
|
139
|
+
} while (pageToken);
|
|
140
|
+
return models;
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
throw new AgentError_1.ExecutionError(`Failed to list Gemini models: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
54
146
|
getToolDefinitionsForGemini() {
|
|
55
147
|
const tools = Array.from(this.tools.values());
|
|
56
148
|
if (tools.length === 0) {
|
|
@@ -1,6 +1,56 @@
|
|
|
1
|
+
import { Model } from "openai/resources/models";
|
|
1
2
|
import { History } from "../../history/History";
|
|
3
|
+
import { ModelInfo } from "../BaseAgent";
|
|
2
4
|
import { OpenAICompatibleAgent, OpenAICompatibleConfig } from "../openai-compatible/OpenAICompatibleAgent";
|
|
3
5
|
import { LlamaCppModel } from "../model-types";
|
|
6
|
+
/**
|
|
7
|
+
* The GGUF details `llama-server` reports for a model it has loaded. Absent
|
|
8
|
+
* from the listing for a model the router knows about but has not loaded.
|
|
9
|
+
*/
|
|
10
|
+
export type LlamaCppModelMeta = {
|
|
11
|
+
vocab_type?: number;
|
|
12
|
+
n_vocab?: number;
|
|
13
|
+
/** Context the model was actually loaded with (`--ctx-size`). */
|
|
14
|
+
n_ctx?: number;
|
|
15
|
+
/** Context the model was trained with — its ceiling, not its current size. */
|
|
16
|
+
n_ctx_train?: number;
|
|
17
|
+
n_embd?: number;
|
|
18
|
+
n_params?: number;
|
|
19
|
+
/** On-disk size in bytes. */
|
|
20
|
+
size?: number;
|
|
21
|
+
/** Quantization, e.g. `"Q6_K"`. */
|
|
22
|
+
ftype?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* One entry from a llama.cpp server's `/v1/models`.
|
|
26
|
+
*
|
|
27
|
+
* Everything past the OpenAI-standard fields is optional because it depends on
|
|
28
|
+
* how the server was started: a single-model `llama-server` reports `meta` for
|
|
29
|
+
* the model it is serving and nothing else, while a server in model-router mode
|
|
30
|
+
* lists every model it can serve, each with a `status` saying whether it is
|
|
31
|
+
* currently loaded. Verified against llama.cpp b10148.
|
|
32
|
+
*/
|
|
33
|
+
export type LlamaCppModelCard = Model & {
|
|
34
|
+
/** Alternative ids that resolve to this model. */
|
|
35
|
+
aliases?: string[];
|
|
36
|
+
tags?: string[];
|
|
37
|
+
/** Router mode only: whether the model is in memory, and how it is launched. */
|
|
38
|
+
status?: {
|
|
39
|
+
value: "loaded" | "unloaded" | (string & {});
|
|
40
|
+
/** The `llama-server` argv the router uses to bring this model up. */
|
|
41
|
+
args?: string[];
|
|
42
|
+
/** The preset block backing this model, as INI text. */
|
|
43
|
+
preset?: string;
|
|
44
|
+
};
|
|
45
|
+
architecture?: {
|
|
46
|
+
input_modalities?: string[];
|
|
47
|
+
output_modalities?: string[];
|
|
48
|
+
};
|
|
49
|
+
/** Where the router got the model — a config preset or the local HF cache. */
|
|
50
|
+
source?: "preset" | "cache" | (string & {});
|
|
51
|
+
can_remove?: boolean;
|
|
52
|
+
meta?: LlamaCppModelMeta;
|
|
53
|
+
};
|
|
4
54
|
type LlamaCppConfig = Omit<OpenAICompatibleConfig, "baseURL" | "model" | "vendor"> & {
|
|
5
55
|
/** Base URL of the llama.cpp server's OpenAI-compatible API (default: `http://localhost:8080/v1`) */
|
|
6
56
|
baseURL?: string;
|
|
@@ -32,6 +82,23 @@ type LlamaCppConfig = Omit<OpenAICompatibleConfig, "baseURL" | "model" | "vendor
|
|
|
32
82
|
export declare class LlamaCppAgent extends OpenAICompatibleAgent {
|
|
33
83
|
constructor(config: LlamaCppConfig, history?: History);
|
|
34
84
|
protected getVendorName(): string;
|
|
85
|
+
/**
|
|
86
|
+
* List the models the server offers, adding the two things llama.cpp reports
|
|
87
|
+
* beyond the OpenAI-standard fields:
|
|
88
|
+
*
|
|
89
|
+
* - `loaded` — in model-router mode a listed model is not necessarily in
|
|
90
|
+
* memory; an unloaded one has to be loaded before it answers. Left
|
|
91
|
+
* undefined by a single-model server, which reports no status at all.
|
|
92
|
+
* - `contextLength` — `meta.n_ctx`, the context the model was actually loaded
|
|
93
|
+
* with, falling back to the trained ceiling `n_ctx_train`. Only loaded
|
|
94
|
+
* models carry `meta`.
|
|
95
|
+
*
|
|
96
|
+
* `capabilities.vision` follows from the declared input modalities. Tool
|
|
97
|
+
* support is not reported — it depends on the chat template, not the server —
|
|
98
|
+
* so it stays undefined. The rest, launch args and presets and quantization
|
|
99
|
+
* included, is on `raw`.
|
|
100
|
+
*/
|
|
101
|
+
listModels(): Promise<ModelInfo<LlamaCppModelCard>[]>;
|
|
35
102
|
}
|
|
36
103
|
export {};
|
|
37
104
|
//# sourceMappingURL=LlamaCppAgent.d.ts.map
|
|
@@ -39,6 +39,35 @@ class LlamaCppAgent extends OpenAICompatibleAgent_1.OpenAICompatibleAgent {
|
|
|
39
39
|
getVendorName() {
|
|
40
40
|
return "llama.cpp";
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* List the models the server offers, adding the two things llama.cpp reports
|
|
44
|
+
* beyond the OpenAI-standard fields:
|
|
45
|
+
*
|
|
46
|
+
* - `loaded` — in model-router mode a listed model is not necessarily in
|
|
47
|
+
* memory; an unloaded one has to be loaded before it answers. Left
|
|
48
|
+
* undefined by a single-model server, which reports no status at all.
|
|
49
|
+
* - `contextLength` — `meta.n_ctx`, the context the model was actually loaded
|
|
50
|
+
* with, falling back to the trained ceiling `n_ctx_train`. Only loaded
|
|
51
|
+
* models carry `meta`.
|
|
52
|
+
*
|
|
53
|
+
* `capabilities.vision` follows from the declared input modalities. Tool
|
|
54
|
+
* support is not reported — it depends on the chat template, not the server —
|
|
55
|
+
* so it stays undefined. The rest, launch args and presets and quantization
|
|
56
|
+
* included, is on `raw`.
|
|
57
|
+
*/
|
|
58
|
+
async listModels() {
|
|
59
|
+
const models = (await super.listModels());
|
|
60
|
+
return models.map((model) => ({
|
|
61
|
+
...model,
|
|
62
|
+
loaded: model.raw.status
|
|
63
|
+
? model.raw.status.value === "loaded"
|
|
64
|
+
: undefined,
|
|
65
|
+
contextLength: model.raw.meta?.n_ctx ?? model.raw.meta?.n_ctx_train,
|
|
66
|
+
capabilities: {
|
|
67
|
+
vision: model.raw.architecture?.input_modalities?.includes("image"),
|
|
68
|
+
},
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
42
71
|
}
|
|
43
72
|
exports.LlamaCppAgent = LlamaCppAgent;
|
|
44
73
|
//# sourceMappingURL=LlamaCppAgent.js.map
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import { BaseAgent, BaseAgentConfig, TokenUsage } from "../BaseAgent";
|
|
1
|
+
import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
|
|
2
2
|
import { History, MessageContent } from "../../history/History";
|
|
3
|
-
import { ChatCompletionResponse, Tool, UsageInfo } from "@mistralai/mistralai/models/components";
|
|
3
|
+
import { ChatCompletionResponse, ModelList, Tool, UsageInfo } from "@mistralai/mistralai/models/components";
|
|
4
4
|
import { MistralModel } from "../model-types";
|
|
5
|
+
/**
|
|
6
|
+
* One entry from Mistral's `/v1/models` response — a base model card or a
|
|
7
|
+
* fine-tuned one. Derived from the SDK's `ModelList` rather than imported
|
|
8
|
+
* directly, since the SDK exports the union under the unhelpful name `Data`.
|
|
9
|
+
*/
|
|
10
|
+
export type MistralModelCard = NonNullable<ModelList["data"]>[number];
|
|
5
11
|
type AgentConfig = BaseAgentConfig & {
|
|
6
12
|
apiKey: string;
|
|
7
13
|
model?: MistralModel;
|
|
@@ -44,6 +50,20 @@ export declare class MistralAgent extends BaseAgent {
|
|
|
44
50
|
/** Count of tool calls in current execution */
|
|
45
51
|
private currentToolCallCount;
|
|
46
52
|
constructor(config: Omit<AgentConfig, "vendor">, history?: History);
|
|
53
|
+
/**
|
|
54
|
+
* List the models available to this API key, base and fine-tuned alike.
|
|
55
|
+
*
|
|
56
|
+
* Mistral is the most forthcoming of the providers: it reports a context
|
|
57
|
+
* window, a full capability set, and a retirement date with a replacement
|
|
58
|
+
* model. All of that is mapped onto the neutral fields.
|
|
59
|
+
*
|
|
60
|
+
* Note that `raw` here is the SDK's parsed view, not the wire response — the
|
|
61
|
+
* Mistral SDK validates against a schema that drops fields it does not know,
|
|
62
|
+
* so capabilities the API has added since the installed SDK version (as of
|
|
63
|
+
* `1.13.0`: `reasoning`, the audio flags) are gone before this code sees
|
|
64
|
+
* them. Every other agent's `raw` is the untouched response.
|
|
65
|
+
*/
|
|
66
|
+
listModels(): Promise<ModelInfo<MistralModelCard>[]>;
|
|
47
67
|
protected getToolDefinitions(): Tool[];
|
|
48
68
|
protected process(_input: string): Promise<string>;
|
|
49
69
|
execute(input: string | MessageContent[]): Promise<string>;
|
|
@@ -80,6 +80,42 @@ class MistralAgent extends BaseAgent_1.BaseAgent {
|
|
|
80
80
|
// Add system message to history (skips if already exists with same content)
|
|
81
81
|
this.addSystemMessage(this.getSystemMessage());
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* List the models available to this API key, base and fine-tuned alike.
|
|
85
|
+
*
|
|
86
|
+
* Mistral is the most forthcoming of the providers: it reports a context
|
|
87
|
+
* window, a full capability set, and a retirement date with a replacement
|
|
88
|
+
* model. All of that is mapped onto the neutral fields.
|
|
89
|
+
*
|
|
90
|
+
* Note that `raw` here is the SDK's parsed view, not the wire response — the
|
|
91
|
+
* Mistral SDK validates against a schema that drops fields it does not know,
|
|
92
|
+
* so capabilities the API has added since the installed SDK version (as of
|
|
93
|
+
* `1.13.0`: `reasoning`, the audio flags) are gone before this code sees
|
|
94
|
+
* them. Every other agent's `raw` is the untouched response.
|
|
95
|
+
*/
|
|
96
|
+
async listModels() {
|
|
97
|
+
try {
|
|
98
|
+
const response = await this.client.models.list();
|
|
99
|
+
return (response.data ?? []).map((model) => ({
|
|
100
|
+
id: model.id,
|
|
101
|
+
displayName: model.name ?? undefined,
|
|
102
|
+
created: model.created ? new Date(model.created * 1000) : undefined,
|
|
103
|
+
ownedBy: model.ownedBy,
|
|
104
|
+
contextLength: model.maxContextLength,
|
|
105
|
+
capabilities: {
|
|
106
|
+
chat: model.capabilities.completionChat,
|
|
107
|
+
tools: model.capabilities.functionCalling,
|
|
108
|
+
vision: model.capabilities.vision,
|
|
109
|
+
},
|
|
110
|
+
deprecatedAt: model.deprecation ?? undefined,
|
|
111
|
+
replacedBy: model.deprecationReplacementModel ?? undefined,
|
|
112
|
+
raw: model,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
throw new AgentError_1.ExecutionError(`Failed to list Mistral models: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
83
119
|
getToolDefinitions() {
|
|
84
120
|
return Array.from(this.tools.values()).map((tool) => ({
|
|
85
121
|
type: components_1.ToolTypes.Function,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseAgent, BaseAgentConfig, TokenUsage } from "../BaseAgent";
|
|
1
|
+
import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
|
|
2
2
|
import { History, MessageContent } from "../../history/History";
|
|
3
3
|
import { OllamaModel } from "../model-types";
|
|
4
4
|
type AgentConfig = BaseAgentConfig & {
|
|
@@ -56,8 +56,12 @@ export declare class OllamaAgent extends BaseAgent {
|
|
|
56
56
|
private getClient;
|
|
57
57
|
/**
|
|
58
58
|
* List the models currently available on the Ollama server.
|
|
59
|
+
*
|
|
60
|
+
* `id` is the tag to pass as `model` (e.g. `"llama3.2:latest"`) and `created`
|
|
61
|
+
* carries the local `modified_at` timestamp — Ollama reports when a model was
|
|
62
|
+
* last pulled or changed on this machine, not when it was released.
|
|
59
63
|
*/
|
|
60
|
-
listModels(): Promise<OllamaModelInfo[]>;
|
|
64
|
+
listModels(): Promise<ModelInfo<OllamaModelInfo>[]>;
|
|
61
65
|
protected getToolDefinitions(): OllamaToolDefinition[];
|
|
62
66
|
protected process(_input: string): Promise<string>;
|
|
63
67
|
execute(input: string | MessageContent[]): Promise<string>;
|
|
@@ -113,12 +113,21 @@ class OllamaAgent extends BaseAgent_1.BaseAgent {
|
|
|
113
113
|
}
|
|
114
114
|
/**
|
|
115
115
|
* List the models currently available on the Ollama server.
|
|
116
|
+
*
|
|
117
|
+
* `id` is the tag to pass as `model` (e.g. `"llama3.2:latest"`) and `created`
|
|
118
|
+
* carries the local `modified_at` timestamp — Ollama reports when a model was
|
|
119
|
+
* last pulled or changed on this machine, not when it was released.
|
|
116
120
|
*/
|
|
117
121
|
async listModels() {
|
|
118
122
|
try {
|
|
119
123
|
const client = await this.getClient();
|
|
120
124
|
const response = await client.list();
|
|
121
|
-
return response.models
|
|
125
|
+
return response.models.map((model) => ({
|
|
126
|
+
id: model.model,
|
|
127
|
+
displayName: model.name,
|
|
128
|
+
created: model.modified_at ? new Date(model.modified_at) : undefined,
|
|
129
|
+
raw: model,
|
|
130
|
+
}));
|
|
122
131
|
}
|
|
123
132
|
catch (error) {
|
|
124
133
|
throw new AgentError_1.ExecutionError(`Failed to list Ollama models: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { BaseAgent, BaseAgentConfig, TokenUsage } from "../BaseAgent";
|
|
1
|
+
import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
|
|
2
2
|
import { History, MessageContent } from "../../history/History";
|
|
3
3
|
import { Tool, Response, ResponseUsage } from "openai/resources/responses/responses";
|
|
4
|
+
import type { Model as OpenAIModelCard } from "openai/resources/models";
|
|
4
5
|
import { OpenAIModel, ReasoningEffort, ReasoningEffortFor } from "../model-types";
|
|
5
6
|
import { StreamChunk } from "../openai-compatible/OpenAICompatibleAgent";
|
|
6
7
|
type AgentConfig<M extends OpenAIModel = OpenAIModel> = BaseAgentConfig & {
|
|
@@ -67,6 +68,14 @@ export declare class OpenAiAgent<M extends OpenAIModel = OpenAIModel> extends Ba
|
|
|
67
68
|
/** Count of tool calls in current execution */
|
|
68
69
|
private currentToolCallCount;
|
|
69
70
|
constructor(config: Omit<AgentConfig<M>, "vendor">, history?: History);
|
|
71
|
+
/**
|
|
72
|
+
* List the models available to this API key.
|
|
73
|
+
*
|
|
74
|
+
* The list covers everything the key can reach — chat, embedding, audio and
|
|
75
|
+
* image models alike — so filter by `id` if you only want the ones this
|
|
76
|
+
* agent can drive.
|
|
77
|
+
*/
|
|
78
|
+
listModels(): Promise<ModelInfo<OpenAIModelCard>[]>;
|
|
70
79
|
protected getToolDefinitions(): Tool[];
|
|
71
80
|
/**
|
|
72
81
|
* Build the `reasoning` field for a Responses API request, as an object to
|
|
@@ -13,6 +13,7 @@ const transformers_1 = require("../../history/transformers");
|
|
|
13
13
|
const VizReporter_1 = require("../../viz/VizReporter");
|
|
14
14
|
const VizConfig_1 = require("../../viz/VizConfig");
|
|
15
15
|
const model_types_1 = require("../model-types");
|
|
16
|
+
const openai_strict_1 = require("./openai-strict");
|
|
16
17
|
/**
|
|
17
18
|
* Lowest `reasoning.effort` the given model accepts, used to resolve
|
|
18
19
|
* `disableReasoning`. Returns `undefined` when the model has no reasoning to turn
|
|
@@ -87,20 +88,44 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
87
88
|
// Add system message to history (skips if already exists with same content)
|
|
88
89
|
this.addSystemMessage(this.getSystemMessage());
|
|
89
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* List the models available to this API key.
|
|
93
|
+
*
|
|
94
|
+
* The list covers everything the key can reach — chat, embedding, audio and
|
|
95
|
+
* image models alike — so filter by `id` if you only want the ones this
|
|
96
|
+
* agent can drive.
|
|
97
|
+
*/
|
|
98
|
+
async listModels() {
|
|
99
|
+
try {
|
|
100
|
+
const page = await this.client.models.list();
|
|
101
|
+
return page.data.map((model) => ({
|
|
102
|
+
id: model.id,
|
|
103
|
+
created: model.created ? new Date(model.created * 1000) : undefined,
|
|
104
|
+
ownedBy: model.owned_by,
|
|
105
|
+
raw: model,
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
throw new AgentError_1.ExecutionError(`Failed to list OpenAI models: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
90
112
|
getToolDefinitions() {
|
|
91
113
|
return Array.from(this.tools.values()).map((tool) => {
|
|
92
114
|
const prompt = tool.getPrompt();
|
|
115
|
+
const parameters = {
|
|
116
|
+
type: prompt.input_schema.type,
|
|
117
|
+
properties: prompt.input_schema.properties,
|
|
118
|
+
required: prompt.input_schema.required,
|
|
119
|
+
additionalProperties: false,
|
|
120
|
+
};
|
|
93
121
|
return {
|
|
94
122
|
type: "function",
|
|
95
123
|
name: prompt.name,
|
|
96
124
|
description: prompt.description,
|
|
97
|
-
parameters
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
additionalProperties: false,
|
|
102
|
-
},
|
|
103
|
-
strict: true,
|
|
125
|
+
parameters,
|
|
126
|
+
// Per tool, not unconditional: strict mode requires `required` to name
|
|
127
|
+
// every property, so one optional parameter would 400 the whole request
|
|
128
|
+
strict: (0, openai_strict_1.canUseStrictSchema)(parameters),
|
|
104
129
|
};
|
|
105
130
|
});
|
|
106
131
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI strict function schemas.
|
|
3
|
+
*
|
|
4
|
+
* `strict: true` is a stronger contract than JSON Schema: OpenAI requires
|
|
5
|
+
* `required` to list *every* key in `properties`, so a tool with an optional
|
|
6
|
+
* parameter is rejected with a 400 before the model ever runs. Sending it
|
|
7
|
+
* unconditionally makes any such tool break the whole request — and since the
|
|
8
|
+
* tool belt is identical on every retry, every request of the session fails.
|
|
9
|
+
*
|
|
10
|
+
* The alternative fix is to declare optional parameters as nullable and require
|
|
11
|
+
* them anyway, per OpenAI's own guidance. In a library that is worse: it
|
|
12
|
+
* changes the schema every *other* provider sees (Gemini does not accept a
|
|
13
|
+
* `["string", "null"]` type union), and it makes each tool responsible for
|
|
14
|
+
* telling an explicit null from an omitted argument. Deciding `strict` per tool
|
|
15
|
+
* leaves the schemas untouched and keeps the guarantee for the tools that can
|
|
16
|
+
* already honour it.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Whether OpenAI will accept this parameter schema under `strict: true`.
|
|
20
|
+
*
|
|
21
|
+
* Conservative on purpose: a false negative costs the schema-adherence
|
|
22
|
+
* guarantee for one tool, a false positive costs the whole request. MCP tools
|
|
23
|
+
* come from servers the host does not control, so "unrecognised shape" has to
|
|
24
|
+
* mean no.
|
|
25
|
+
*/
|
|
26
|
+
export declare function canUseStrictSchema(parameters: unknown): boolean;
|
|
27
|
+
//# sourceMappingURL=openai-strict.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OpenAI strict function schemas.
|
|
4
|
+
*
|
|
5
|
+
* `strict: true` is a stronger contract than JSON Schema: OpenAI requires
|
|
6
|
+
* `required` to list *every* key in `properties`, so a tool with an optional
|
|
7
|
+
* parameter is rejected with a 400 before the model ever runs. Sending it
|
|
8
|
+
* unconditionally makes any such tool break the whole request — and since the
|
|
9
|
+
* tool belt is identical on every retry, every request of the session fails.
|
|
10
|
+
*
|
|
11
|
+
* The alternative fix is to declare optional parameters as nullable and require
|
|
12
|
+
* them anyway, per OpenAI's own guidance. In a library that is worse: it
|
|
13
|
+
* changes the schema every *other* provider sees (Gemini does not accept a
|
|
14
|
+
* `["string", "null"]` type union), and it makes each tool responsible for
|
|
15
|
+
* telling an explicit null from an omitted argument. Deciding `strict` per tool
|
|
16
|
+
* leaves the schemas untouched and keeps the guarantee for the tools that can
|
|
17
|
+
* already honour it.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.canUseStrictSchema = canUseStrictSchema;
|
|
21
|
+
/**
|
|
22
|
+
* Whether OpenAI will accept this parameter schema under `strict: true`.
|
|
23
|
+
*
|
|
24
|
+
* Conservative on purpose: a false negative costs the schema-adherence
|
|
25
|
+
* guarantee for one tool, a false positive costs the whole request. MCP tools
|
|
26
|
+
* come from servers the host does not control, so "unrecognised shape" has to
|
|
27
|
+
* mean no.
|
|
28
|
+
*/
|
|
29
|
+
function canUseStrictSchema(parameters) {
|
|
30
|
+
if (!parameters || typeof parameters !== "object")
|
|
31
|
+
return false;
|
|
32
|
+
const schema = parameters;
|
|
33
|
+
const properties = schema.properties && typeof schema.properties === "object"
|
|
34
|
+
? schema.properties
|
|
35
|
+
: {};
|
|
36
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
37
|
+
// Strict mode also wants `additionalProperties: false` on every nested
|
|
38
|
+
// object, which `getToolDefinitions()` only sets at the top level. Rather
|
|
39
|
+
// than rewrite anyone's schema, treat a nested object as reason enough to
|
|
40
|
+
// drop the guarantee.
|
|
41
|
+
const isNested = (property) => {
|
|
42
|
+
if (!property || typeof property !== "object")
|
|
43
|
+
return false;
|
|
44
|
+
const value = property;
|
|
45
|
+
return value.type === "object" || isNested(value.items);
|
|
46
|
+
};
|
|
47
|
+
return (Object.keys(properties).every((key) => required.includes(key)) &&
|
|
48
|
+
!Object.values(properties).some(isNested));
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=openai-strict.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
import { ChatCompletion, ChatCompletionTool } from "openai/resources/chat/completions";
|
|
3
3
|
import { Model } from "openai/resources/models";
|
|
4
|
-
import { BaseAgent, BaseAgentConfig, TokenUsage } from "../BaseAgent";
|
|
4
|
+
import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
|
|
5
5
|
import { AgentVendor } from "../AgentConfig";
|
|
6
6
|
import { History, MessageContent } from "../../history/History";
|
|
7
7
|
/**
|
|
@@ -43,8 +43,11 @@ export declare abstract class OpenAICompatibleAgent extends BaseAgent {
|
|
|
43
43
|
protected buildExtraRequestParams(): Record<string, unknown>;
|
|
44
44
|
/**
|
|
45
45
|
* List the models available on the server via the `/v1/models` endpoint.
|
|
46
|
+
*
|
|
47
|
+
* Local servers vary in how much they fill in — llama.cpp reports little
|
|
48
|
+
* beyond the id — so most fields other than `id` are typically undefined.
|
|
46
49
|
*/
|
|
47
|
-
listModels(): Promise<Model[]>;
|
|
50
|
+
listModels(): Promise<ModelInfo<Model>[]>;
|
|
48
51
|
protected getToolDefinitions(): ChatCompletionTool[];
|
|
49
52
|
protected process(_input: string): Promise<string>;
|
|
50
53
|
execute(input: string | MessageContent[]): Promise<string>;
|
|
@@ -50,11 +50,19 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
|
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* List the models available on the server via the `/v1/models` endpoint.
|
|
53
|
+
*
|
|
54
|
+
* Local servers vary in how much they fill in — llama.cpp reports little
|
|
55
|
+
* beyond the id — so most fields other than `id` are typically undefined.
|
|
53
56
|
*/
|
|
54
57
|
async listModels() {
|
|
55
58
|
try {
|
|
56
59
|
const page = await this.client.models.list();
|
|
57
|
-
return page.data
|
|
60
|
+
return page.data.map((model) => ({
|
|
61
|
+
id: model.id,
|
|
62
|
+
created: model.created ? new Date(model.created * 1000) : undefined,
|
|
63
|
+
ownedBy: model.owned_by,
|
|
64
|
+
raw: model,
|
|
65
|
+
}));
|
|
58
66
|
}
|
|
59
67
|
catch (error) {
|
|
60
68
|
throw new AgentError_1.ExecutionError(`Failed to list ${this.getVendorName()} models: ${error instanceof Error ? error.message : "Unknown error"}`);
|
package/dist/gemini.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from "./core";
|
|
2
|
-
export { GeminiAgent } from "./agents/google/GeminiAgent";
|
|
2
|
+
export { GeminiAgent, GEMINI_RETIRED_MODELS, } from "./agents/google/GeminiAgent";
|
|
3
|
+
export type { GeminiModelCard, GeminiListModelsOptions, } from "./agents/google/GeminiAgent";
|
|
3
4
|
export { geminiTransformer } from "./history/transformers";
|
|
4
5
|
//# sourceMappingURL=gemini.d.ts.map
|
package/dist/gemini.js
CHANGED
|
@@ -14,11 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.geminiTransformer = exports.GeminiAgent = void 0;
|
|
17
|
+
exports.geminiTransformer = exports.GEMINI_RETIRED_MODELS = exports.GeminiAgent = void 0;
|
|
18
18
|
// Gemini Agent Entry Point
|
|
19
19
|
__exportStar(require("./core"), exports);
|
|
20
20
|
var GeminiAgent_1 = require("./agents/google/GeminiAgent");
|
|
21
21
|
Object.defineProperty(exports, "GeminiAgent", { enumerable: true, get: function () { return GeminiAgent_1.GeminiAgent; } });
|
|
22
|
+
Object.defineProperty(exports, "GEMINI_RETIRED_MODELS", { enumerable: true, get: function () { return GeminiAgent_1.GEMINI_RETIRED_MODELS; } });
|
|
22
23
|
var transformers_1 = require("./history/transformers");
|
|
23
24
|
Object.defineProperty(exports, "geminiTransformer", { enumerable: true, get: function () { return transformers_1.geminiTransformer; } });
|
|
24
25
|
//# sourceMappingURL=gemini.js.map
|
|
@@ -449,17 +449,34 @@ exports.geminiTransformer = {
|
|
|
449
449
|
name: block.name,
|
|
450
450
|
args: block.input,
|
|
451
451
|
},
|
|
452
|
+
// Gemini 3 requires its own reasoning token back on the part it came
|
|
453
|
+
// from, or it rejects the follow-up request outright
|
|
454
|
+
...(block.thoughtSignature
|
|
455
|
+
? { thoughtSignature: block.thoughtSignature }
|
|
456
|
+
: {}),
|
|
452
457
|
});
|
|
453
458
|
}
|
|
454
459
|
// Add function response parts (for user messages with tool results)
|
|
455
460
|
for (const block of toolResultBlocks) {
|
|
456
|
-
//
|
|
461
|
+
// `functionResponse.response` is a protobuf Struct, so it has to be a
|
|
462
|
+
// JSON *object*. Parsing alone is not enough: a tool returning a plain
|
|
463
|
+
// string is stored as `JSON.stringify(result)`, which parses back to a
|
|
464
|
+
// string rather than throwing, so a bare scalar would go out and Gemini
|
|
465
|
+
// would answer 400 with the tool output quoted back. Anything that is
|
|
466
|
+
// not already an object is nested under `result` — the same shape the
|
|
467
|
+
// parse-failure path produces, so the model sees no difference and no
|
|
468
|
+
// tool has to know about any of this.
|
|
457
469
|
let responseData;
|
|
458
470
|
try {
|
|
459
471
|
responseData = JSON.parse(block.content);
|
|
460
472
|
}
|
|
461
473
|
catch {
|
|
462
|
-
responseData =
|
|
474
|
+
responseData = block.content;
|
|
475
|
+
}
|
|
476
|
+
if (typeof responseData !== "object" ||
|
|
477
|
+
responseData === null ||
|
|
478
|
+
Array.isArray(responseData)) {
|
|
479
|
+
responseData = { result: responseData ?? "" };
|
|
463
480
|
}
|
|
464
481
|
parts.push({
|
|
465
482
|
functionResponse: {
|
|
@@ -485,8 +502,14 @@ exports.geminiTransformer = {
|
|
|
485
502
|
}
|
|
486
503
|
if ("functionCall" in part && part.functionCall) {
|
|
487
504
|
const fc = part.functionCall;
|
|
488
|
-
normalizedContent.push((0, types_1.toolUse)(
|
|
489
|
-
|
|
505
|
+
normalizedContent.push((0, types_1.toolUse)(
|
|
506
|
+
// Deliberately the name, not the `id` the live response also
|
|
507
|
+
// carries: `toProvider` sends a tool result as
|
|
508
|
+
// `functionResponse.name = block.tool_use_id`, and Gemini requires
|
|
509
|
+
// that to be the function name. Keying the block by Gemini's id
|
|
510
|
+
// would desynchronise the tool_use/tool_result pair without
|
|
511
|
+
// buying anything — the id is never echoed back.
|
|
512
|
+
fc.name, fc.name, (fc.args || {}), part.thoughtSignature));
|
|
490
513
|
}
|
|
491
514
|
}
|
|
492
515
|
return {
|
package/dist/history/types.d.ts
CHANGED
|
@@ -19,6 +19,15 @@ export type ToolUseContent = {
|
|
|
19
19
|
id: string;
|
|
20
20
|
name: string;
|
|
21
21
|
input: Record<string, unknown>;
|
|
22
|
+
/**
|
|
23
|
+
* Provider-opaque reasoning token that has to be echoed back verbatim.
|
|
24
|
+
*
|
|
25
|
+
* Gemini 3 returns one beside every `functionCall` and rejects any later
|
|
26
|
+
* request in the conversation that omits it — "Function call is missing a
|
|
27
|
+
* thought_signature in functionCall parts". Nothing reads its contents; it
|
|
28
|
+
* only has to survive the round trip through history.
|
|
29
|
+
*/
|
|
30
|
+
thoughtSignature?: string;
|
|
22
31
|
};
|
|
23
32
|
/**
|
|
24
33
|
* Result of a tool execution
|
|
@@ -174,7 +183,7 @@ export declare function text(value: string): TextContent;
|
|
|
174
183
|
/**
|
|
175
184
|
* Create a tool use content block
|
|
176
185
|
*/
|
|
177
|
-
export declare function toolUse(id: string, name: string, input: Record<string, unknown
|
|
186
|
+
export declare function toolUse(id: string, name: string, input: Record<string, unknown>, thoughtSignature?: string): ToolUseContent;
|
|
178
187
|
/**
|
|
179
188
|
* Create a thinking content block. Pass `redactedData` for redacted thinking.
|
|
180
189
|
*/
|
package/dist/history/types.js
CHANGED
|
@@ -56,8 +56,16 @@ function text(value) {
|
|
|
56
56
|
/**
|
|
57
57
|
* Create a tool use content block
|
|
58
58
|
*/
|
|
59
|
-
function toolUse(id, name, input) {
|
|
60
|
-
|
|
59
|
+
function toolUse(id, name, input, thoughtSignature) {
|
|
60
|
+
// Only set the key when there is one, so a block stored without a signature
|
|
61
|
+
// serializes exactly as it did before the field existed
|
|
62
|
+
return {
|
|
63
|
+
type: "tool_use",
|
|
64
|
+
id,
|
|
65
|
+
name,
|
|
66
|
+
input,
|
|
67
|
+
...(thoughtSignature ? { thoughtSignature } : {}),
|
|
68
|
+
};
|
|
61
69
|
}
|
|
62
70
|
/**
|
|
63
71
|
* Create a thinking content block. Pass `redactedData` for redacted thinking.
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,13 @@ export * from "./agents/BaseAgent";
|
|
|
2
2
|
export * from "./agents/anthropic/ClaudeAgent";
|
|
3
3
|
export { OpenAiAgent } from "./agents/openai/OpenAiAgent";
|
|
4
4
|
export { MistralAgent } from "./agents/mistral/MistralAgent";
|
|
5
|
-
export {
|
|
5
|
+
export type { MistralModelCard } from "./agents/mistral/MistralAgent";
|
|
6
|
+
export { GeminiAgent, GEMINI_RETIRED_MODELS, } from "./agents/google/GeminiAgent";
|
|
7
|
+
export type { GeminiModelCard, GeminiListModelsOptions, } from "./agents/google/GeminiAgent";
|
|
6
8
|
export { OllamaAgent } from "./agents/ollama/OllamaAgent";
|
|
9
|
+
export type { OllamaModelInfo } from "./agents/ollama/OllamaAgent";
|
|
7
10
|
export { LlamaCppAgent } from "./agents/llamacpp/LlamaCppAgent";
|
|
11
|
+
export type { LlamaCppModelCard, LlamaCppModelMeta, } from "./agents/llamacpp/LlamaCppAgent";
|
|
8
12
|
export { OpenAICompatibleAgent } from "./agents/openai-compatible/OpenAICompatibleAgent";
|
|
9
13
|
export type { OpenAICompatibleConfig, StreamChunk } from "./agents/openai-compatible/OpenAICompatibleAgent";
|
|
10
14
|
export * from "./agents/model-types";
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
22
22
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.chatCompletionsTransformer = exports.ollamaTransformer = exports.geminiTransformer = exports.mistralTransformer = exports.openAiTransformer = exports.anthropicTransformer = exports.OpenAICompatibleAgent = exports.LlamaCppAgent = exports.OllamaAgent = exports.GeminiAgent = exports.MistralAgent = exports.OpenAiAgent = void 0;
|
|
25
|
+
exports.chatCompletionsTransformer = exports.ollamaTransformer = exports.geminiTransformer = exports.mistralTransformer = exports.openAiTransformer = exports.anthropicTransformer = exports.OpenAICompatibleAgent = exports.LlamaCppAgent = exports.OllamaAgent = exports.GEMINI_RETIRED_MODELS = exports.GeminiAgent = exports.MistralAgent = exports.OpenAiAgent = void 0;
|
|
26
26
|
// Agents
|
|
27
27
|
__exportStar(require("./agents/BaseAgent"), exports);
|
|
28
28
|
__exportStar(require("./agents/anthropic/ClaudeAgent"), exports);
|
|
@@ -32,6 +32,7 @@ var MistralAgent_1 = require("./agents/mistral/MistralAgent");
|
|
|
32
32
|
Object.defineProperty(exports, "MistralAgent", { enumerable: true, get: function () { return MistralAgent_1.MistralAgent; } });
|
|
33
33
|
var GeminiAgent_1 = require("./agents/google/GeminiAgent");
|
|
34
34
|
Object.defineProperty(exports, "GeminiAgent", { enumerable: true, get: function () { return GeminiAgent_1.GeminiAgent; } });
|
|
35
|
+
Object.defineProperty(exports, "GEMINI_RETIRED_MODELS", { enumerable: true, get: function () { return GeminiAgent_1.GEMINI_RETIRED_MODELS; } });
|
|
35
36
|
var OllamaAgent_1 = require("./agents/ollama/OllamaAgent");
|
|
36
37
|
Object.defineProperty(exports, "OllamaAgent", { enumerable: true, get: function () { return OllamaAgent_1.OllamaAgent; } });
|
|
37
38
|
var LlamaCppAgent_1 = require("./agents/llamacpp/LlamaCppAgent");
|
package/dist/llamacpp.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./core";
|
|
2
2
|
export { LlamaCppAgent } from "./agents/llamacpp/LlamaCppAgent";
|
|
3
|
+
export type { LlamaCppModelCard, LlamaCppModelMeta, } from "./agents/llamacpp/LlamaCppAgent";
|
|
3
4
|
export { OpenAICompatibleAgent } from "./agents/openai-compatible/OpenAICompatibleAgent";
|
|
4
5
|
export type { OpenAICompatibleConfig, StreamChunk } from "./agents/openai-compatible/OpenAICompatibleAgent";
|
|
5
6
|
export { chatCompletionsTransformer } from "./history/transformers";
|
package/dist/mistral.d.ts
CHANGED
package/dist/ollama.d.ts
CHANGED