@framers/agentos 0.10.21 → 0.10.23
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/api/embedText.d.ts +9 -4
- package/dist/api/embedText.d.ts.map +1 -1
- package/dist/api/embedText.js +114 -8
- package/dist/api/embedText.js.map +1 -1
- package/dist/api/generateText.d.ts +14 -1
- package/dist/api/generateText.d.ts.map +1 -1
- package/dist/api/generateText.js +165 -20
- package/dist/api/generateText.js.map +1 -1
- package/dist/api/model.d.ts +25 -1
- package/dist/api/model.d.ts.map +1 -1
- package/dist/api/model.js +105 -3
- package/dist/api/model.js.map +1 -1
- package/dist/api/runtime/global-default.d.ts +9 -1
- package/dist/api/runtime/global-default.d.ts.map +1 -1
- package/dist/api/runtime/global-default.js.map +1 -1
- package/dist/api/runtime/provider-defaults.d.ts.map +1 -1
- package/dist/api/runtime/provider-defaults.js +11 -4
- package/dist/api/runtime/provider-defaults.js.map +1 -1
- package/dist/api/runtime/tool-emulation/history.d.ts +16 -0
- package/dist/api/runtime/tool-emulation/history.d.ts.map +1 -0
- package/dist/api/runtime/tool-emulation/history.js +89 -0
- package/dist/api/runtime/tool-emulation/history.js.map +1 -0
- package/dist/api/runtime/tool-emulation/index.d.ts +1 -0
- package/dist/api/runtime/tool-emulation/index.d.ts.map +1 -1
- package/dist/api/runtime/tool-emulation/index.js +1 -0
- package/dist/api/runtime/tool-emulation/index.js.map +1 -1
- package/dist/api/runtime/tool-emulation/loop.d.ts +6 -0
- package/dist/api/runtime/tool-emulation/loop.d.ts.map +1 -1
- package/dist/api/runtime/tool-emulation/loop.js +1 -0
- package/dist/api/runtime/tool-emulation/loop.js.map +1 -1
- package/dist/api/sessionTranscript.d.ts +40 -0
- package/dist/api/sessionTranscript.d.ts.map +1 -1
- package/dist/api/sessionTranscript.js +61 -1
- package/dist/api/sessionTranscript.js.map +1 -1
- package/dist/api/streamText.d.ts.map +1 -1
- package/dist/api/streamText.js +28 -7
- package/dist/api/streamText.js.map +1 -1
- package/dist/cognition/substrate/ConversationHistoryManager.d.ts.map +1 -1
- package/dist/cognition/substrate/ConversationHistoryManager.js +2 -0
- package/dist/cognition/substrate/ConversationHistoryManager.js.map +1 -1
- package/dist/cognition/substrate/GMI.d.ts.map +1 -1
- package/dist/cognition/substrate/GMI.js +3 -1
- package/dist/cognition/substrate/GMI.js.map +1 -1
- package/dist/cognition/substrate/IGMI.d.ts +5 -0
- package/dist/cognition/substrate/IGMI.d.ts.map +1 -1
- package/dist/cognition/substrate/IGMI.js.map +1 -1
- package/dist/core/llm/PromptEngine.d.ts.map +1 -1
- package/dist/core/llm/PromptEngine.js +1 -0
- package/dist/core/llm/PromptEngine.js.map +1 -1
- package/dist/core/llm/providers/AIModelProviderManager.d.ts +20 -0
- package/dist/core/llm/providers/AIModelProviderManager.d.ts.map +1 -1
- package/dist/core/llm/providers/AIModelProviderManager.js +35 -5
- package/dist/core/llm/providers/AIModelProviderManager.js.map +1 -1
- package/dist/core/llm/providers/IProvider.d.ts +9 -0
- package/dist/core/llm/providers/IProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/errors/GeminiProviderError.d.ts +11 -0
- package/dist/core/llm/providers/errors/GeminiProviderError.d.ts.map +1 -1
- package/dist/core/llm/providers/errors/GeminiProviderError.js.map +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts +27 -6
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.js +107 -19
- package/dist/core/llm/providers/implementations/AnthropicProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.d.ts +1 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.js +70 -7
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIBridge.js +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.js +56 -4
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiProvider.d.ts +20 -4
- package/dist/core/llm/providers/implementations/GeminiProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiProvider.js +352 -56
- package/dist/core/llm/providers/implementations/GeminiProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/MistralProvider.d.ts +11 -0
- package/dist/core/llm/providers/implementations/MistralProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/MistralProvider.js +32 -2
- package/dist/core/llm/providers/implementations/MistralProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OllamaProvider.js +7 -7
- package/dist/core/llm/providers/implementations/OllamaProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts +61 -0
- package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/OpenAIProvider.js +171 -29
- package/dist/core/llm/providers/implementations/OpenAIProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OpenRouterProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/OpenRouterProvider.js +14 -5
- package/dist/core/llm/providers/implementations/OpenRouterProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/TogetherProvider.d.ts +3 -3
- package/dist/core/llm/providers/implementations/TogetherProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/TogetherProvider.js +25 -13
- package/dist/core/llm/providers/implementations/TogetherProvider.js.map +1 -1
- package/dist/core/llm/providers/model-forced-tool-choice.d.ts +32 -15
- package/dist/core/llm/providers/model-forced-tool-choice.d.ts.map +1 -1
- package/dist/core/llm/providers/model-forced-tool-choice.js +33 -16
- package/dist/core/llm/providers/model-forced-tool-choice.js.map +1 -1
- package/dist/core/llm/providers/openrouter-only-params.d.ts +26 -7
- package/dist/core/llm/providers/openrouter-only-params.d.ts.map +1 -1
- package/dist/core/llm/providers/openrouter-only-params.js +58 -9
- package/dist/core/llm/providers/openrouter-only-params.js.map +1 -1
- package/dist/core/llm/providers/url-secrets.d.ts +42 -0
- package/dist/core/llm/providers/url-secrets.d.ts.map +1 -0
- package/dist/core/llm/providers/url-secrets.js +64 -0
- package/dist/core/llm/providers/url-secrets.js.map +1 -0
- package/dist/core/llm/streaming/StreamingBatcher.d.ts.map +1 -1
- package/dist/core/llm/streaming/StreamingBatcher.js +3 -0
- package/dist/core/llm/streaming/StreamingBatcher.js.map +1 -1
- package/dist/core/llm/streaming/StreamingReconstructor.d.ts +2 -0
- package/dist/core/llm/streaming/StreamingReconstructor.d.ts.map +1 -1
- package/dist/core/llm/streaming/StreamingReconstructor.js +12 -1
- package/dist/core/llm/streaming/StreamingReconstructor.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/io/vision/providers/LLMVisionProvider.d.ts +2 -2
- package/dist/io/vision/providers/LLMVisionProvider.js +1 -1
- package/dist/io/vision/types.d.ts +1 -1
- package/dist/orchestration/pipeline/query/types.d.ts.map +1 -1
- package/dist/orchestration/pipeline/query/types.js +5 -4
- package/dist/orchestration/pipeline/query/types.js.map +1 -1
- package/dist/orchestration/planning/ProviderAssignmentEngine.d.ts.map +1 -1
- package/dist/orchestration/planning/ProviderAssignmentEngine.js +4 -3
- package/dist/orchestration/planning/ProviderAssignmentEngine.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
// File: backend/agentos/core/llm/providers/implementations/GeminiProvider.ts
|
|
2
2
|
import { stripOpenRouterOnlyParams } from '../openrouter-only-params.js';
|
|
3
|
+
import { redactUrlSecrets } from '../url-secrets.js';
|
|
3
4
|
import { GeminiProviderError } from '../errors/GeminiProviderError.js';
|
|
4
5
|
import { ApiKeyPool } from '../../../providers/ApiKeyPool.js';
|
|
5
6
|
import { computeRetryBackoffMs } from './retry-backoff.js';
|
|
6
7
|
import { sanitizeGeminiResponseSchema } from './geminiResponseSchema.js';
|
|
8
|
+
/**
|
|
9
|
+
* Most requests `models/{model}:batchEmbedContents` accepts in one call. The
|
|
10
|
+
* API returns HTTP 400 "at most 100 requests can be in one batch" above this
|
|
11
|
+
* (probed 2026-09-29 on gemini-embedding-001).
|
|
12
|
+
*/
|
|
13
|
+
const GEMINI_EMBED_BATCH_LIMIT = 100;
|
|
14
|
+
/**
|
|
15
|
+
* Placeholder signature Google documents for replaying a function call whose
|
|
16
|
+
* real signature is unavailable, such as a call made by another provider in a
|
|
17
|
+
* fallback chain. Gemini 3 returns HTTP 400 for a replayed call with no
|
|
18
|
+
* signature and accepts this value, and Gemini 2.5 accepts it too (probed
|
|
19
|
+
* 2026-09-29).
|
|
20
|
+
*/
|
|
21
|
+
const GEMINI_SKIP_THOUGHT_SIGNATURE = 'skip_thought_signature_validator';
|
|
7
22
|
/**
|
|
8
23
|
* Thinking levels each model accepts, keyed by exact model id and limited to
|
|
9
24
|
* what was verified against the live API (2026-09-30). The set differs per
|
|
@@ -48,12 +63,30 @@ const warnedRetiredGeminiModels = new Set();
|
|
|
48
63
|
const GEMINI_LONG_CONTEXT_PRICING = {
|
|
49
64
|
'gemini-3.1-pro-preview': { abovePromptTokens: 200000, input: 4.00, output: 18.00 },
|
|
50
65
|
'gemini-pro-latest': { abovePromptTokens: 200000, input: 4.00, output: 18.00 },
|
|
66
|
+
'gemini-2.5-pro': { abovePromptTokens: 200000, input: 2.50, output: 15.00 },
|
|
51
67
|
};
|
|
52
68
|
// ---------------------------------------------------------------------------
|
|
53
69
|
// Known model catalog
|
|
54
70
|
// ---------------------------------------------------------------------------
|
|
55
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Static catalog of well-known Gemini models and their metadata.
|
|
73
|
+
*
|
|
74
|
+
* Ids and token limits come from a live GET /v1beta/models probe and rates from
|
|
75
|
+
* ai.google.dev/gemini-api/docs/pricing, both on 2026-09-29. `getModelInfo` is
|
|
76
|
+
* an exact-id lookup over this array, so a model missing here resolves to
|
|
77
|
+
* undefined and its calls carry no context limit and no price.
|
|
78
|
+
*
|
|
79
|
+
* Tiers advance independently. Flash has reached 3.8 while Pro is at
|
|
80
|
+
* 3.1-preview, the only Pro id served. `gemini-3-pro-preview` appears in
|
|
81
|
+
* Google's doc index and is absent from the live listing.
|
|
82
|
+
*
|
|
83
|
+
* Rates are the standard tier. Prompts over 200K tokens on 3.1 Pro and 2.5
|
|
84
|
+
* Pro bill at the tier in GEMINI_LONG_CONTEXT_PRICING, and Gemini 3.6 / 3.7 /
|
|
85
|
+
* 3.8 Flash list at promotional rates through 2026-12-31 and meter at the
|
|
86
|
+
* post-promotion rate.
|
|
87
|
+
*/
|
|
56
88
|
const GEMINI_MODELS = [
|
|
89
|
+
// --- Pro ---
|
|
57
90
|
{
|
|
58
91
|
modelId: 'gemini-3.1-pro-preview',
|
|
59
92
|
providerId: 'gemini',
|
|
@@ -68,6 +101,107 @@ const GEMINI_MODELS = [
|
|
|
68
101
|
supportsStreaming: true,
|
|
69
102
|
status: 'active',
|
|
70
103
|
},
|
|
104
|
+
// --- Flash. 3.6, 3.7 and 3.8 list at $0.75 / $3.75 through 2026-12-31 and
|
|
105
|
+
// $1.50 / $7.50 from 2027-01-01. They meter at $1.50 / $7.50, as
|
|
106
|
+
// claude-sonnet-5 meters at its sticker rate in the Anthropic catalog, so
|
|
107
|
+
// cost rollups stay conservative and stay right after the promotion. ---
|
|
108
|
+
{
|
|
109
|
+
modelId: 'gemini-3.8-flash',
|
|
110
|
+
providerId: 'gemini',
|
|
111
|
+
displayName: 'Gemini 3.8 Flash',
|
|
112
|
+
description: 'Newest Flash model. Fast and low-cost, with reasoning and multimodal input.',
|
|
113
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
114
|
+
contextWindowSize: 1048576,
|
|
115
|
+
outputTokenLimit: 65536,
|
|
116
|
+
pricePer1MTokensInput: 1.50,
|
|
117
|
+
pricePer1MTokensOutput: 7.50,
|
|
118
|
+
supportsStreaming: true,
|
|
119
|
+
status: 'active',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
modelId: 'gemini-3.7-flash',
|
|
123
|
+
providerId: 'gemini',
|
|
124
|
+
displayName: 'Gemini 3.7 Flash',
|
|
125
|
+
description: 'Flash model with reasoning and multimodal input.',
|
|
126
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
127
|
+
contextWindowSize: 1048576,
|
|
128
|
+
outputTokenLimit: 65536,
|
|
129
|
+
pricePer1MTokensInput: 1.50,
|
|
130
|
+
pricePer1MTokensOutput: 7.50,
|
|
131
|
+
supportsStreaming: true,
|
|
132
|
+
status: 'active',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
modelId: 'gemini-3.6-flash',
|
|
136
|
+
providerId: 'gemini',
|
|
137
|
+
displayName: 'Gemini 3.6 Flash',
|
|
138
|
+
description: 'Flash model with reasoning and multimodal input.',
|
|
139
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
140
|
+
contextWindowSize: 1048576,
|
|
141
|
+
outputTokenLimit: 65536,
|
|
142
|
+
pricePer1MTokensInput: 1.50,
|
|
143
|
+
pricePer1MTokensOutput: 7.50,
|
|
144
|
+
supportsStreaming: true,
|
|
145
|
+
status: 'active',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
modelId: 'gemini-3.5-flash',
|
|
149
|
+
providerId: 'gemini',
|
|
150
|
+
displayName: 'Gemini 3.5 Flash',
|
|
151
|
+
description: 'Flash model with reasoning and multimodal input, at standard pricing.',
|
|
152
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
153
|
+
contextWindowSize: 1048576,
|
|
154
|
+
outputTokenLimit: 65536,
|
|
155
|
+
pricePer1MTokensInput: 1.50,
|
|
156
|
+
pricePer1MTokensOutput: 9.00,
|
|
157
|
+
supportsStreaming: true,
|
|
158
|
+
status: 'active',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
modelId: 'gemini-3-flash-preview',
|
|
162
|
+
providerId: 'gemini',
|
|
163
|
+
displayName: 'Gemini 3 Flash Preview',
|
|
164
|
+
description: 'First Gemini 3 Flash preview.',
|
|
165
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
166
|
+
contextWindowSize: 1048576,
|
|
167
|
+
outputTokenLimit: 65536,
|
|
168
|
+
pricePer1MTokensInput: 0.50,
|
|
169
|
+
pricePer1MTokensOutput: 3.00,
|
|
170
|
+
supportsStreaming: true,
|
|
171
|
+
status: 'active',
|
|
172
|
+
},
|
|
173
|
+
// --- Flash-Lite ---
|
|
174
|
+
{
|
|
175
|
+
modelId: 'gemini-3.5-flash-lite',
|
|
176
|
+
providerId: 'gemini',
|
|
177
|
+
displayName: 'Gemini 3.5 Flash-Lite',
|
|
178
|
+
description: 'Lowest-cost Gemini 3.5 tier, for high-volume and latency-sensitive work.',
|
|
179
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
180
|
+
contextWindowSize: 1048576,
|
|
181
|
+
outputTokenLimit: 65536,
|
|
182
|
+
pricePer1MTokensInput: 0.30,
|
|
183
|
+
pricePer1MTokensOutput: 2.50,
|
|
184
|
+
supportsStreaming: true,
|
|
185
|
+
status: 'active',
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
modelId: 'gemini-3.1-flash-lite',
|
|
189
|
+
providerId: 'gemini',
|
|
190
|
+
displayName: 'Gemini 3.1 Flash-Lite',
|
|
191
|
+
description: 'Lowest-cost Gemini 3.1 tier. Google lists a shutdown date of 2027-05-07.',
|
|
192
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
193
|
+
contextWindowSize: 1048576,
|
|
194
|
+
outputTokenLimit: 65536,
|
|
195
|
+
pricePer1MTokensInput: 0.25,
|
|
196
|
+
pricePer1MTokensOutput: 1.50,
|
|
197
|
+
supportsStreaming: true,
|
|
198
|
+
status: 'active',
|
|
199
|
+
},
|
|
200
|
+
// --- Floating aliases. Google can repoint these without changing the id, so
|
|
201
|
+
// a price here can go stale unnoticed. They are listed because an unlisted id
|
|
202
|
+
// resolves to undefined and meters at zero. generateContent reported these
|
|
203
|
+
// targets on 2026-09-29 (response modelVersion): pro-latest to 3.1 Pro,
|
|
204
|
+
// flash-latest to 3.8 Flash, flash-lite-latest to 3.5 Flash-Lite. ---
|
|
71
205
|
{
|
|
72
206
|
modelId: 'gemini-pro-latest',
|
|
73
207
|
providerId: 'gemini',
|
|
@@ -83,23 +217,37 @@ const GEMINI_MODELS = [
|
|
|
83
217
|
status: 'active',
|
|
84
218
|
},
|
|
85
219
|
{
|
|
86
|
-
modelId: 'gemini-
|
|
220
|
+
modelId: 'gemini-flash-latest',
|
|
87
221
|
providerId: 'gemini',
|
|
88
|
-
displayName: 'Gemini
|
|
89
|
-
description: '
|
|
222
|
+
displayName: 'Gemini Flash (latest)',
|
|
223
|
+
description: 'Floating alias for the current Gemini Flash tier.',
|
|
90
224
|
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
91
225
|
contextWindowSize: 1048576,
|
|
92
226
|
outputTokenLimit: 65536,
|
|
93
|
-
pricePer1MTokensInput:
|
|
94
|
-
pricePer1MTokensOutput:
|
|
227
|
+
pricePer1MTokensInput: 1.50,
|
|
228
|
+
pricePer1MTokensOutput: 7.50,
|
|
95
229
|
supportsStreaming: true,
|
|
96
230
|
status: 'active',
|
|
97
231
|
},
|
|
232
|
+
{
|
|
233
|
+
modelId: 'gemini-flash-lite-latest',
|
|
234
|
+
providerId: 'gemini',
|
|
235
|
+
displayName: 'Gemini Flash-Lite (latest)',
|
|
236
|
+
description: 'Floating alias for the current Gemini Flash-Lite tier.',
|
|
237
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
238
|
+
contextWindowSize: 1048576,
|
|
239
|
+
outputTokenLimit: 65536,
|
|
240
|
+
pricePer1MTokensInput: 0.30,
|
|
241
|
+
pricePer1MTokensOutput: 2.50,
|
|
242
|
+
supportsStreaming: true,
|
|
243
|
+
status: 'active',
|
|
244
|
+
},
|
|
245
|
+
// --- Gemini 2.5 ---
|
|
98
246
|
{
|
|
99
247
|
modelId: 'gemini-2.5-pro',
|
|
100
248
|
providerId: 'gemini',
|
|
101
249
|
displayName: 'Gemini 2.5 Pro',
|
|
102
|
-
description: '
|
|
250
|
+
description: 'Previous-generation Pro model.',
|
|
103
251
|
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
104
252
|
contextWindowSize: 1048576,
|
|
105
253
|
outputTokenLimit: 65536,
|
|
@@ -108,31 +256,93 @@ const GEMINI_MODELS = [
|
|
|
108
256
|
supportsStreaming: true,
|
|
109
257
|
status: 'active',
|
|
110
258
|
},
|
|
259
|
+
{
|
|
260
|
+
modelId: 'gemini-2.5-flash',
|
|
261
|
+
providerId: 'gemini',
|
|
262
|
+
displayName: 'Gemini 2.5 Flash',
|
|
263
|
+
description: 'Previous-generation Flash model with reasoning and multimodal input.',
|
|
264
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
265
|
+
contextWindowSize: 1048576,
|
|
266
|
+
outputTokenLimit: 65536,
|
|
267
|
+
pricePer1MTokensInput: 0.30,
|
|
268
|
+
pricePer1MTokensOutput: 2.50,
|
|
269
|
+
supportsStreaming: true,
|
|
270
|
+
status: 'active',
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
modelId: 'gemini-2.5-flash-lite',
|
|
274
|
+
providerId: 'gemini',
|
|
275
|
+
displayName: 'Gemini 2.5 Flash-Lite',
|
|
276
|
+
description: 'Cheapest Gemini model served.',
|
|
277
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
278
|
+
contextWindowSize: 1048576,
|
|
279
|
+
outputTokenLimit: 65536,
|
|
280
|
+
pricePer1MTokensInput: 0.10,
|
|
281
|
+
pricePer1MTokensOutput: 0.40,
|
|
282
|
+
supportsStreaming: true,
|
|
283
|
+
status: 'active',
|
|
284
|
+
},
|
|
285
|
+
// --- Embeddings. Both ids accept embedContent and batchEmbedContents and
|
|
286
|
+
// return 3072-dimension vectors (probed 2026-09-29). Google's pricing page
|
|
287
|
+
// lists gemini-embedding-2 at $0.20 per 1M text tokens and carries no price
|
|
288
|
+
// for gemini-embedding-001, so that row leaves its price unset: an unset
|
|
289
|
+
// price reads as unknown to a caller, where 0 would read as free. ---
|
|
290
|
+
{
|
|
291
|
+
modelId: 'gemini-embedding-2',
|
|
292
|
+
providerId: 'gemini',
|
|
293
|
+
displayName: 'Gemini Embedding 2',
|
|
294
|
+
description: 'Multimodal embedding model mapping text, images, video, audio and PDFs into one vector space.',
|
|
295
|
+
capabilities: ['embeddings'],
|
|
296
|
+
contextWindowSize: 8192,
|
|
297
|
+
inputTokenLimit: 8192,
|
|
298
|
+
embeddingDimension: 3072,
|
|
299
|
+
pricePer1MTokensInput: 0.20,
|
|
300
|
+
pricePer1MTokensTotal: 0.20,
|
|
301
|
+
supportsStreaming: false,
|
|
302
|
+
status: 'active',
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
modelId: 'gemini-embedding-001',
|
|
306
|
+
providerId: 'gemini',
|
|
307
|
+
displayName: 'Gemini Embedding 001',
|
|
308
|
+
description: 'Text embedding model. An index built with it needs it for queries too, since vectors from different embedding models are not comparable.',
|
|
309
|
+
capabilities: ['embeddings'],
|
|
310
|
+
contextWindowSize: 2048,
|
|
311
|
+
inputTokenLimit: 2048,
|
|
312
|
+
embeddingDimension: 3072,
|
|
313
|
+
supportsStreaming: false,
|
|
314
|
+
status: 'active',
|
|
315
|
+
},
|
|
316
|
+
// --- Retired by Google. generateContent returns HTTP 404 for both ids
|
|
317
|
+
// ("This model models/gemini-2.0-flash is no longer available" for 2.0
|
|
318
|
+
// Flash), probed 2026-09-29 with a gemini-2.5-flash-lite control returning
|
|
319
|
+
// 200. The rows stay as `deprecated` so getModelInfo still answers for a
|
|
320
|
+
// caller holding either id. ---
|
|
111
321
|
{
|
|
112
322
|
modelId: 'gemini-2.0-flash',
|
|
113
323
|
providerId: 'gemini',
|
|
114
|
-
displayName: 'Gemini 2.0 Flash',
|
|
115
|
-
description: '
|
|
324
|
+
displayName: 'Gemini 2.0 Flash (retired)',
|
|
325
|
+
description: 'Retired by Google and no longer served. Google names gemini-3.8-flash as its replacement.',
|
|
116
326
|
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
117
327
|
contextWindowSize: 1048576,
|
|
118
328
|
outputTokenLimit: 8192,
|
|
119
329
|
pricePer1MTokensInput: 0.10,
|
|
120
330
|
pricePer1MTokensOutput: 0.40,
|
|
121
331
|
supportsStreaming: true,
|
|
122
|
-
status: '
|
|
332
|
+
status: 'deprecated',
|
|
123
333
|
},
|
|
124
334
|
{
|
|
125
335
|
modelId: 'gemini-1.5-pro',
|
|
126
336
|
providerId: 'gemini',
|
|
127
|
-
displayName: 'Gemini 1.5 Pro',
|
|
128
|
-
description: '
|
|
337
|
+
displayName: 'Gemini 1.5 Pro (retired)',
|
|
338
|
+
description: 'Retired by Google and no longer served. Use gemini-3.1-pro-preview.',
|
|
129
339
|
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
130
340
|
contextWindowSize: 2097152,
|
|
131
341
|
outputTokenLimit: 8192,
|
|
132
342
|
pricePer1MTokensInput: 1.25,
|
|
133
343
|
pricePer1MTokensOutput: 5.00,
|
|
134
344
|
supportsStreaming: true,
|
|
135
|
-
status: '
|
|
345
|
+
status: 'deprecated',
|
|
136
346
|
},
|
|
137
347
|
];
|
|
138
348
|
// ---------------------------------------------------------------------------
|
|
@@ -355,6 +565,9 @@ export class GeminiProvider {
|
|
|
355
565
|
}
|
|
356
566
|
const parts = candidate.content?.parts ?? [];
|
|
357
567
|
for (const part of parts) {
|
|
568
|
+
// A thought summary is the model's reasoning; it stays out of the answer.
|
|
569
|
+
if (part.thought)
|
|
570
|
+
continue;
|
|
358
571
|
if (part.text !== undefined) {
|
|
359
572
|
// Text delta
|
|
360
573
|
accumulatedContent += part.text;
|
|
@@ -378,6 +591,7 @@ export class GeminiProvider {
|
|
|
378
591
|
toolCallAccum.set(idx, {
|
|
379
592
|
name: part.functionCall.name,
|
|
380
593
|
args: part.functionCall.args,
|
|
594
|
+
thoughtSignature: part.thoughtSignature,
|
|
381
595
|
});
|
|
382
596
|
yield {
|
|
383
597
|
id: responseId,
|
|
@@ -398,6 +612,7 @@ export class GeminiProvider {
|
|
|
398
612
|
// Gemini delivers complete args, so emit them as a single delta
|
|
399
613
|
arguments_delta: JSON.stringify(part.functionCall.args),
|
|
400
614
|
},
|
|
615
|
+
...(part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {}),
|
|
401
616
|
}],
|
|
402
617
|
};
|
|
403
618
|
}
|
|
@@ -462,10 +677,20 @@ export class GeminiProvider {
|
|
|
462
677
|
/**
|
|
463
678
|
* Generates embeddings using Gemini's embedding models.
|
|
464
679
|
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
680
|
+
* Sends the texts through `models/{model}:batchEmbedContents` in chunks of
|
|
681
|
+
* at most {@link GEMINI_EMBED_BATCH_LIMIT}, one request per chunk, and
|
|
682
|
+
* returns the vectors in input order. gemini-embedding-001 and
|
|
683
|
+
* gemini-embedding-2 both accept batchEmbedContents (probed 2026-09-29),
|
|
684
|
+
* although neither lists it in `supportedGenerationMethods`.
|
|
685
|
+
* `options.dimensions` is sent as `outputDimensionality`, which both models
|
|
686
|
+
* honor (768 and 1536 probed 2026-09-29); without it they return 3072.
|
|
687
|
+
* Usage sums each call's `usageMetadata.promptTokenCount`, which
|
|
688
|
+
* gemini-embedding-2 reports and gemini-embedding-001 omits, so the older
|
|
689
|
+
* model's calls count zero tokens (probed 2026-09-30). When a later batch
|
|
690
|
+
* fails, the thrown error carries the earlier batches' usage as
|
|
691
|
+
* `partialUsage`, since those calls were billed.
|
|
467
692
|
*
|
|
468
|
-
* @param {string} modelId - Embedding model (e.g., "
|
|
693
|
+
* @param {string} modelId - Embedding model (e.g., "gemini-embedding-001").
|
|
469
694
|
* @param {string[]} texts - Input texts to embed.
|
|
470
695
|
* @param {ProviderEmbeddingOptions} [options] - Optional embedding parameters.
|
|
471
696
|
* @returns {Promise<ProviderEmbeddingResponse>} Embedding vectors.
|
|
@@ -473,25 +698,46 @@ export class GeminiProvider {
|
|
|
473
698
|
*/
|
|
474
699
|
async generateEmbeddings(modelId, texts, options) {
|
|
475
700
|
this.ensureInitialized();
|
|
476
|
-
// Gemini's batch embedding endpoint
|
|
477
701
|
const endpoint = `/models/${modelId}:batchEmbedContents`;
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
702
|
+
const pricePer1M = GEMINI_MODELS.find(m => m.modelId === modelId)?.pricePer1MTokensInput;
|
|
703
|
+
const usageFor = (tokens) => ({
|
|
704
|
+
prompt_tokens: tokens,
|
|
705
|
+
total_tokens: tokens,
|
|
706
|
+
...(pricePer1M !== undefined && tokens > 0 ? { costUSD: (tokens / 1000000) * pricePer1M } : {}),
|
|
707
|
+
});
|
|
708
|
+
const vectors = [];
|
|
709
|
+
let promptTokens = 0;
|
|
710
|
+
for (let start = 0; start < texts.length; start += GEMINI_EMBED_BATCH_LIMIT) {
|
|
711
|
+
const requests = texts.slice(start, start + GEMINI_EMBED_BATCH_LIMIT).map(text => ({
|
|
712
|
+
model: `models/${modelId}`,
|
|
713
|
+
content: { parts: [{ text }] },
|
|
714
|
+
...(options?.dimensions ? { outputDimensionality: options.dimensions } : {}),
|
|
715
|
+
}));
|
|
716
|
+
let apiResponse;
|
|
717
|
+
try {
|
|
718
|
+
apiResponse = await this.makeApiRequest(endpoint, { requests });
|
|
719
|
+
}
|
|
720
|
+
catch (error) {
|
|
721
|
+
// The batches before this one were billed; keep their usage on the
|
|
722
|
+
// error so the caller can still record it.
|
|
723
|
+
if (start > 0 && error instanceof GeminiProviderError) {
|
|
724
|
+
error.partialUsage = usageFor(promptTokens);
|
|
725
|
+
}
|
|
726
|
+
throw error;
|
|
727
|
+
}
|
|
728
|
+
for (const emb of apiResponse.embeddings)
|
|
729
|
+
vectors.push(emb.values);
|
|
730
|
+
promptTokens += apiResponse.usageMetadata?.promptTokenCount ?? 0;
|
|
731
|
+
}
|
|
483
732
|
return {
|
|
484
733
|
object: 'list',
|
|
485
|
-
data:
|
|
734
|
+
data: vectors.map((embedding, index) => ({
|
|
486
735
|
object: 'embedding',
|
|
487
|
-
embedding
|
|
736
|
+
embedding,
|
|
488
737
|
index,
|
|
489
738
|
})),
|
|
490
739
|
model: modelId,
|
|
491
|
-
usage:
|
|
492
|
-
prompt_tokens: 0, // Gemini does not report embedding token counts
|
|
493
|
-
total_tokens: 0,
|
|
494
|
-
},
|
|
740
|
+
usage: usageFor(promptTokens),
|
|
495
741
|
};
|
|
496
742
|
}
|
|
497
743
|
// -------------------------------------------------------------------------
|
|
@@ -656,7 +902,9 @@ export class GeminiProvider {
|
|
|
656
902
|
// otherwise the provider-neutral `effort` becomes the model's thinking
|
|
657
903
|
// level when the model is known to take that level. Thinking shares the
|
|
658
904
|
// maxOutputTokens cap: room for it on a rescue hop comes from
|
|
659
|
-
// FallbackProviderEntry.maxTokensHeadroom, not from this provider.
|
|
905
|
+
// FallbackProviderEntry.maxTokensHeadroom, not from this provider. The
|
|
906
|
+
// shared `thinking` option is Anthropic's on-switch and is not translated:
|
|
907
|
+
// on a fallback hop it would turn "thinking on" into a thinking-token cap.
|
|
660
908
|
const customThinking = options.customModelParams?.thinkingConfig;
|
|
661
909
|
if (customThinking && typeof customThinking === 'object') {
|
|
662
910
|
generationConfig.thinkingConfig = customThinking;
|
|
@@ -696,12 +944,19 @@ export class GeminiProvider {
|
|
|
696
944
|
* - `assistant` -> `model` (Gemini uses "model" instead of "assistant")
|
|
697
945
|
* - `tool` -> `user` with `functionResponse` parts
|
|
698
946
|
*
|
|
947
|
+
* Consecutive tool results share one `user` turn, because Gemini requires
|
|
948
|
+
* the responses to a turn of parallel function calls in a single content
|
|
949
|
+
* (one functionResponse part per call). A tool result without `name`, as
|
|
950
|
+
* `generateText` records them, takes the function name of the call with the
|
|
951
|
+
* same id.
|
|
952
|
+
*
|
|
699
953
|
* @param {ChatMessage[]} messages - IProvider-format messages.
|
|
700
954
|
* @returns {GeminiContent[]} Gemini-format content array.
|
|
701
955
|
* @private
|
|
702
956
|
*/
|
|
703
957
|
convertMessages(messages) {
|
|
704
958
|
const contents = [];
|
|
959
|
+
const toolNameByCallId = new Map();
|
|
705
960
|
for (const msg of messages) {
|
|
706
961
|
if (msg.role === 'assistant') {
|
|
707
962
|
// --- Assistant messages map to "model" role ---
|
|
@@ -710,9 +965,16 @@ export class GeminiProvider {
|
|
|
710
965
|
if (typeof msg.content === 'string' && msg.content) {
|
|
711
966
|
parts.push({ text: msg.content });
|
|
712
967
|
}
|
|
713
|
-
// Convert tool_calls to functionCall parts
|
|
968
|
+
// Convert tool_calls to functionCall parts. Each call's thought
|
|
969
|
+
// signature is replayed verbatim. A turn with no signature at all (a
|
|
970
|
+
// call made by another provider, or history that dropped it) gets the
|
|
971
|
+
// documented placeholder on its first call, which is where Gemini puts
|
|
972
|
+
// its own; without one Gemini 3 rejects the request with HTTP 400.
|
|
714
973
|
if (msg.tool_calls?.length) {
|
|
715
|
-
|
|
974
|
+
const turnHasSignature = msg.tool_calls.some(tc => tc.thoughtSignature);
|
|
975
|
+
msg.tool_calls.forEach((tc, i) => {
|
|
976
|
+
if (tc.id)
|
|
977
|
+
toolNameByCallId.set(tc.id, tc.function.name);
|
|
716
978
|
let parsedArgs;
|
|
717
979
|
try {
|
|
718
980
|
parsedArgs = typeof tc.function.arguments === 'string'
|
|
@@ -722,10 +984,17 @@ export class GeminiProvider {
|
|
|
722
984
|
catch {
|
|
723
985
|
parsedArgs = {};
|
|
724
986
|
}
|
|
725
|
-
|
|
987
|
+
const part = {
|
|
726
988
|
functionCall: { name: tc.function.name, args: parsedArgs },
|
|
727
|
-
}
|
|
728
|
-
|
|
989
|
+
};
|
|
990
|
+
if (tc.thoughtSignature) {
|
|
991
|
+
part.thoughtSignature = tc.thoughtSignature;
|
|
992
|
+
}
|
|
993
|
+
else if (!turnHasSignature && i === 0) {
|
|
994
|
+
part.thoughtSignature = GEMINI_SKIP_THOUGHT_SIGNATURE;
|
|
995
|
+
}
|
|
996
|
+
parts.push(part);
|
|
997
|
+
});
|
|
729
998
|
}
|
|
730
999
|
// Ensure at least one part — Gemini requires non-empty parts
|
|
731
1000
|
if (parts.length === 0) {
|
|
@@ -737,25 +1006,42 @@ export class GeminiProvider {
|
|
|
737
1006
|
// --- Tool result messages become user-role functionResponse ---
|
|
738
1007
|
// Gemini expects tool results as functionResponse parts in a user turn.
|
|
739
1008
|
let responseData;
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
1009
|
+
if (typeof msg.content === 'string') {
|
|
1010
|
+
let parsed;
|
|
1011
|
+
try {
|
|
1012
|
+
parsed = JSON.parse(msg.content);
|
|
1013
|
+
}
|
|
1014
|
+
catch {
|
|
1015
|
+
// If the tool result isn't valid JSON, wrap it
|
|
1016
|
+
parsed = msg.content;
|
|
1017
|
+
}
|
|
1018
|
+
// functionResponse.response is a JSON object (a protobuf Struct);
|
|
1019
|
+
// Gemini rejects a string, number, array or null there with HTTP
|
|
1020
|
+
// 400, so such a result is wrapped.
|
|
1021
|
+
responseData = parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
1022
|
+
? parsed
|
|
1023
|
+
: { result: parsed };
|
|
745
1024
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
1025
|
+
else {
|
|
1026
|
+
responseData = { result: JSON.stringify(msg.content ?? '') };
|
|
1027
|
+
}
|
|
1028
|
+
const responsePart = {
|
|
1029
|
+
functionResponse: {
|
|
1030
|
+
name: msg.name
|
|
1031
|
+
|| (msg.tool_call_id ? toolNameByCallId.get(msg.tool_call_id) : undefined)
|
|
1032
|
+
|| 'unknown',
|
|
1033
|
+
response: responseData,
|
|
1034
|
+
},
|
|
1035
|
+
};
|
|
1036
|
+
const previous = contents[contents.length - 1];
|
|
1037
|
+
if (previous?.role === 'user'
|
|
1038
|
+
&& previous.parts.length > 0
|
|
1039
|
+
&& previous.parts.every(part => part.functionResponse)) {
|
|
1040
|
+
previous.parts.push(responsePart);
|
|
1041
|
+
}
|
|
1042
|
+
else {
|
|
1043
|
+
contents.push({ role: 'user', parts: [responsePart] });
|
|
749
1044
|
}
|
|
750
|
-
contents.push({
|
|
751
|
-
role: 'user',
|
|
752
|
-
parts: [{
|
|
753
|
-
functionResponse: {
|
|
754
|
-
name: msg.name || 'unknown',
|
|
755
|
-
response: responseData,
|
|
756
|
-
},
|
|
757
|
-
}],
|
|
758
|
-
});
|
|
759
1045
|
}
|
|
760
1046
|
else {
|
|
761
1047
|
// --- User messages ---
|
|
@@ -830,8 +1116,10 @@ export class GeminiProvider {
|
|
|
830
1116
|
const candidate = apiResponse.candidates?.[0];
|
|
831
1117
|
const parts = candidate?.content?.parts ?? [];
|
|
832
1118
|
// Collect text from all text parts
|
|
1119
|
+
// Thought-summary parts (present only with includeThoughts) are the
|
|
1120
|
+
// model's reasoning and stay out of the answer.
|
|
833
1121
|
const textParts = parts
|
|
834
|
-
.filter(p => p.text !== undefined)
|
|
1122
|
+
.filter(p => p.text !== undefined && !p.thought)
|
|
835
1123
|
.map(p => p.text);
|
|
836
1124
|
const fullText = textParts.join('');
|
|
837
1125
|
// Collect function calls and convert to OpenAI-style tool_calls
|
|
@@ -844,6 +1132,7 @@ export class GeminiProvider {
|
|
|
844
1132
|
name: p.functionCall.name,
|
|
845
1133
|
arguments: JSON.stringify(p.functionCall.args ?? {}),
|
|
846
1134
|
},
|
|
1135
|
+
...(p.thoughtSignature ? { thoughtSignature: p.thoughtSignature } : {}),
|
|
847
1136
|
}));
|
|
848
1137
|
const hasToolCalls = toolCalls.length > 0;
|
|
849
1138
|
const finishReason = this.mapFinishReason(candidate?.finishReason ?? null);
|
|
@@ -948,6 +1237,7 @@ export class GeminiProvider {
|
|
|
948
1237
|
name: tc.name,
|
|
949
1238
|
arguments: JSON.stringify(tc.args ?? {}),
|
|
950
1239
|
},
|
|
1240
|
+
...(tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}),
|
|
951
1241
|
}));
|
|
952
1242
|
}
|
|
953
1243
|
/**
|
|
@@ -1010,7 +1300,8 @@ export class GeminiProvider {
|
|
|
1010
1300
|
*/
|
|
1011
1301
|
async makeApiRequest(endpoint, body, requestTimeoutOverride) {
|
|
1012
1302
|
// API key is passed as query parameter — Gemini's auth convention
|
|
1013
|
-
const
|
|
1303
|
+
const apiKey = this.keyPool?.hasKeys ? this.keyPool.next() : this.config.apiKey;
|
|
1304
|
+
const url = `${this.config.baseURL}${endpoint}?key=${apiKey}`;
|
|
1014
1305
|
const headers = {
|
|
1015
1306
|
'Content-Type': 'application/json',
|
|
1016
1307
|
'User-Agent': 'AgentOS/1.0 (GeminiProvider)',
|
|
@@ -1070,7 +1361,9 @@ export class GeminiProvider {
|
|
|
1070
1361
|
lastError = new GeminiProviderError(`Request timed out after ${effectiveTimeout}ms.`, 'REQUEST_TIMEOUT');
|
|
1071
1362
|
}
|
|
1072
1363
|
else {
|
|
1073
|
-
lastError = new GeminiProviderError(error instanceof Error
|
|
1364
|
+
lastError = new GeminiProviderError(error instanceof Error
|
|
1365
|
+
? redactUrlSecrets(error.message, this.config.baseURL, [apiKey])
|
|
1366
|
+
: 'Network or unknown error', 'NETWORK_ERROR');
|
|
1074
1367
|
}
|
|
1075
1368
|
if (attempt === this.config.maxRetries - 1)
|
|
1076
1369
|
break;
|
|
@@ -1095,7 +1388,8 @@ export class GeminiProvider {
|
|
|
1095
1388
|
*/
|
|
1096
1389
|
async makeStreamRequest(endpoint, body, requestTimeoutOverride) {
|
|
1097
1390
|
// Both alt=sse and key= are query params
|
|
1098
|
-
const
|
|
1391
|
+
const apiKey = this.keyPool?.hasKeys ? this.keyPool.next() : this.config.apiKey;
|
|
1392
|
+
const url = `${this.config.baseURL}${endpoint}?alt=sse&key=${apiKey}`;
|
|
1099
1393
|
const headers = {
|
|
1100
1394
|
'Content-Type': 'application/json',
|
|
1101
1395
|
'User-Agent': 'AgentOS/1.0 (GeminiProvider)',
|
|
@@ -1128,7 +1422,9 @@ export class GeminiProvider {
|
|
|
1128
1422
|
clearTimeout(timeoutId);
|
|
1129
1423
|
if (error instanceof GeminiProviderError)
|
|
1130
1424
|
throw error;
|
|
1131
|
-
throw new GeminiProviderError(error instanceof Error
|
|
1425
|
+
throw new GeminiProviderError(error instanceof Error
|
|
1426
|
+
? redactUrlSecrets(error.message, this.config.baseURL, [apiKey])
|
|
1427
|
+
: 'Failed to connect to Gemini stream.', 'STREAM_CONNECTION_FAILED');
|
|
1132
1428
|
}
|
|
1133
1429
|
}
|
|
1134
1430
|
// -------------------------------------------------------------------------
|