@tanstack/ai-gemini 0.10.9 → 0.11.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 CHANGED
@@ -1,93 +1,253 @@
1
1
  <div align="center">
2
- <img src="./media/header_ai.png" >
2
+ <img src="https://raw.githubusercontent.com/TanStack/ai/main/media/header_ai.png" alt="TanStack AI" />
3
3
  </div>
4
4
 
5
5
  <br />
6
6
 
7
7
  <div align="center">
8
- <a href="https://npmjs.com/package/@tanstack/ai" target="\_parent">
9
- <img alt="" src="https://img.shields.io/npm/dm/@tanstack/ai.svg" />
10
- </a>
11
- <a href="https://github.com/TanStack/ai" target="\_parent">
12
- <img alt="" src="https://img.shields.io/github/stars/TanStack/ai.svg?style=social&label=Star" alt="GitHub stars" />
13
- </a>
14
- <a href="https://bundlephobia.com/result?p=@tanstack/ai@latest" target="\_parent">
15
- <img alt="" src="https://badgen.net/bundlephobia/minzip/@tanstack/ai@latest" />
16
- </a>
8
+ <a href="https://npmjs.com/package/@tanstack/ai" target="_parent">
9
+ <img alt="NPM downloads" src="https://img.shields.io/npm/dm/@tanstack/ai.svg" />
10
+ </a>
11
+ <a href="https://github.com/TanStack/ai" target="_parent">
12
+ <img alt="GitHub stars" src="https://img.shields.io/github/stars/TanStack/ai.svg?style=social&label=Star" />
13
+ </a>
14
+ <a href="https://github.com/TanStack/ai/releases" target="_parent">
15
+ <img alt="Release" src="https://img.shields.io/github/v/release/tanstack/ai" />
16
+ </a>
17
+ <a href="https://bundlephobia.com/result?p=@tanstack/ai@latest" target="_parent">
18
+ <img alt="Bundle size" src="https://badgen.net/bundlephobia/minzip/@tanstack/ai@latest" />
19
+ </a>
20
+ <a href="https://twitter.com/tan_stack">
21
+ <img alt="Follow @TanStack" src="https://img.shields.io/twitter/follow/tan_stack.svg?style=social" />
22
+ </a>
17
23
  </div>
18
24
 
19
- <div align="center">
20
- <a href="#badge">
21
- <img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
22
- </a>
23
- <a href="#badge">
24
- <img src="https://img.shields.io/github/v/release/tanstack/ai" alt="Release"/>
25
- </a>
26
- <a href="https://twitter.com/tan_stack">
27
- <img src="https://img.shields.io/twitter/follow/tan_stack.svg?style=social" alt="Follow @TanStack"/>
28
- </a>
29
- </div>
25
+ # TanStack AI
30
26
 
31
- <div align="center">
32
-
33
- ### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
34
- </div>
27
+ Type-safe, provider-agnostic TypeScript SDK for building streaming chat,
28
+ tool-calling agents, structured outputs, realtime voice, media generation, and
29
+ framework-native AI apps.
35
30
 
36
- # TanStack AI
31
+ TanStack AI is built from composable activities and provider adapters. Use one
32
+ provider or switch between many. Import only chat, or add image, audio, video,
33
+ speech, transcription, summarization, realtime, Code Mode, devtools, and
34
+ framework bindings as your app needs them.
35
+
36
+ ## <a href="https://tanstack.com/ai">Read the docs -></a>
37
+
38
+ ## Start Here
39
+
40
+ - [Overview](https://tanstack.com/ai/latest/docs/getting-started/overview) -
41
+ what TanStack AI is and how the packages fit together.
42
+ - [Quick Start: React](https://tanstack.com/ai/latest/docs/getting-started/quick-start) -
43
+ add streaming chat to a React app.
44
+ - [Quick Start: Vue](https://tanstack.com/ai/latest/docs/getting-started/quick-start-vue) -
45
+ build with Vue composables.
46
+ - [Quick Start: Svelte](https://tanstack.com/ai/latest/docs/getting-started/quick-start-svelte) -
47
+ build with Svelte 5 runes.
48
+ - [Quick Start: Server Only](https://tanstack.com/ai/latest/docs/getting-started/quick-start-server) -
49
+ use TanStack AI from a server endpoint, script, or backend service.
50
+ - [TanStack AI vs Vercel AI SDK](https://tanstack.com/ai/latest/docs/comparison/vercel-ai-sdk) -
51
+ compare architecture, feature coverage, and tradeoffs.
52
+
53
+ ## What You Can Build
54
+
55
+ - Streaming chat experiences with typed messages, tool calls, reasoning parts,
56
+ and configurable connection adapters.
57
+ - Type-safe tools that can run on the server or client from one shared
58
+ `toolDefinition()` contract.
59
+ - Structured output flows backed by JSON Schema, Zod, ArkType, Valibot, or
60
+ plain JSON Schema.
61
+ - Multimodal prompts and responses that include text, images, audio, video, and
62
+ documents.
63
+ - Image, audio, video, speech, transcription, and summarization workflows using
64
+ a shared generation client pattern.
65
+ - Realtime voice chat with provider adapters for realtime sessions and token
66
+ minting.
67
+ - Code Mode agents that let an LLM write and execute TypeScript in an isolated
68
+ sandbox to orchestrate tools with loops, branches, and parallel calls.
69
+ - Devtools and observability pipelines for inspecting messages, tool calls,
70
+ stream chunks, errors, usage, and OpenTelemetry traces.
71
+ - Framework-native clients for React, Solid, Vue, Svelte, and Preact, plus a
72
+ headless client for custom runtimes.
73
+
74
+ ## Install
37
75
 
38
- A powerful, type-safe AI SDK for building AI-powered applications.
76
+ Install the core package and the provider/framework packages your app uses:
39
77
 
40
- - Provider-agnostic adapters (OpenAI, Anthropic, Gemini, Ollama, etc.)
41
- - **Tree-shakeable adapters** - Import only what you need for smaller bundles
42
- - **Multimodal content support** - Send images, audio, video, and documents
43
- - **Image generation** - Generate images with OpenAI DALL-E/GPT-Image and Gemini Imagen
44
- - Chat completion, streaming, and agent loop strategies
45
- - Headless chat state management with adapters (SSE, HTTP stream, custom)
46
- - Isomorphic type-safe tools with server/client execution
47
- - **Enhanced integration with TanStack Start** - Share implementations between AI tools and server functions
48
- - **Observability events** - Structured, typed events for text, tools, image, speech, transcription, and video ([docs](./docs/guides/observability.md))
78
+ ```bash
79
+ pnpm add @tanstack/ai @tanstack/ai-openai
80
+ ```
81
+
82
+ For a React chat UI:
83
+
84
+ ```bash
85
+ pnpm add @tanstack/ai @tanstack/ai-client @tanstack/ai-react @tanstack/ai-openai
86
+ ```
49
87
 
50
- ### <a href="https://tanstack.com/ai">Read the docs →</a>
88
+ OpenRouter is also a good starting point if you want access to many providers
89
+ through one API key:
51
90
 
52
- ## Tree-Shakeable Adapters
91
+ ```bash
92
+ pnpm add @tanstack/ai @tanstack/ai-openrouter
93
+ ```
53
94
 
54
- Import only the functionality you need for smaller bundle sizes:
95
+ ## Streaming Chat
55
96
 
56
97
  ```typescript
57
- // Only chat functionality - no summarization code bundled
58
- import { openaiText } from '@tanstack/ai-openai/adapters'
59
- import { generate } from '@tanstack/ai'
98
+ import { chat, toServerSentEventsResponse } from '@tanstack/ai'
99
+ import { openaiText } from '@tanstack/ai-openai'
60
100
 
61
- const textAdapter = openaiText()
101
+ export async function POST(request: Request) {
102
+ const body = await request.json()
62
103
 
63
- const result = generate({
64
- adapter: textAdapter,
65
- model: 'gpt-4o',
66
- messages: [{ role: 'user', content: [{ type: 'text', content: 'Hello!' }] }],
67
- })
104
+ const stream = chat({
105
+ adapter: openaiText('gpt-5.2'),
106
+ messages: body.messages,
107
+ })
68
108
 
69
- for await (const chunk of result) {
70
- console.log(chunk)
109
+ return toServerSentEventsResponse(stream)
71
110
  }
72
111
  ```
73
112
 
74
- Available adapters: `openaiText`, `openaiEmbed`, `openaiSummarize`, `anthropicText`, `geminiText`, `ollamaText`, and more.
113
+ Learn more in the
114
+ [Chat & Streaming docs](https://tanstack.com/ai/latest/docs/chat/streaming) and
115
+ [Connection Adapters docs](https://tanstack.com/ai/latest/docs/chat/connection-adapters).
116
+
117
+ ## Type-Safe Tools
118
+
119
+ Define a tool once, then attach a server or client implementation with the same
120
+ input and output types:
121
+
122
+ ```typescript
123
+ import { toolDefinition } from '@tanstack/ai'
124
+ import { z } from 'zod'
125
+
126
+ const getProducts = toolDefinition({
127
+ name: 'getProducts',
128
+ description: 'Search the product catalog',
129
+ inputSchema: z.object({ query: z.string() }),
130
+ outputSchema: z.array(
131
+ z.object({
132
+ id: z.string(),
133
+ name: z.string(),
134
+ }),
135
+ ),
136
+ }).server(async ({ query }) => {
137
+ return db.products.search(query)
138
+ })
139
+ ```
140
+
141
+ Learn more in the
142
+ [Tools docs](https://tanstack.com/ai/latest/docs/tools/tools),
143
+ [Tool Approval Flow docs](https://tanstack.com/ai/latest/docs/tools/tool-approval),
144
+ and
145
+ [Lazy Tool Discovery docs](https://tanstack.com/ai/latest/docs/tools/lazy-tool-discovery).
146
+
147
+ ## Structured Outputs
148
+
149
+ Use `outputSchema` when you need typed objects instead of freeform text:
150
+
151
+ ```typescript
152
+ import { chat } from '@tanstack/ai'
153
+ import { openaiText } from '@tanstack/ai-openai'
154
+ import { z } from 'zod'
155
+
156
+ const Person = z.object({
157
+ name: z.string(),
158
+ age: z.number(),
159
+ })
160
+
161
+ const person = await chat({
162
+ adapter: openaiText('gpt-5.2'),
163
+ messages: [{ role: 'user', content: 'Ada Lovelace, 36' }],
164
+ outputSchema: Person,
165
+ })
166
+ ```
167
+
168
+ Learn more in the
169
+ [Structured Outputs docs](https://tanstack.com/ai/latest/docs/structured-outputs/overview).
170
+
171
+ ## Media, Realtime, and Code Mode
172
+
173
+ - [Generations](https://tanstack.com/ai/latest/docs/media/generations) - one
174
+ pattern for image generation, text-to-speech, transcription, summarization,
175
+ audio generation, and video generation.
176
+ - [Realtime Voice Chat](https://tanstack.com/ai/latest/docs/media/realtime-chat) -
177
+ build low-latency realtime voice experiences.
178
+ - [Code Mode](https://tanstack.com/ai/latest/docs/code-mode/code-mode) - let
179
+ models write and execute TypeScript inside a secure isolate.
180
+ - [Code Mode with Skills](https://tanstack.com/ai/latest/docs/code-mode/code-mode-with-skills) -
181
+ give Code Mode reusable runtime capabilities.
182
+
183
+ ## Providers
184
+
185
+ Official adapters include:
186
+
187
+ | Package | Use it for |
188
+ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
189
+ | [`@tanstack/ai-openrouter`](https://tanstack.com/ai/latest/docs/adapters/openrouter) | 300+ models through one OpenRouter API |
190
+ | [`@tanstack/ai-openai`](https://tanstack.com/ai/latest/docs/adapters/openai) | OpenAI chat, image, video, speech, transcription, realtime, and provider tools |
191
+ | [`@tanstack/ai-anthropic`](https://tanstack.com/ai/latest/docs/adapters/anthropic) | Anthropic Claude chat, thinking, tools, and structured outputs |
192
+ | [`@tanstack/ai-gemini`](https://tanstack.com/ai/latest/docs/adapters/gemini) | Google Gemini chat, image, speech, and audio generation |
193
+ | [`@tanstack/ai-ollama`](https://tanstack.com/ai/latest/docs/adapters/ollama) | Local Ollama models |
194
+ | [`@tanstack/ai-grok`](https://tanstack.com/ai/latest/docs/adapters/grok) | xAI Grok chat, images, and realtime |
195
+ | [`@tanstack/ai-groq`](https://tanstack.com/ai/latest/docs/adapters/groq) | Groq low-latency inference |
196
+ | [`@tanstack/ai-elevenlabs`](https://tanstack.com/ai/latest/docs/adapters/elevenlabs) | ElevenLabs realtime voice, speech, transcription, music, and sound effects |
197
+ | [`@tanstack/ai-fal`](https://tanstack.com/ai/latest/docs/adapters/fal) | fal.ai image, video, audio, speech, and transcription models |
198
+
199
+ The adapter system is tree-shakeable by activity. Import `openaiText` for chat,
200
+ `openaiImage` for images, `falVideo` for video, `geminiSpeech` for TTS, and so
201
+ on.
202
+
203
+ ## Framework Packages
204
+
205
+ | Package | What it provides |
206
+ | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
207
+ | [`@tanstack/ai-client`](https://tanstack.com/ai/latest/docs/api/ai-client) | Headless chat, realtime, and generation clients |
208
+ | [`@tanstack/ai-react`](https://tanstack.com/ai/latest/docs/api/ai-react) | React hooks including `useChat`, `useRealtimeChat`, and generation hooks |
209
+ | [`@tanstack/ai-solid`](https://tanstack.com/ai/latest/docs/api/ai-solid) | Solid hooks for chat and generations |
210
+ | [`@tanstack/ai-vue`](https://tanstack.com/ai/latest/docs/api/ai-vue) | Vue composables for chat and generations |
211
+ | [`@tanstack/ai-svelte`](https://tanstack.com/ai/latest/docs/api/ai-svelte) | Svelte 5 factories for chat and generations |
212
+ | [`@tanstack/ai-preact`](https://tanstack.com/ai/latest/docs/api/ai-preact) | Preact hooks for chat |
213
+ | `@tanstack/ai-react-ui`, `@tanstack/ai-solid-ui`, `@tanstack/ai-vue-ui` | Headless UI components for chat interfaces |
214
+
215
+ ## Advanced Docs
216
+
217
+ - [Middleware](https://tanstack.com/ai/latest/docs/advanced/middleware) - hook
218
+ into chat configuration, chunks, tool calls, usage, errors, and structured
219
+ outputs.
220
+ - [OpenTelemetry](https://tanstack.com/ai/latest/docs/advanced/otel) - emit
221
+ vendor-neutral GenAI traces and metrics.
222
+ - [Observability](https://tanstack.com/ai/latest/docs/advanced/observability) -
223
+ subscribe to typed TanStack AI events.
224
+ - [Per-Model Type Safety](https://tanstack.com/ai/latest/docs/advanced/per-model-type-safety) -
225
+ narrow model options and content modalities to the selected model.
226
+ - [Runtime Adapter Switching](https://tanstack.com/ai/latest/docs/advanced/runtime-adapter-switching) -
227
+ switch providers at runtime.
228
+ - [Tree-Shaking](https://tanstack.com/ai/latest/docs/advanced/tree-shaking) -
229
+ ship only the activities and adapters you use.
230
+ - [Agent Skills](https://tanstack.com/ai/latest/docs/getting-started/agent-skills) -
231
+ install TanStack AI skills into Claude Code, Cursor, GitHub Copilot, Codex,
232
+ and other coding agents with TanStack Intent.
75
233
 
76
234
  ## Get Involved
77
235
 
78
- - We welcome issues and pull requests!
79
- - Participate in [GitHub discussions](https://github.com/TanStack/ai/discussions)
80
- - Chat with the community on [Discord](https://discord.com/invite/WrRKjPJ)
81
- - See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions
236
+ - Read the [docs](https://tanstack.com/ai).
237
+ - Participate in [GitHub discussions](https://github.com/TanStack/ai/discussions).
238
+ - Chat with the community on [Discord](https://discord.com/invite/WrRKjPJ).
239
+ - See [CONTRIBUTING.md](https://github.com/TanStack/ai/blob/main/CONTRIBUTING.md)
240
+ for setup instructions.
241
+ - [Become a sponsor](https://github.com/sponsors/tannerlinsley/).
82
242
 
83
243
  ## Partners
84
244
 
85
245
  <table align="center">
86
246
  <tr>
87
247
  <td>
88
- <a href="https://www.coderabbit.ai/?via=tanstack&dub_id=aCcEEdAOqqutX6OS" >
248
+ <a href="https://www.coderabbit.ai/?via=tanstack&dub_id=aCcEEdAOqqutX6OS">
89
249
  <picture>
90
- <source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/coderabbit-dark-D643Zkrv.svg" />
250
+ <source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/coderabbit-dark-D643Zkrv.svg" />
91
251
  <source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/coderabbit-light-CIzGLYU_.svg" />
92
252
  <img src="https://tanstack.com/assets/coderabbit-light-CIzGLYU_.svg" height="40" alt="CodeRabbit" />
93
253
  </picture>
@@ -96,7 +256,7 @@ Available adapters: `openaiText`, `openaiEmbed`, `openaiSummarize`, `anthropicTe
96
256
  <td>
97
257
  <a href="https://www.cloudflare.com?utm_source=tanstack">
98
258
  <picture>
99
- <source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/cloudflare-white-Co-Tyjbl.svg" />
259
+ <source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/cloudflare-white-Co-Tyjbl.svg" />
100
260
  <source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/cloudflare-black-6Ojsn8yh.svg" />
101
261
  <img src="https://tanstack.com/assets/cloudflare-white-Co-Tyjbl.svg" height="60" alt="Cloudflare" />
102
262
  </picture>
@@ -106,28 +266,39 @@ Available adapters: `openaiText`, `openaiEmbed`, `openaiSummarize`, `anthropicTe
106
266
  </table>
107
267
 
108
268
  <div align="center">
109
- <img src="./media/partner_logo.svg" alt="AI & you?" height="65">
110
- <p>
111
- We're looking for TanStack AI Partners to join our mission! Partner with us to push the boundaries of TanStack AI and build amazing things together.
112
- </p>
113
- <a href="mailto:partners@tanstack.com?subject=TanStack AI Partnership"><b>LET'S CHAT</b></a>
269
+ <img src="https://raw.githubusercontent.com/TanStack/ai/main/media/partner_logo.svg" alt="AI and you?" height="65" />
270
+ <p>
271
+ We're looking for TanStack AI partners to join our mission. Partner with us
272
+ to push the boundaries of TanStack AI and build amazing things together.
273
+ </p>
274
+ <a href="mailto:partners@tanstack.com?subject=TanStack AI Partnership"><b>LET'S CHAT</b></a>
114
275
  </div>
115
276
 
116
277
  ## Explore the TanStack Ecosystem
117
278
 
118
- - <a href="https://github.com/tanstack/config"><b>TanStack Config</b></a> – Tooling for JS/TS packages
119
- - <a href="https://github.com/tanstack/db"><b>TanStack DB</b></a> – Reactive sync client store
120
- - <a href="https://github.com/tanstack/devtools"><b>TanStack Devtools</b></a> – Unified devtools panel
121
- - <a href="https://github.com/tanstack/form"><b>TanStack Form</b></a> – Type‑safe form state
122
- - <a href="https://github.com/tanstack/pacer"><b>TanStack Pacer</b></a> – Debouncing, throttling, batching
123
- - <a href="https://github.com/tanstack/query"><b>TanStack Query</b></a> – Async state & caching
124
- - <a href="https://github.com/tanstack/ranger"><b>TanStack Ranger</b></a> – Range & slider primitives
125
- - <a href="https://github.com/tanstack/router"><b>TanStack Router</b></a> – Type‑safe routing, caching & URL state
126
- - <a href="https://github.com/tanstack/router"><b>TanStack Start</b></a> – Full‑stack SSR & streaming
127
- - <a href="https://github.com/tanstack/store"><b>TanStack Store</b></a> – Reactive data store
128
- - <a href="https://github.com/tanstack/table"><b>TanStack Table</b></a> – Headless datagrids
129
- - <a href="https://github.com/tanstack/virtual"><b>TanStack Virtual</b></a> – Virtualized rendering
130
-
131
- … and more at <a href="https://tanstack.com"><b>TanStack.com »</b></a>
132
-
133
- <!-- USE THE FORCE LUKE -->
279
+ - <a href="https://github.com/tanstack/config"><b>TanStack Config</b></a> -
280
+ tooling for JS/TS packages
281
+ - <a href="https://github.com/tanstack/db"><b>TanStack DB</b></a> - reactive
282
+ sync client store
283
+ - <a href="https://github.com/tanstack/devtools"><b>TanStack Devtools</b></a> -
284
+ unified devtools panel
285
+ - <a href="https://github.com/tanstack/form"><b>TanStack Form</b></a> -
286
+ type-safe form state
287
+ - <a href="https://github.com/tanstack/pacer"><b>TanStack Pacer</b></a> -
288
+ debouncing, throttling, batching
289
+ - <a href="https://github.com/tanstack/query"><b>TanStack Query</b></a> -
290
+ async state and caching
291
+ - <a href="https://github.com/tanstack/ranger"><b>TanStack Ranger</b></a> -
292
+ range and slider primitives
293
+ - <a href="https://github.com/tanstack/router"><b>TanStack Router</b></a> -
294
+ type-safe routing, caching, and URL state
295
+ - <a href="https://github.com/tanstack/start"><b>TanStack Start</b></a> -
296
+ full-stack SSR and streaming
297
+ - <a href="https://github.com/tanstack/store"><b>TanStack Store</b></a> -
298
+ reactive data store
299
+ - <a href="https://github.com/tanstack/table"><b>TanStack Table</b></a> -
300
+ headless datagrids
301
+ - <a href="https://github.com/tanstack/virtual"><b>TanStack Virtual</b></a> -
302
+ virtualized rendering
303
+
304
+ ...and more at <a href="https://tanstack.com"><b>TanStack.com</b></a>.
@@ -64,6 +64,13 @@ export declare class GeminiTextAdapter<TModel extends (typeof GEMINI_MODELS)[num
64
64
  */
65
65
  private mergeConsecutiveSameRoleMessages;
66
66
  private mapCommonOptionsToGemini;
67
+ /**
68
+ * Gemini 3.x natively combines `tools` + `responseSchema` in a single
69
+ * streaming `generateContentStream` call (issue #605). Gemini 2.x is
70
+ * documented as brittle for the combination and keeps the engine's
71
+ * legacy finalization path.
72
+ */
73
+ supportsCombinedToolsAndSchema(): boolean;
67
74
  }
68
75
  /**
69
76
  * Creates a Gemini text adapter with explicit API key.
@@ -3,6 +3,7 @@ import { EventType, normalizeSystemPrompts } from "@tanstack/ai";
3
3
  import { BaseTextAdapter } from "@tanstack/ai/adapters";
4
4
  import { convertToolsToProviderFormat } from "../tools/tool-converter.js";
5
5
  import { createGeminiClient, generateId, getGeminiApiKeyFromEnv } from "../utils/client.js";
6
+ import { GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS } from "../model-meta.js";
6
7
  class GeminiTextAdapter extends BaseTextAdapter {
7
8
  kind = "text";
8
9
  name = "gemini";
@@ -570,6 +571,11 @@ class GeminiTextAdapter extends BaseTextAdapter {
570
571
  } : void 0;
571
572
  const normalizedPrompts = normalizeSystemPrompts(options.systemPrompts);
572
573
  const systemInstruction = normalizedPrompts.length > 0 ? normalizedPrompts.map((p) => p.content).join("\n") : void 0;
574
+ const combinedSchema = options.outputSchema;
575
+ const combinedSchemaConfig = combinedSchema ? {
576
+ responseMimeType: "application/json",
577
+ responseSchema: combinedSchema
578
+ } : void 0;
573
579
  const requestOptions = {
574
580
  model: options.model,
575
581
  contents: this.formatMessages(options.messages),
@@ -586,11 +592,21 @@ class GeminiTextAdapter extends BaseTextAdapter {
586
592
  thinkingConfig: mappedThinkingConfig
587
593
  },
588
594
  ...systemInstruction !== void 0 && { systemInstruction },
589
- tools: convertToolsToProviderFormat(options.tools)
595
+ tools: convertToolsToProviderFormat(options.tools),
596
+ ...combinedSchemaConfig ?? {}
590
597
  }
591
598
  };
592
599
  return requestOptions;
593
600
  }
601
+ /**
602
+ * Gemini 3.x natively combines `tools` + `responseSchema` in a single
603
+ * streaming `generateContentStream` call (issue #605). Gemini 2.x is
604
+ * documented as brittle for the combination and keeps the engine's
605
+ * legacy finalization path.
606
+ */
607
+ supportsCombinedToolsAndSchema() {
608
+ return GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS.has(this.model);
609
+ }
594
610
  }
595
611
  function createGeminiChat(model, apiKey, config) {
596
612
  return new GeminiTextAdapter({ apiKey, ...config }, model);
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { FinishReason } from '@google/genai'\nimport { EventType, normalizeSystemPrompts } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { convertToolsToProviderFormat } from '../tools/tool-converter'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport type {\n GEMINI_MODELS,\n GeminiChatModelProviderOptionsByName,\n GeminiChatModelToolCapabilitiesByName,\n GeminiModelInputModalitiesByName,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type {\n Content,\n GenerateContentParameters,\n GenerateContentResponse,\n GoogleGenAI,\n Part,\n ThinkingLevel,\n} from '@google/genai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type { ExternalTextProviderOptions } from '../text/text-provider-options'\nimport type {\n GeminiMessageMetadataByModality,\n GeminiToolCallMetadata,\n} from '../message-types'\nimport type { GeminiClientConfig } from '../utils'\n\n/**\n * Configuration for Gemini text adapter\n */\nexport interface GeminiTextConfig extends GeminiClientConfig {}\n\n/**\n * Gemini-specific provider options for text/chat\n */\nexport type GeminiTextProviderOptions = ExternalTextProviderOptions\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model.\n * If the model has explicit options in the map, use those; otherwise use base options.\n */\ntype ResolveProviderOptions<TModel extends string> =\n TModel extends keyof GeminiChatModelProviderOptionsByName\n ? GeminiChatModelProviderOptionsByName[TModel]\n : GeminiTextProviderOptions\n\n/**\n * Resolve input modalities for a specific model.\n * If the model has explicit modalities in the map, use those; otherwise use all modalities.\n */\ntype ResolveInputModalities<TModel extends string> =\n TModel extends keyof GeminiModelInputModalitiesByName\n ? GeminiModelInputModalitiesByName[TModel]\n : readonly ['text', 'image', 'audio', 'video', 'document']\n\n/**\n * Resolve tool capabilities for a specific model.\n * If the model has explicit tools in the map, use those; otherwise use empty tuple.\n */\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GeminiChatModelToolCapabilitiesByName\n ? NonNullable<GeminiChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n// ===========================\n// Adapter Implementation\n// ===========================\n\n/**\n * Gemini Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Gemini chat/text completion functionality.\n * Import only what you need for smaller bundle sizes.\n */\nexport class GeminiTextAdapter<\n TModel extends (typeof GEMINI_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GeminiMessageMetadataByModality,\n TToolCapabilities,\n GeminiToolCallMetadata\n> {\n override readonly kind = 'text' as const\n readonly name = 'gemini' as const\n\n private readonly client: GoogleGenAI\n\n constructor(config: GeminiTextConfig, model: TModel) {\n super({}, model)\n this.client = createGeminiClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GeminiTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const mappedOptions = this.mapCommonOptionsToGemini(options)\n const { logger } = options\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: 'gemini', model: this.model },\n )\n const result =\n await this.client.models.generateContentStream(mappedOptions)\n\n yield* this.processStreamChunks(result, options, logger)\n } catch (error) {\n logger.errors('gemini.chatStream fatal', {\n error,\n source: 'gemini.chatStream',\n })\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\n error: {\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\n },\n }\n }\n }\n\n /**\n * Generate structured output using Gemini's native JSON response format.\n * Uses responseMimeType: 'application/json' and responseSchema for structured output.\n * The outputSchema is already JSON Schema (converted in the ai layer).\n */\n async structuredOutput(\n options: StructuredOutputOptions<GeminiTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const { logger } = chatOptions\n\n const mappedOptions = this.mapCommonOptionsToGemini(chatOptions)\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,\n { provider: 'gemini', model: this.model },\n )\n // Add structured output configuration\n const result = await this.client.models.generateContent({\n ...mappedOptions,\n config: {\n ...mappedOptions.config,\n responseMimeType: 'application/json',\n responseSchema: outputSchema,\n },\n })\n\n // Extract text content from the response\n const rawText = this.extractTextFromResponse(result)\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n return {\n data: parsed,\n rawText,\n }\n } catch (error) {\n logger.errors('gemini.structuredOutput fatal', {\n error,\n source: 'gemini.structuredOutput',\n })\n throw new Error(\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during structured output generation.',\n )\n }\n }\n\n /**\n * Extract text content from a non-streaming response\n */\n private extractTextFromResponse(response: GenerateContentResponse): string {\n let textContent = ''\n\n if (response.candidates?.[0]?.content?.parts) {\n for (const part of response.candidates[0].content.parts) {\n if (part.text) {\n textContent += part.text\n }\n }\n }\n\n return textContent\n }\n\n private async *processStreamChunks(\n result: AsyncGenerator<GenerateContentResponse, unknown, unknown>,\n options: TextOptions<GeminiTextProviderOptions>,\n logger: InternalLogger,\n ): AsyncIterable<StreamChunk> {\n const model = options.model\n let accumulatedContent = ''\n let accumulatedThinking = ''\n const toolCallMap = new Map<\n string,\n {\n name: string\n args: string\n index: number\n started: boolean\n thoughtSignature?: string\n }\n >()\n let nextToolIndex = 0\n\n // AG-UI lifecycle tracking\n const runId = options.runId ?? generateId(this.name)\n const threadId = options.threadId ?? generateId(this.name)\n const messageId = generateId(this.name)\n let stepId: string | null = null\n let reasoningMessageId: string | null = null\n let hasClosedReasoning = false\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let hasEmittedStepStarted = false\n\n for await (const chunk of result) {\n logger.provider(`provider=gemini`, { chunk })\n // Emit RUN_STARTED on first chunk\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n }\n\n if (chunk.candidates?.[0]?.content?.parts) {\n const parts = chunk.candidates[0].content.parts\n\n for (const part of parts) {\n if (part.text) {\n if (part.thought) {\n // Emit STEP_STARTED and REASONING events on first thinking content\n if (!hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n reasoningMessageId = generateId(this.name)\n\n // Spec REASONING events\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model,\n timestamp: Date.now(),\n }\n\n // Legacy STEP events (kept during transition)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n accumulatedThinking += part.text\n\n // Spec REASONING content event — reasoningMessageId is set in the\n // hasEmittedStepStarted block above (entered on the same `part.thought` path)\n if (!reasoningMessageId) continue\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: part.text,\n model,\n timestamp: Date.now(),\n }\n\n // Legacy STEP event\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId || generateId(this.name),\n stepId: stepId || generateId(this.name),\n model,\n timestamp: Date.now(),\n delta: part.text,\n content: accumulatedThinking,\n }\n } else if (part.text.trim()) {\n // Close reasoning before text starts\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n // Skip whitespace-only text parts (e.g. \"\\n\" during auto-continuation)\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += part.text\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: part.text,\n content: accumulatedContent,\n }\n }\n }\n\n const functionCall = part.functionCall\n if (functionCall) {\n const toolCallId =\n functionCall.id ||\n `${functionCall.name}_${Date.now()}_${nextToolIndex}`\n const functionArgs = functionCall.args || {}\n\n // Gemini emits thoughtSignature as a Part-level sibling of\n // functionCall (per @google/genai Part type), not nested inside\n // functionCall itself.\n const partThoughtSignature = part.thoughtSignature || undefined\n\n let toolCallData = toolCallMap.get(toolCallId)\n if (!toolCallData) {\n toolCallData = {\n name: functionCall.name || '',\n args:\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs),\n index: nextToolIndex++,\n started: false,\n // Only set thoughtSignature when present — under EOPT, the\n // optional field cannot accept an explicit `undefined`.\n ...(partThoughtSignature !== undefined && {\n thoughtSignature: partThoughtSignature,\n }),\n }\n toolCallMap.set(toolCallId, toolCallData)\n } else {\n if (!toolCallData.thoughtSignature && partThoughtSignature) {\n toolCallData.thoughtSignature = partThoughtSignature\n }\n try {\n const existingArgs = JSON.parse(toolCallData.args)\n const newArgs =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n const mergedArgs = { ...existingArgs, ...newArgs }\n toolCallData.args = JSON.stringify(mergedArgs)\n } catch {\n toolCallData.args =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n }\n }\n\n // Emit TOOL_CALL_START if not already started\n if (!toolCallData.started) {\n toolCallData.started = true\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n index: toolCallData.index,\n ...(toolCallData.thoughtSignature && {\n metadata: {\n thoughtSignature: toolCallData.thoughtSignature,\n } satisfies GeminiToolCallMetadata,\n }),\n }\n }\n\n // Emit TOOL_CALL_ARGS\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n model,\n timestamp: Date.now(),\n delta: toolCallData.args,\n args: toolCallData.args,\n }\n }\n }\n } else if (chunk.data && chunk.data.trim()) {\n // Skip whitespace-only data (e.g. \"\\n\" during auto-continuation)\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += chunk.data\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: chunk.data,\n content: accumulatedContent,\n }\n }\n\n if (chunk.candidates?.[0]?.finishReason) {\n const finishReason = chunk.candidates[0].finishReason\n\n if (finishReason === FinishReason.UNEXPECTED_TOOL_CALL) {\n if (chunk.candidates[0].content?.parts) {\n for (const part of chunk.candidates[0].content.parts) {\n const functionCall = part.functionCall\n if (functionCall) {\n const toolCallId =\n functionCall.id ||\n `${functionCall.name}_${Date.now()}_${nextToolIndex}`\n const functionArgs = functionCall.args || {}\n\n const argsString =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n\n toolCallMap.set(toolCallId, {\n name: functionCall.name || '',\n args: argsString,\n index: nextToolIndex++,\n started: true,\n })\n\n // Emit TOOL_CALL_START\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n index: nextToolIndex - 1,\n }\n\n // Emit TOOL_CALL_END with parsed input\n let parsedInput: unknown = {}\n try {\n const parsed =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n }\n }\n }\n\n // Emit TOOL_CALL_END for all tracked tool calls\n for (const [toolCallId, toolCallData] of toolCallMap.entries()) {\n let parsedInput: unknown = {}\n try {\n const parsed = JSON.parse(toolCallData.args)\n parsedInput = parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n\n // Reset so a new TEXT_MESSAGE_START is emitted if text follows tool calls\n if (toolCallMap.size > 0) {\n hasEmittedTextMessageStart = false\n }\n\n if (finishReason === FinishReason.MAX_TOKENS) {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model,\n timestamp: Date.now(),\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n error: {\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n },\n }\n }\n\n // Close reasoning events if still open\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n finishReason: toolCallMap.size > 0 ? 'tool_calls' : 'stop',\n // RunFinishedEvent.usage is `usage?: {...}` (no `| undefined`) under\n // exactOptionalPropertyTypes; only include it when usageMetadata is\n // present rather than assigning an explicit `undefined`.\n ...(chunk.usageMetadata && {\n usage: {\n promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,\n completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,\n totalTokens: chunk.usageMetadata.totalTokenCount ?? 0,\n },\n }),\n }\n }\n }\n }\n\n private convertContentPartToGemini(part: ContentPart): Part {\n switch (part.type) {\n case 'text':\n return { text: part.content }\n case 'image':\n case 'audio':\n case 'video':\n case 'document': {\n if (part.source.type === 'data') {\n return {\n inlineData: {\n data: part.source.value,\n mimeType: part.source.mimeType,\n },\n }\n } else {\n // For URL sources, use provided mimeType or fall back to reasonable defaults\n const defaultMimeType = {\n image: 'image/jpeg',\n audio: 'audio/mp3',\n video: 'video/mp4',\n document: 'application/pdf',\n }[part.type]\n\n return {\n fileData: {\n fileUri: part.source.value,\n mimeType: part.source.mimeType ?? defaultMimeType,\n },\n }\n }\n }\n default: {\n const _exhaustiveCheck: never = part\n throw new Error(\n `Unsupported content part type: ${(_exhaustiveCheck as ContentPart).type}`,\n )\n }\n }\n }\n\n private formatMessages(\n messages: Array<ModelMessage>,\n ): GenerateContentParameters['contents'] {\n // Build a lookup from toolCallId → function name so functionResponse uses the\n // correct name instead of the raw call ID.\n const toolCallIdToName = new Map<string, string>()\n for (const msg of messages) {\n if (msg.role === 'assistant' && msg.toolCalls) {\n for (const tc of msg.toolCalls) {\n toolCallIdToName.set(tc.id, tc.function.name)\n }\n }\n }\n\n const formatted = messages.map((msg) => {\n const role: 'user' | 'model' = msg.role === 'assistant' ? 'model' : 'user'\n const parts: Array<Part> = []\n\n if (Array.isArray(msg.content)) {\n for (const contentPart of msg.content) {\n parts.push(this.convertContentPartToGemini(contentPart))\n }\n } else if (msg.content && msg.role !== 'tool') {\n parts.push({ text: msg.content })\n }\n\n if (msg.role === 'assistant' && msg.toolCalls?.length) {\n for (const toolCall of msg.toolCalls) {\n let parsedArgs: Record<string, unknown> = {}\n try {\n parsedArgs = toolCall.function.arguments\n ? (JSON.parse(toolCall.function.arguments) as Record<\n string,\n unknown\n >)\n : {}\n } catch {\n parsedArgs = {}\n }\n\n const thoughtSignature = (\n toolCall.metadata as GeminiToolCallMetadata | undefined\n )?.thoughtSignature\n // Gemini requires thoughtSignature at the Part level (sibling of\n // functionCall), not nested inside functionCall. Nesting it causes\n // the API to reject the next turn with\n // \"Function call is missing a thought_signature\".\n const part: Part = {\n functionCall: {\n id: toolCall.id,\n name: toolCall.function.name,\n args: parsedArgs,\n },\n }\n if (thoughtSignature) {\n part.thoughtSignature = thoughtSignature\n }\n parts.push(part)\n }\n }\n\n if (msg.role === 'tool' && msg.toolCallId) {\n const functionName =\n toolCallIdToName.get(msg.toolCallId) || msg.toolCallId\n parts.push({\n functionResponse: {\n id: msg.toolCallId,\n name: functionName,\n response: {\n content: msg.content || '',\n },\n },\n })\n }\n\n return {\n role,\n parts: parts.length > 0 ? parts : [{ text: '' }],\n }\n })\n\n // Post-process: Gemini requires strictly alternating user/model roles.\n // Tool results are mapped to role:'user', which can create consecutive\n // user messages when followed by a new user message. Merge them.\n return this.mergeConsecutiveSameRoleMessages(formatted)\n }\n\n /**\n * Merge consecutive messages of the same role into a single message.\n * Gemini's API requires strictly alternating user/model roles.\n * Tool results are mapped to role:'user', which can collide with actual\n * user messages in multi-turn conversations.\n *\n * Also filters out empty model messages (e.g., from a previous failed request)\n * and deduplicates functionResponse parts with the same name (tool call ID).\n */\n private mergeConsecutiveSameRoleMessages(\n messages: Array<Content>,\n ): Array<Content> {\n const merged: Array<Content> = []\n\n for (const msg of messages) {\n const parts = msg.parts || []\n\n // Skip empty model messages (no parts or only empty text)\n if (msg.role === 'model') {\n const hasContent =\n parts.length > 0 &&\n !parts.every(\n (p) => 'text' in p && (p as { text: string }).text === '',\n )\n if (!hasContent) {\n continue\n }\n }\n\n const prev = merged[merged.length - 1]\n if (prev && prev.role === msg.role) {\n // Merge parts arrays\n prev.parts = [...(prev.parts || []), ...parts]\n } else {\n merged.push({ ...msg, parts: [...parts] })\n }\n }\n\n // Deduplicate functionResponse parts with the same name (tool call ID)\n for (const msg of merged) {\n if (!msg.parts) continue\n const seenFunctionResponseNames = new Set<string>()\n msg.parts = msg.parts.filter((part) => {\n if ('functionResponse' in part && part.functionResponse?.name) {\n if (seenFunctionResponseNames.has(part.functionResponse.name)) {\n return false\n }\n seenFunctionResponseNames.add(part.functionResponse.name)\n }\n return true\n })\n }\n\n return merged\n }\n\n private mapCommonOptionsToGemini(\n options: TextOptions<GeminiTextProviderOptions>,\n ) {\n // Separate `thinkingConfig` from the other model options so the loose\n // local `thinkingLevel?: keyof typeof ThinkingLevel` type doesn't leak\n // into the SDK config object via the `...modelOpts` spread — we re-add a\n // properly-typed `ThinkingConfig` below.\n const { thinkingConfig, ...modelOpts } = options.modelOptions ?? {}\n // Build the thinkingConfig payload only when the caller actually supplied\n // one. Our local `thinkingLevel` is typed as `keyof typeof ThinkingLevel`\n // (string union) so users can pass plain strings; the SDK target is the\n // `ThinkingLevel` enum, and every field is `field?: T` under EOPT — so we\n // re-emit fields via conditional spreads.\n const mappedThinkingConfig = thinkingConfig\n ? {\n ...(thinkingConfig.includeThoughts !== undefined && {\n includeThoughts: thinkingConfig.includeThoughts,\n }),\n ...(thinkingConfig.thinkingBudget !== undefined && {\n thinkingBudget: thinkingConfig.thinkingBudget,\n }),\n ...(thinkingConfig.thinkingLevel\n ? {\n thinkingLevel: thinkingConfig.thinkingLevel as ThinkingLevel,\n }\n : {}),\n }\n : undefined\n\n const normalizedPrompts = normalizeSystemPrompts(options.systemPrompts)\n const systemInstruction =\n normalizedPrompts.length > 0\n ? normalizedPrompts.map((p) => p.content).join('\\n')\n : undefined\n\n // Vendor `GenerateContentConfig` fields are `field?: T` (no `| undefined`)\n // under EOPT, so spread each common option only when present rather than\n // emitting `field: undefined`s into the wire payload.\n const requestOptions: GenerateContentParameters = {\n model: options.model,\n contents: this.formatMessages(options.messages),\n config: {\n ...modelOpts,\n ...(options.temperature !== undefined && {\n temperature: options.temperature,\n }),\n ...(options.topP !== undefined && { topP: options.topP }),\n ...(options.maxTokens !== undefined && {\n maxOutputTokens: options.maxTokens,\n }),\n ...(mappedThinkingConfig !== undefined && {\n thinkingConfig: mappedThinkingConfig,\n }),\n ...(systemInstruction !== undefined && { systemInstruction }),\n tools: convertToolsToProviderFormat(options.tools),\n },\n }\n\n return requestOptions\n }\n}\n\n/**\n * Creates a Gemini text adapter with explicit API key.\n * Type resolution happens here at the call site.\n */\nexport function createGeminiChat<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n return new GeminiTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Gemini text adapter with automatic API key detection.\n * Type resolution happens here at the call site.\n */\nexport function geminiText<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiChat(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { FinishReason } from '@google/genai'\nimport { EventType, normalizeSystemPrompts } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { convertToolsToProviderFormat } from '../tools/tool-converter'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport { GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS } from '../model-meta'\nimport type {\n GEMINI_MODELS,\n GeminiChatModelProviderOptionsByName,\n GeminiChatModelToolCapabilitiesByName,\n GeminiModelInputModalitiesByName,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type {\n Content,\n GenerateContentParameters,\n GenerateContentResponse,\n GoogleGenAI,\n Part,\n ThinkingLevel,\n} from '@google/genai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type { ExternalTextProviderOptions } from '../text/text-provider-options'\nimport type {\n GeminiMessageMetadataByModality,\n GeminiToolCallMetadata,\n} from '../message-types'\nimport type { GeminiClientConfig } from '../utils'\n\n/**\n * Configuration for Gemini text adapter\n */\nexport interface GeminiTextConfig extends GeminiClientConfig {}\n\n/**\n * Gemini-specific provider options for text/chat\n */\nexport type GeminiTextProviderOptions = ExternalTextProviderOptions\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model.\n * If the model has explicit options in the map, use those; otherwise use base options.\n */\ntype ResolveProviderOptions<TModel extends string> =\n TModel extends keyof GeminiChatModelProviderOptionsByName\n ? GeminiChatModelProviderOptionsByName[TModel]\n : GeminiTextProviderOptions\n\n/**\n * Resolve input modalities for a specific model.\n * If the model has explicit modalities in the map, use those; otherwise use all modalities.\n */\ntype ResolveInputModalities<TModel extends string> =\n TModel extends keyof GeminiModelInputModalitiesByName\n ? GeminiModelInputModalitiesByName[TModel]\n : readonly ['text', 'image', 'audio', 'video', 'document']\n\n/**\n * Resolve tool capabilities for a specific model.\n * If the model has explicit tools in the map, use those; otherwise use empty tuple.\n */\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GeminiChatModelToolCapabilitiesByName\n ? NonNullable<GeminiChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n// ===========================\n// Adapter Implementation\n// ===========================\n\n/**\n * Gemini Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Gemini chat/text completion functionality.\n * Import only what you need for smaller bundle sizes.\n */\nexport class GeminiTextAdapter<\n TModel extends (typeof GEMINI_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GeminiMessageMetadataByModality,\n TToolCapabilities,\n GeminiToolCallMetadata\n> {\n override readonly kind = 'text' as const\n readonly name = 'gemini' as const\n\n private readonly client: GoogleGenAI\n\n constructor(config: GeminiTextConfig, model: TModel) {\n super({}, model)\n this.client = createGeminiClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GeminiTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const mappedOptions = this.mapCommonOptionsToGemini(options)\n const { logger } = options\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: 'gemini', model: this.model },\n )\n const result =\n await this.client.models.generateContentStream(mappedOptions)\n\n yield* this.processStreamChunks(result, options, logger)\n } catch (error) {\n logger.errors('gemini.chatStream fatal', {\n error,\n source: 'gemini.chatStream',\n })\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\n error: {\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\n },\n }\n }\n }\n\n /**\n * Generate structured output using Gemini's native JSON response format.\n * Uses responseMimeType: 'application/json' and responseSchema for structured output.\n * The outputSchema is already JSON Schema (converted in the ai layer).\n */\n async structuredOutput(\n options: StructuredOutputOptions<GeminiTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const { logger } = chatOptions\n\n const mappedOptions = this.mapCommonOptionsToGemini(chatOptions)\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,\n { provider: 'gemini', model: this.model },\n )\n // Add structured output configuration\n const result = await this.client.models.generateContent({\n ...mappedOptions,\n config: {\n ...mappedOptions.config,\n responseMimeType: 'application/json',\n responseSchema: outputSchema,\n },\n })\n\n // Extract text content from the response\n const rawText = this.extractTextFromResponse(result)\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n return {\n data: parsed,\n rawText,\n }\n } catch (error) {\n logger.errors('gemini.structuredOutput fatal', {\n error,\n source: 'gemini.structuredOutput',\n })\n throw new Error(\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during structured output generation.',\n )\n }\n }\n\n /**\n * Extract text content from a non-streaming response\n */\n private extractTextFromResponse(response: GenerateContentResponse): string {\n let textContent = ''\n\n if (response.candidates?.[0]?.content?.parts) {\n for (const part of response.candidates[0].content.parts) {\n if (part.text) {\n textContent += part.text\n }\n }\n }\n\n return textContent\n }\n\n private async *processStreamChunks(\n result: AsyncGenerator<GenerateContentResponse, unknown, unknown>,\n options: TextOptions<GeminiTextProviderOptions>,\n logger: InternalLogger,\n ): AsyncIterable<StreamChunk> {\n const model = options.model\n let accumulatedContent = ''\n let accumulatedThinking = ''\n const toolCallMap = new Map<\n string,\n {\n name: string\n args: string\n index: number\n started: boolean\n thoughtSignature?: string\n }\n >()\n let nextToolIndex = 0\n\n // AG-UI lifecycle tracking\n const runId = options.runId ?? generateId(this.name)\n const threadId = options.threadId ?? generateId(this.name)\n const messageId = generateId(this.name)\n let stepId: string | null = null\n let reasoningMessageId: string | null = null\n let hasClosedReasoning = false\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let hasEmittedStepStarted = false\n\n for await (const chunk of result) {\n logger.provider(`provider=gemini`, { chunk })\n // Emit RUN_STARTED on first chunk\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n }\n\n if (chunk.candidates?.[0]?.content?.parts) {\n const parts = chunk.candidates[0].content.parts\n\n for (const part of parts) {\n if (part.text) {\n if (part.thought) {\n // Emit STEP_STARTED and REASONING events on first thinking content\n if (!hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n reasoningMessageId = generateId(this.name)\n\n // Spec REASONING events\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model,\n timestamp: Date.now(),\n }\n\n // Legacy STEP events (kept during transition)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n accumulatedThinking += part.text\n\n // Spec REASONING content event — reasoningMessageId is set in the\n // hasEmittedStepStarted block above (entered on the same `part.thought` path)\n if (!reasoningMessageId) continue\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: part.text,\n model,\n timestamp: Date.now(),\n }\n\n // Legacy STEP event\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId || generateId(this.name),\n stepId: stepId || generateId(this.name),\n model,\n timestamp: Date.now(),\n delta: part.text,\n content: accumulatedThinking,\n }\n } else if (part.text.trim()) {\n // Close reasoning before text starts\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n // Skip whitespace-only text parts (e.g. \"\\n\" during auto-continuation)\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += part.text\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: part.text,\n content: accumulatedContent,\n }\n }\n }\n\n const functionCall = part.functionCall\n if (functionCall) {\n const toolCallId =\n functionCall.id ||\n `${functionCall.name}_${Date.now()}_${nextToolIndex}`\n const functionArgs = functionCall.args || {}\n\n // Gemini emits thoughtSignature as a Part-level sibling of\n // functionCall (per @google/genai Part type), not nested inside\n // functionCall itself.\n const partThoughtSignature = part.thoughtSignature || undefined\n\n let toolCallData = toolCallMap.get(toolCallId)\n if (!toolCallData) {\n toolCallData = {\n name: functionCall.name || '',\n args:\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs),\n index: nextToolIndex++,\n started: false,\n // Only set thoughtSignature when present — under EOPT, the\n // optional field cannot accept an explicit `undefined`.\n ...(partThoughtSignature !== undefined && {\n thoughtSignature: partThoughtSignature,\n }),\n }\n toolCallMap.set(toolCallId, toolCallData)\n } else {\n if (!toolCallData.thoughtSignature && partThoughtSignature) {\n toolCallData.thoughtSignature = partThoughtSignature\n }\n try {\n const existingArgs = JSON.parse(toolCallData.args)\n const newArgs =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n const mergedArgs = { ...existingArgs, ...newArgs }\n toolCallData.args = JSON.stringify(mergedArgs)\n } catch {\n toolCallData.args =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n }\n }\n\n // Emit TOOL_CALL_START if not already started\n if (!toolCallData.started) {\n toolCallData.started = true\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n index: toolCallData.index,\n ...(toolCallData.thoughtSignature && {\n metadata: {\n thoughtSignature: toolCallData.thoughtSignature,\n } satisfies GeminiToolCallMetadata,\n }),\n }\n }\n\n // Emit TOOL_CALL_ARGS\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n model,\n timestamp: Date.now(),\n delta: toolCallData.args,\n args: toolCallData.args,\n }\n }\n }\n } else if (chunk.data && chunk.data.trim()) {\n // Skip whitespace-only data (e.g. \"\\n\" during auto-continuation)\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += chunk.data\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: chunk.data,\n content: accumulatedContent,\n }\n }\n\n if (chunk.candidates?.[0]?.finishReason) {\n const finishReason = chunk.candidates[0].finishReason\n\n if (finishReason === FinishReason.UNEXPECTED_TOOL_CALL) {\n if (chunk.candidates[0].content?.parts) {\n for (const part of chunk.candidates[0].content.parts) {\n const functionCall = part.functionCall\n if (functionCall) {\n const toolCallId =\n functionCall.id ||\n `${functionCall.name}_${Date.now()}_${nextToolIndex}`\n const functionArgs = functionCall.args || {}\n\n const argsString =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n\n toolCallMap.set(toolCallId, {\n name: functionCall.name || '',\n args: argsString,\n index: nextToolIndex++,\n started: true,\n })\n\n // Emit TOOL_CALL_START\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n index: nextToolIndex - 1,\n }\n\n // Emit TOOL_CALL_END with parsed input\n let parsedInput: unknown = {}\n try {\n const parsed =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n }\n }\n }\n\n // Emit TOOL_CALL_END for all tracked tool calls\n for (const [toolCallId, toolCallData] of toolCallMap.entries()) {\n let parsedInput: unknown = {}\n try {\n const parsed = JSON.parse(toolCallData.args)\n parsedInput = parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n\n // Reset so a new TEXT_MESSAGE_START is emitted if text follows tool calls\n if (toolCallMap.size > 0) {\n hasEmittedTextMessageStart = false\n }\n\n if (finishReason === FinishReason.MAX_TOKENS) {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model,\n timestamp: Date.now(),\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n error: {\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n },\n }\n }\n\n // Close reasoning events if still open\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n finishReason: toolCallMap.size > 0 ? 'tool_calls' : 'stop',\n // RunFinishedEvent.usage is `usage?: {...}` (no `| undefined`) under\n // exactOptionalPropertyTypes; only include it when usageMetadata is\n // present rather than assigning an explicit `undefined`.\n ...(chunk.usageMetadata && {\n usage: {\n promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,\n completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,\n totalTokens: chunk.usageMetadata.totalTokenCount ?? 0,\n },\n }),\n }\n }\n }\n }\n\n private convertContentPartToGemini(part: ContentPart): Part {\n switch (part.type) {\n case 'text':\n return { text: part.content }\n case 'image':\n case 'audio':\n case 'video':\n case 'document': {\n if (part.source.type === 'data') {\n return {\n inlineData: {\n data: part.source.value,\n mimeType: part.source.mimeType,\n },\n }\n } else {\n // For URL sources, use provided mimeType or fall back to reasonable defaults\n const defaultMimeType = {\n image: 'image/jpeg',\n audio: 'audio/mp3',\n video: 'video/mp4',\n document: 'application/pdf',\n }[part.type]\n\n return {\n fileData: {\n fileUri: part.source.value,\n mimeType: part.source.mimeType ?? defaultMimeType,\n },\n }\n }\n }\n default: {\n const _exhaustiveCheck: never = part\n throw new Error(\n `Unsupported content part type: ${(_exhaustiveCheck as ContentPart).type}`,\n )\n }\n }\n }\n\n private formatMessages(\n messages: Array<ModelMessage>,\n ): GenerateContentParameters['contents'] {\n // Build a lookup from toolCallId → function name so functionResponse uses the\n // correct name instead of the raw call ID.\n const toolCallIdToName = new Map<string, string>()\n for (const msg of messages) {\n if (msg.role === 'assistant' && msg.toolCalls) {\n for (const tc of msg.toolCalls) {\n toolCallIdToName.set(tc.id, tc.function.name)\n }\n }\n }\n\n const formatted = messages.map((msg) => {\n const role: 'user' | 'model' = msg.role === 'assistant' ? 'model' : 'user'\n const parts: Array<Part> = []\n\n if (Array.isArray(msg.content)) {\n for (const contentPart of msg.content) {\n parts.push(this.convertContentPartToGemini(contentPart))\n }\n } else if (msg.content && msg.role !== 'tool') {\n parts.push({ text: msg.content })\n }\n\n if (msg.role === 'assistant' && msg.toolCalls?.length) {\n for (const toolCall of msg.toolCalls) {\n let parsedArgs: Record<string, unknown> = {}\n try {\n parsedArgs = toolCall.function.arguments\n ? (JSON.parse(toolCall.function.arguments) as Record<\n string,\n unknown\n >)\n : {}\n } catch {\n parsedArgs = {}\n }\n\n const thoughtSignature = (\n toolCall.metadata as GeminiToolCallMetadata | undefined\n )?.thoughtSignature\n // Gemini requires thoughtSignature at the Part level (sibling of\n // functionCall), not nested inside functionCall. Nesting it causes\n // the API to reject the next turn with\n // \"Function call is missing a thought_signature\".\n const part: Part = {\n functionCall: {\n id: toolCall.id,\n name: toolCall.function.name,\n args: parsedArgs,\n },\n }\n if (thoughtSignature) {\n part.thoughtSignature = thoughtSignature\n }\n parts.push(part)\n }\n }\n\n if (msg.role === 'tool' && msg.toolCallId) {\n const functionName =\n toolCallIdToName.get(msg.toolCallId) || msg.toolCallId\n parts.push({\n functionResponse: {\n id: msg.toolCallId,\n name: functionName,\n response: {\n content: msg.content || '',\n },\n },\n })\n }\n\n return {\n role,\n parts: parts.length > 0 ? parts : [{ text: '' }],\n }\n })\n\n // Post-process: Gemini requires strictly alternating user/model roles.\n // Tool results are mapped to role:'user', which can create consecutive\n // user messages when followed by a new user message. Merge them.\n return this.mergeConsecutiveSameRoleMessages(formatted)\n }\n\n /**\n * Merge consecutive messages of the same role into a single message.\n * Gemini's API requires strictly alternating user/model roles.\n * Tool results are mapped to role:'user', which can collide with actual\n * user messages in multi-turn conversations.\n *\n * Also filters out empty model messages (e.g., from a previous failed request)\n * and deduplicates functionResponse parts with the same name (tool call ID).\n */\n private mergeConsecutiveSameRoleMessages(\n messages: Array<Content>,\n ): Array<Content> {\n const merged: Array<Content> = []\n\n for (const msg of messages) {\n const parts = msg.parts || []\n\n // Skip empty model messages (no parts or only empty text)\n if (msg.role === 'model') {\n const hasContent =\n parts.length > 0 &&\n !parts.every(\n (p) => 'text' in p && (p as { text: string }).text === '',\n )\n if (!hasContent) {\n continue\n }\n }\n\n const prev = merged[merged.length - 1]\n if (prev && prev.role === msg.role) {\n // Merge parts arrays\n prev.parts = [...(prev.parts || []), ...parts]\n } else {\n merged.push({ ...msg, parts: [...parts] })\n }\n }\n\n // Deduplicate functionResponse parts with the same name (tool call ID)\n for (const msg of merged) {\n if (!msg.parts) continue\n const seenFunctionResponseNames = new Set<string>()\n msg.parts = msg.parts.filter((part) => {\n if ('functionResponse' in part && part.functionResponse?.name) {\n if (seenFunctionResponseNames.has(part.functionResponse.name)) {\n return false\n }\n seenFunctionResponseNames.add(part.functionResponse.name)\n }\n return true\n })\n }\n\n return merged\n }\n\n private mapCommonOptionsToGemini(\n options: TextOptions<GeminiTextProviderOptions>,\n ) {\n // Separate `thinkingConfig` from the other model options so the loose\n // local `thinkingLevel?: keyof typeof ThinkingLevel` type doesn't leak\n // into the SDK config object via the `...modelOpts` spread — we re-add a\n // properly-typed `ThinkingConfig` below.\n const { thinkingConfig, ...modelOpts } = options.modelOptions ?? {}\n // Build the thinkingConfig payload only when the caller actually supplied\n // one. Our local `thinkingLevel` is typed as `keyof typeof ThinkingLevel`\n // (string union) so users can pass plain strings; the SDK target is the\n // `ThinkingLevel` enum, and every field is `field?: T` under EOPT — so we\n // re-emit fields via conditional spreads.\n const mappedThinkingConfig = thinkingConfig\n ? {\n ...(thinkingConfig.includeThoughts !== undefined && {\n includeThoughts: thinkingConfig.includeThoughts,\n }),\n ...(thinkingConfig.thinkingBudget !== undefined && {\n thinkingBudget: thinkingConfig.thinkingBudget,\n }),\n ...(thinkingConfig.thinkingLevel\n ? {\n thinkingLevel: thinkingConfig.thinkingLevel as ThinkingLevel,\n }\n : {}),\n }\n : undefined\n\n const normalizedPrompts = normalizeSystemPrompts(options.systemPrompts)\n const systemInstruction =\n normalizedPrompts.length > 0\n ? normalizedPrompts.map((p) => p.content).join('\\n')\n : undefined\n\n // Native combined mode (issue #605): when the engine threads\n // `outputSchema` through TextOptions, the adapter declared\n // `supportsCombinedToolsAndSchema` (Gemini 3.x only). The schema is\n // already JSON Schema (pre-converted at the activity boundary). Wire\n // it into `config.responseSchema` + `responseMimeType: 'application/json'`\n // alongside any `tools` — the model emits function calls during the\n // agent loop and the schema-constrained JSON on its natural final\n // turn, so the engine can harvest it without the separate\n // `structuredOutput` finalization round-trip.\n const combinedSchema = options.outputSchema as\n | Record<string, unknown>\n | undefined\n const combinedSchemaConfig = combinedSchema\n ? {\n responseMimeType: 'application/json' as const,\n responseSchema: combinedSchema,\n }\n : undefined\n\n // Vendor `GenerateContentConfig` fields are `field?: T` (no `| undefined`)\n // under EOPT, so spread each common option only when present rather than\n // emitting `field: undefined`s into the wire payload.\n const requestOptions: GenerateContentParameters = {\n model: options.model,\n contents: this.formatMessages(options.messages),\n config: {\n ...modelOpts,\n ...(options.temperature !== undefined && {\n temperature: options.temperature,\n }),\n ...(options.topP !== undefined && { topP: options.topP }),\n ...(options.maxTokens !== undefined && {\n maxOutputTokens: options.maxTokens,\n }),\n ...(mappedThinkingConfig !== undefined && {\n thinkingConfig: mappedThinkingConfig,\n }),\n ...(systemInstruction !== undefined && { systemInstruction }),\n tools: convertToolsToProviderFormat(options.tools),\n ...(combinedSchemaConfig ?? {}),\n },\n }\n\n return requestOptions\n }\n\n /**\n * Gemini 3.x natively combines `tools` + `responseSchema` in a single\n * streaming `generateContentStream` call (issue #605). Gemini 2.x is\n * documented as brittle for the combination and keeps the engine's\n * legacy finalization path.\n */\n supportsCombinedToolsAndSchema(): boolean {\n return GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS.has(this.model)\n }\n}\n\n/**\n * Creates a Gemini text adapter with explicit API key.\n * Type resolution happens here at the call site.\n */\nexport function createGeminiChat<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n return new GeminiTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Gemini text adapter with automatic API key detection.\n * Type resolution happens here at the call site.\n */\nexport function geminiText<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiChat(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ export { GeminiTTSAdapter, createGeminiSpeech, geminiSpeech, type GeminiTTSConfi
10
10
  * @experimental Gemini Lyria music generation is an experimental feature and may change.
11
11
  */
12
12
  export { GeminiAudioAdapter, createGeminiAudio, geminiAudio, type GeminiAudioConfig, type GeminiAudioModel, type GeminiAudioProviderOptions, } from './adapters/audio.js';
13
- export { GEMINI_MODELS } from './model-meta.js';
13
+ export { GEMINI_MODELS, GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS, } from './model-meta.js';
14
14
  export { GEMINI_MODELS as GeminiTextModels } from './model-meta.js';
15
15
  export { GEMINI_IMAGE_MODELS as GeminiImageModels } from './model-meta.js';
16
16
  export { GEMINI_TTS_MODELS as GeminiTTSModels } from './model-meta.js';
package/dist/esm/index.js CHANGED
@@ -3,8 +3,9 @@ import { createGeminiSummarize, geminiSummarize } from "./adapters/summarize.js"
3
3
  import { GeminiImageAdapter, createGeminiImage, geminiImage } from "./adapters/image.js";
4
4
  import { GeminiTTSAdapter, createGeminiSpeech, geminiSpeech } from "./adapters/tts.js";
5
5
  import { GeminiAudioAdapter, createGeminiAudio, geminiAudio } from "./adapters/audio.js";
6
- import { GEMINI_MODELS, GEMINI_AUDIO_MODELS, GEMINI_IMAGE_MODELS, GEMINI_TTS_MODELS, GEMINI_TTS_VOICES, GEMINI_MODELS as GEMINI_MODELS2 } from "./model-meta.js";
6
+ import { GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS, GEMINI_MODELS, GEMINI_AUDIO_MODELS, GEMINI_IMAGE_MODELS, GEMINI_TTS_MODELS, GEMINI_TTS_VOICES, GEMINI_MODELS as GEMINI_MODELS2 } from "./model-meta.js";
7
7
  export {
8
+ GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS,
8
9
  GEMINI_MODELS,
9
10
  GeminiAudioAdapter,
10
11
  GEMINI_AUDIO_MODELS as GeminiAudioModels,
@@ -354,6 +354,14 @@ declare const GEMINI_3_5_FLASH: {
354
354
  };
355
355
  };
356
356
  export declare const GEMINI_MODELS: readonly ["gemini-3.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview", "gemini-3.1-flash-lite-preview", "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-preview-09-2025", "gemini-2.5-flash-lite", "gemini-2.5-flash-lite-preview-09-2025", "gemini-2.0-flash", "gemini-2.0-flash-lite", "gemini-3.5-flash"];
357
+ /**
358
+ * Gemini models that support combining `tools` + `responseSchema` in a
359
+ * single streaming `generateContent` call (per issue #605). Per the
360
+ * provider matrix, Gemini 3.x natively interleaves the schema-constrained
361
+ * answer with function-calling on one pass; Gemini 2.x is unsupported /
362
+ * brittle and keeps the engine's legacy finalization fallback.
363
+ */
364
+ export declare const GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS: Set<string>;
357
365
  export type GeminiModels = (typeof GEMINI_MODELS)[number];
358
366
  export type GeminiImageModels = (typeof GEMINI_IMAGE_MODELS)[number];
359
367
  export declare const GEMINI_IMAGE_MODELS: readonly ["gemini-3.1-flash-image-preview", "gemini-3-pro-image-preview", "gemini-2.5-flash-image", "gemini-2.0-flash-preview-image-generation", "imagen-3.0-generate-002", "imagen-4.0-generate-001", "imagen-4.0-fast-generate-001", "imagen-4.0-ultra-generate-001"];
@@ -87,6 +87,13 @@ const GEMINI_MODELS = [
87
87
  GEMINI_2_FLASH_LITE.name,
88
88
  GEMINI_3_5_FLASH.name
89
89
  ];
90
+ const GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS = /* @__PURE__ */ new Set([
91
+ GEMINI_3_1_PRO.name,
92
+ GEMINI_3_PRO.name,
93
+ GEMINI_3_FLASH.name,
94
+ GEMINI_3_1_FLASH_LITE.name,
95
+ GEMINI_3_5_FLASH.name
96
+ ]);
90
97
  const GEMINI_IMAGE_MODELS = [
91
98
  GEMINI_3_1_FLASH_IMAGE.name,
92
99
  GEMINI_3_PRO_IMAGE.name,
@@ -140,6 +147,7 @@ const GEMINI_TTS_VOICES = [
140
147
  ];
141
148
  export {
142
149
  GEMINI_AUDIO_MODELS,
150
+ GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS,
143
151
  GEMINI_IMAGE_MODELS,
144
152
  GEMINI_MODELS,
145
153
  GEMINI_TTS_MODELS,
@@ -1 +1 @@
1
- {"version":3,"file":"model-meta.js","sources":["../../src/model-meta.ts"],"sourcesContent":["import type {\n GeminiCachedContentOptions,\n GeminiCommonConfigOptions,\n GeminiSafetyOptions,\n GeminiStructuredOutputOptions,\n GeminiThinkingOptions,\n GeminiToolConfigOptions,\n} from './text/text-provider-options'\n\ninterface ModelMeta<TProviderOptions = unknown> {\n name: string\n supports: {\n input: Array<'text' | 'image' | 'audio' | 'video' | 'document'>\n output: Array<'text' | 'image' | 'audio' | 'video'>\n capabilities?: Array<\n | 'audio_generation'\n | 'batch_api'\n | 'caching'\n | 'function_calling'\n | 'live_api'\n | 'structured_output'\n | 'thinking'\n >\n tools?: Array<\n | 'code_execution'\n | 'file_search'\n | 'google_search'\n | 'google_search_retrieval'\n | 'google_maps'\n | 'url_context'\n | 'computer_use'\n >\n }\n max_input_tokens?: number\n max_output_tokens?: number\n knowledge_cutoff?: string\n pricing?: {\n input: {\n normal: number\n cached?: number\n }\n output: {\n normal: number\n }\n }\n /**\n * Type-level description of which provider options this model supports.\n */\n providerOptions?: TProviderOptions\n}\n\nconst GEMINI_3_1_PRO = {\n name: 'gemini-3.1-pro-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_PRO = {\n name: 'gemini-3-pro-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_FLASH = {\n name: 'gemini-3-flash-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.5,\n },\n output: {\n normal: 3,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_PRO_IMAGE = {\n name: 'gemini-3-pro-image-preview',\n max_input_tokens: 65_536,\n max_output_tokens: 32_768,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 2,\n },\n output: {\n normal: 0.134,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_1_FLASH_IMAGE = {\n name: 'gemini-3.1-flash-image-preview',\n max_input_tokens: 65_536,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_1_FLASH_LITE = {\n name: 'gemini-3.1-flash-lite-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_PRO = {\n name: 'gemini-2.5-pro',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_maps',\n 'google_search',\n 'url_context',\n ],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_PRO_TTS = {\n name: 'gemini-2.5-pro-preview-tts',\n max_input_tokens: 8_192,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst GEMINI_2_5_FLASH = {\n name: 'gemini-2.5-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_maps',\n 'google_search',\n 'url_context',\n ],\n },\n pricing: {\n input: {\n normal: 1,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_FLASH_PREVIEW = {\n name: 'gemini-2.5-flash-preview-09-2025',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 1,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_FLASH_IMAGE = {\n name: 'gemini-2.5-flash-image',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-06-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'caching', 'structured_output'],\n tools: ['file_search'],\n },\n pricing: {\n input: {\n normal: 0.3,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n/**\nconst GEMINI_2_5_FLASH_LIVE = {\n name: 'gemini-2.5-flash-native-audio-preview-09-2025',\n max_input_tokens: 141_072,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'audio', 'video'],\n output: ['text', 'audio'],\n capabilities: [\n 'audio_generation',\n 'file_search',\n 'function_calling',\n 'live_api',\n 'search_grounding',\n 'thinking',\n ],\n },\n pricing: {\n // todo find this info\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions &\n GeminiThinkingOptions\n>\n*/\nconst GEMINI_2_5_FLASH_TTS = {\n name: 'gemini-2.5-flash-preview-tts',\n max_input_tokens: 8_192,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation', 'batch_api'],\n },\n pricing: {\n input: {\n normal: 1,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Gemini 3.1 Flash TTS Preview - latest expressive TTS model with\n * 200+ audio tags, 70+ languages, and multi-speaker dialogue support.\n * @see https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-tts-preview\n */\nconst GEMINI_3_1_FLASH_TTS = {\n name: 'gemini-3.1-flash-tts-preview',\n max_input_tokens: 32_768,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation', 'batch_api'],\n },\n pricing: {\n input: {\n normal: 0.5,\n },\n output: {\n normal: 10,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Lyria 3 Pro Preview — Google's flagship music generation model.\n * Generates full-length songs with multiple verses, choruses, and bridges.\n * Outputs MP3 or WAV at 48 kHz stereo.\n * @see https://ai.google.dev/gemini-api/docs/models/lyria-3-pro-preview\n */\nconst LYRIA_3_PRO = {\n name: 'lyria-3-pro-preview',\n max_input_tokens: 131_072,\n supports: {\n input: ['text', 'image'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta\n\n/**\n * Lyria 3 Clip Preview — 30-second music clips in MP3 format.\n * @see https://ai.google.dev/gemini-api/docs/music-generation\n */\nconst LYRIA_3_CLIP = {\n name: 'lyria-3-clip-preview',\n max_input_tokens: 131_072,\n supports: {\n input: ['text', 'image'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta\n\nconst GEMINI_2_5_FLASH_LITE = {\n name: 'gemini-2.5-flash-lite',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'google_maps', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.1,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_FLASH_LITE_PREVIEW = {\n name: 'gemini-2.5-flash-lite-preview-09-2025',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.1,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_FLASH = {\n name: 'gemini-2.0-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2024-08-01',\n supports: {\n input: ['text', 'image', 'audio', 'video'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'live_api',\n 'structured_output',\n ],\n tools: ['code_execution', 'google_maps', 'google_search'],\n },\n pricing: {\n input: {\n normal: 0.1,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions\n>\n\nconst GEMINI_2_FLASH_IMAGE = {\n name: 'gemini-2.0-flash-preview-image-generation',\n max_input_tokens: 32_768,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2024-08-01',\n supports: {\n input: ['text', 'image', 'audio', 'video'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'caching', 'structured_output'],\n tools: [],\n },\n pricing: {\n input: {\n normal: 0.1,\n },\n output: {\n normal: 0.039,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n/* \nconst GEMINI_2_FLASH_LIVE = {\n name: 'gemini-2.0-flash-live-001',\n max_input_tokens: 1_048_576,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2024-08-01',\n supports: {\n input: ['text', 'audio', 'video'],\n output: ['text', 'audio'],\n capabilities: [\n 'audio_generation',\n 'code_execution',\n 'function_calling',\n 'live_api',\n 'search_grounding',\n 'structured_output',\n 'url_context',\n ],\n },\n pricing: {\n // todo find this info\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n> */\n\nconst GEMINI_2_FLASH_LITE = {\n name: 'gemini-2.0-flash-lite',\n max_input_tokens: 1_048_576,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2024-08-01',\n supports: {\n input: ['text', 'audio', 'video', 'image'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n ],\n tools: [],\n },\n pricing: {\n input: {\n normal: 0.075,\n },\n output: {\n normal: 0.3,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions\n>\n\nconst IMAGEN_4_GENERATE = {\n name: 'imagen-4.0-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst IMAGEN_4_GENERATE_ULTRA = {\n name: 'imagen-4.0-ultra-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.6,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst IMAGEN_4_GENERATE_FAST = {\n name: 'imagen-4.0-fast-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.2,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst IMAGEN_3 = {\n name: 'imagen-3.0-generate-002',\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.03,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n/** \nconst VEO_3_1_PREVIEW = {\n name: 'veo-3.1-generate-preview',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_3_1_FAST_PREVIEW = {\n name: 'veo-3.1-fast-generate-preview',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_3 = {\n name: 'veo-3.0-generate-001',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_3_FAST = {\n name: 'veo-3.0-fast-generate-001',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_2 = {\n name: 'veo-2.0-generate-001',\n max_output_tokens: 2,\n supports: {\n input: ['text', 'image'],\n output: ['video'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.35,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n> */\n\n/* const GEMINI_MODEL_META = {\n [GEMINI_3_PRO.name]: GEMINI_3_PRO,\n [GEMINI_2_5_PRO.name]: GEMINI_2_5_PRO,\n [GEMINI_2_5_PRO_TTS.name]: GEMINI_2_5_PRO_TTS,\n [GEMINI_2_5_FLASH.name]: GEMINI_2_5_FLASH,\n [GEMINI_2_5_FLASH_PREVIEW.name]: GEMINI_2_5_FLASH_PREVIEW,\n [GEMINI_2_5_FLASH_IMAGE.name]: GEMINI_2_5_FLASH_IMAGE,\n [GEMINI_2_5_FLASH_LIVE.name]: GEMINI_2_5_FLASH_LIVE,\n [GEMINI_2_5_FLASH_TTS.name]: GEMINI_2_5_FLASH_TTS,\n [GEMINI_2_5_FLASH_LITE.name]: GEMINI_2_5_FLASH_LITE,\n [GEMINI_2_5_FLASH_LITE_PREVIEW.name]: GEMINI_2_5_FLASH_LITE_PREVIEW,\n [GEMINI_2_FLASH.name]: GEMINI_2_FLASH,\n [GEMINI_2_FLASH_IMAGE.name]: GEMINI_2_FLASH_IMAGE,\n [GEMINI_2_FLASH_LIVE.name]: GEMINI_2_FLASH_LIVE,\n [GEMINI_2_FLASH_LITE.name]: GEMINI_2_FLASH_LITE,\n [IMAGEN_4_GENERATE.name]: IMAGEN_4_GENERATE,\n [IMAGEN_4_GENERATE_ULTRA.name]: IMAGEN_4_GENERATE_ULTRA,\n [IMAGEN_4_GENERATE_FAST.name]: IMAGEN_4_GENERATE_FAST,\n [IMAGEN_3.name]: IMAGEN_3,\n [VEO_3_1_PREVIEW.name]: VEO_3_1_PREVIEW,\n [VEO_3_1_FAST_PREVIEW.name]: VEO_3_1_FAST_PREVIEW,\n [VEO_3.name]: VEO_3,\n [VEO_3_FAST.name]: VEO_3_FAST,\n [VEO_2.name]: VEO_2,\n} as const */\n\nconst GEMINI_3_5_FLASH = {\n name: 'gemini-3.5-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n supports: {\n input: ['text', 'image', 'video', 'document', 'audio'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 1.5,\n cached: 0.15,\n },\n output: {\n normal: 9,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nexport const GEMINI_MODELS = [\n GEMINI_3_1_PRO.name,\n GEMINI_3_PRO.name,\n GEMINI_3_FLASH.name,\n GEMINI_3_1_FLASH_LITE.name,\n GEMINI_2_5_PRO.name,\n GEMINI_2_5_FLASH.name,\n GEMINI_2_5_FLASH_PREVIEW.name,\n GEMINI_2_5_FLASH_LITE.name,\n GEMINI_2_5_FLASH_LITE_PREVIEW.name,\n GEMINI_2_FLASH.name,\n GEMINI_2_FLASH_LITE.name,\n\n GEMINI_3_5_FLASH.name,\n] as const\n\nexport type GeminiModels = (typeof GEMINI_MODELS)[number]\n\nexport type GeminiImageModels = (typeof GEMINI_IMAGE_MODELS)[number]\n\nexport const GEMINI_IMAGE_MODELS = [\n GEMINI_3_1_FLASH_IMAGE.name,\n GEMINI_3_PRO_IMAGE.name,\n GEMINI_2_5_FLASH_IMAGE.name,\n GEMINI_2_FLASH_IMAGE.name,\n IMAGEN_3.name,\n IMAGEN_4_GENERATE.name,\n IMAGEN_4_GENERATE_FAST.name,\n IMAGEN_4_GENERATE_ULTRA.name,\n] as const\n\n/**\n * Text-to-speech models\n * @experimental Gemini TTS is an experimental feature and may change.\n */\nexport const GEMINI_TTS_MODELS = [\n GEMINI_3_1_FLASH_TTS.name,\n GEMINI_2_5_FLASH_TTS.name,\n GEMINI_2_5_PRO_TTS.name,\n] as const\n\n/**\n * Audio generation models (Lyria music generation).\n * @experimental Lyria music generation is an experimental feature and may change.\n */\nexport const GEMINI_AUDIO_MODELS = [\n LYRIA_3_PRO.name,\n LYRIA_3_CLIP.name,\n] as const\n\n/**\n * Available voice names for Gemini TTS\n * @see https://ai.google.dev/gemini-api/docs/speech-generation\n */\nexport const GEMINI_TTS_VOICES = [\n 'Zephyr',\n 'Puck',\n 'Charon',\n 'Kore',\n 'Fenrir',\n 'Leda',\n 'Orus',\n 'Aoede',\n 'Callirrhoe',\n 'Autonoe',\n 'Enceladus',\n 'Iapetus',\n 'Umbriel',\n 'Algieba',\n 'Despina',\n 'Erinome',\n 'Algenib',\n 'Rasalgethi',\n 'Laomedeia',\n 'Achernar',\n 'Alnilam',\n 'Schedar',\n 'Gacrux',\n 'Pulcherrima',\n 'Achird',\n 'Zubenelgenubi',\n 'Vindemiatrix',\n 'Sadachbia',\n 'Sadaltager',\n 'Sulafat',\n] as const\n\nexport type GeminiTTSVoice = (typeof GEMINI_TTS_VOICES)[number]\n\n/* const GEMINI_AUDIO_MODELS = [\n GEMINI_2_5_PRO_TTS.name,\n GEMINI_2_5_FLASH_TTS.name,\n GEMINI_2_5_FLASH_LIVE.name,\n GEMINI_2_FLASH_LIVE.name,\n] as const\n\n const GEMINI_VIDEO_MODELS = [\n VEO_3_1_PREVIEW.name,\n VEO_3_1_FAST_PREVIEW.name,\n VEO_3.name,\n VEO_3_FAST.name,\n VEO_2.name,\n] as const */\n\n// Manual type map for per-model provider options\nexport type GeminiChatModelProviderOptionsByName = {\n // Models with thinking and structured output support\n [GEMINI_3_1_PRO.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_PRO.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_1_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_PRO.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH_PREVIEW.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH_LITE_PREVIEW.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n // Models with structured output but no thinking support\n [GEMINI_2_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions\n [GEMINI_2_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions\n [GEMINI_3_5_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n}\n\n/**\n * Type-only map from chat model name to its supported tool capabilities.\n * Based on the 'supports.tools' arrays defined for each model.\n */\nexport type GeminiChatModelToolCapabilitiesByName = {\n [GEMINI_3_1_PRO.name]: typeof GEMINI_3_1_PRO.supports.tools\n [GEMINI_3_PRO.name]: typeof GEMINI_3_PRO.supports.tools\n [GEMINI_3_FLASH.name]: typeof GEMINI_3_FLASH.supports.tools\n [GEMINI_3_1_FLASH_LITE.name]: typeof GEMINI_3_1_FLASH_LITE.supports.tools\n [GEMINI_2_5_PRO.name]: typeof GEMINI_2_5_PRO.supports.tools\n [GEMINI_2_5_FLASH.name]: typeof GEMINI_2_5_FLASH.supports.tools\n [GEMINI_2_5_FLASH_PREVIEW.name]: typeof GEMINI_2_5_FLASH_PREVIEW.supports.tools\n [GEMINI_2_5_FLASH_LITE.name]: typeof GEMINI_2_5_FLASH_LITE.supports.tools\n [GEMINI_2_5_FLASH_LITE_PREVIEW.name]: typeof GEMINI_2_5_FLASH_LITE_PREVIEW.supports.tools\n [GEMINI_2_FLASH.name]: typeof GEMINI_2_FLASH.supports.tools\n [GEMINI_2_FLASH_LITE.name]: typeof GEMINI_2_FLASH_LITE.supports.tools\n}\n\n/**\n * Type-only map from chat model name to its supported input modalities.\n * Based on the 'supports.input' arrays defined for each model.\n * Note: 'document' in the model meta is mapped to 'document' modality.\n * Used by the core AI types to constrain ContentPart types based on the selected model.\n * Note: These must be inlined as readonly arrays (not typeof) because the model\n * constants are not exported and typeof references don't work in .d.ts files\n * when consumed by external packages.\n *\n * @see https://ai.google.dev/gemini-api/docs/vision\n * @see https://ai.google.dev/gemini-api/docs/audio\n * @see https://ai.google.dev/gemini-api/docs/document-processing\n */\nexport type GeminiModelInputModalitiesByName = {\n // Models with full multimodal support (text, image, audio, video, document)\n [GEMINI_3_1_PRO.name]: typeof GEMINI_3_1_PRO.supports.input\n [GEMINI_3_PRO.name]: typeof GEMINI_3_PRO.supports.input\n [GEMINI_3_FLASH.name]: typeof GEMINI_3_FLASH.supports.input\n [GEMINI_3_1_FLASH_LITE.name]: typeof GEMINI_3_1_FLASH_LITE.supports.input\n [GEMINI_2_5_PRO.name]: typeof GEMINI_2_5_PRO.supports.input\n [GEMINI_2_5_FLASH_LITE.name]: typeof GEMINI_2_5_FLASH_LITE.supports.input\n [GEMINI_2_5_FLASH_LITE_PREVIEW.name]: typeof GEMINI_2_5_FLASH_LITE_PREVIEW.supports.input\n\n // Models with text, image, audio, video (no document)\n [GEMINI_2_5_FLASH.name]: typeof GEMINI_2_5_FLASH.supports.input\n [GEMINI_2_5_FLASH_PREVIEW.name]: typeof GEMINI_2_5_FLASH_PREVIEW.supports.input\n [GEMINI_2_FLASH.name]: typeof GEMINI_2_FLASH.supports.input\n [GEMINI_2_FLASH_LITE.name]: typeof GEMINI_2_FLASH_LITE.supports.input\n [GEMINI_3_5_FLASH.name]: typeof GEMINI_3_5_FLASH.supports.input\n}\n"],"names":[],"mappings":"AAmDA,MAAM,iBAAiB;AAAA,EACrB,MAAM;AAwBR;AASA,MAAM,eAAe;AAAA,EACnB,MAAM;AAwBR;AASA,MAAM,iBAAiB;AAAA,EACrB,MAAM;AAwBR;AASA,MAAM,qBAAqB;AAAA,EACzB,MAAM;AAkBR;AASA,MAAM,yBAAyB;AAAA,EAC7B,MAAM;AAkBR;AASA,MAAM,wBAAwB;AAAA,EAC5B,MAAM;AAwBR;AASA,MAAM,iBAAiB;AAAA,EACrB,MAAM;AA8BR;AASA,MAAM,qBAAqB;AAAA,EACzB,MAAM;AAiBR;AAOA,MAAM,mBAAmB;AAAA,EACvB,MAAM;AA8BR;AASA,MAAM,2BAA2B;AAAA,EAC/B,MAAM;AAwBR;AASA,MAAM,yBAAyB;AAAA,EAC7B,MAAM;AAkBR;AAyCA,MAAM,uBAAuB;AAAA,EAC3B,MAAM;AAiBR;AAYA,MAAM,uBAAuB;AAAA,EAC3B,MAAM;AAiBR;AAaA,MAAM,cAAc;AAAA,EAClB,MAAM;AAeR;AAMA,MAAM,eAAe;AAAA,EACnB,MAAM;AAeR;AAEA,MAAM,wBAAwB;AAAA,EAC5B,MAAM;AAwBR;AASA,MAAM,gCAAgC;AAAA,EACpC,MAAM;AAwBR;AASA,MAAM,iBAAiB;AAAA,EACrB,MAAM;AAwBR;AAQA,MAAM,uBAAuB;AAAA,EAC3B,MAAM;AAkBR;AAyCA,MAAM,sBAAsB;AAAA,EAC1B,MAAM;AAuBR;AAQA,MAAM,oBAAoB;AAAA,EACxB,MAAM;AAeR;AAOA,MAAM,0BAA0B;AAAA,EAC9B,MAAM;AAeR;AAOA,MAAM,yBAAyB;AAAA,EAC7B,MAAM;AAeR;AAOA,MAAM,WAAW;AAAA,EACf,MAAM;AAcR;AAmJA,MAAM,mBAAmB;AAAA,EACvB,MAAM;AAwBR;AASO,MAAM,gBAAgB;AAAA,EAC3B,eAAe;AAAA,EACf,aAAa;AAAA,EACb,eAAe;AAAA,EACf,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,yBAAyB;AAAA,EACzB,sBAAsB;AAAA,EACtB,8BAA8B;AAAA,EAC9B,eAAe;AAAA,EACf,oBAAoB;AAAA,EAEpB,iBAAiB;AACnB;AAMO,MAAM,sBAAsB;AAAA,EACjC,uBAAuB;AAAA,EACvB,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,SAAS;AAAA,EACT,kBAAkB;AAAA,EAClB,uBAAuB;AAAA,EACvB,wBAAwB;AAC1B;AAMO,MAAM,oBAAoB;AAAA,EAC/B,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,mBAAmB;AACrB;AAMO,MAAM,sBAAsB;AAAA,EACjC,YAAY;AAAA,EACZ,aAAa;AACf;AAMO,MAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;"}
1
+ {"version":3,"file":"model-meta.js","sources":["../../src/model-meta.ts"],"sourcesContent":["import type {\n GeminiCachedContentOptions,\n GeminiCommonConfigOptions,\n GeminiSafetyOptions,\n GeminiStructuredOutputOptions,\n GeminiThinkingOptions,\n GeminiToolConfigOptions,\n} from './text/text-provider-options'\n\ninterface ModelMeta<TProviderOptions = unknown> {\n name: string\n supports: {\n input: Array<'text' | 'image' | 'audio' | 'video' | 'document'>\n output: Array<'text' | 'image' | 'audio' | 'video'>\n capabilities?: Array<\n | 'audio_generation'\n | 'batch_api'\n | 'caching'\n | 'function_calling'\n | 'live_api'\n | 'structured_output'\n | 'thinking'\n >\n tools?: Array<\n | 'code_execution'\n | 'file_search'\n | 'google_search'\n | 'google_search_retrieval'\n | 'google_maps'\n | 'url_context'\n | 'computer_use'\n >\n }\n max_input_tokens?: number\n max_output_tokens?: number\n knowledge_cutoff?: string\n pricing?: {\n input: {\n normal: number\n cached?: number\n }\n output: {\n normal: number\n }\n }\n /**\n * Type-level description of which provider options this model supports.\n */\n providerOptions?: TProviderOptions\n}\n\nconst GEMINI_3_1_PRO = {\n name: 'gemini-3.1-pro-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_PRO = {\n name: 'gemini-3-pro-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_FLASH = {\n name: 'gemini-3-flash-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.5,\n },\n output: {\n normal: 3,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_PRO_IMAGE = {\n name: 'gemini-3-pro-image-preview',\n max_input_tokens: 65_536,\n max_output_tokens: 32_768,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 2,\n },\n output: {\n normal: 0.134,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_1_FLASH_IMAGE = {\n name: 'gemini-3.1-flash-image-preview',\n max_input_tokens: 65_536,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_1_FLASH_LITE = {\n name: 'gemini-3.1-flash-lite-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_PRO = {\n name: 'gemini-2.5-pro',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_maps',\n 'google_search',\n 'url_context',\n ],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_PRO_TTS = {\n name: 'gemini-2.5-pro-preview-tts',\n max_input_tokens: 8_192,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst GEMINI_2_5_FLASH = {\n name: 'gemini-2.5-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_maps',\n 'google_search',\n 'url_context',\n ],\n },\n pricing: {\n input: {\n normal: 1,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_FLASH_PREVIEW = {\n name: 'gemini-2.5-flash-preview-09-2025',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 1,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_FLASH_IMAGE = {\n name: 'gemini-2.5-flash-image',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-06-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'caching', 'structured_output'],\n tools: ['file_search'],\n },\n pricing: {\n input: {\n normal: 0.3,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n/**\nconst GEMINI_2_5_FLASH_LIVE = {\n name: 'gemini-2.5-flash-native-audio-preview-09-2025',\n max_input_tokens: 141_072,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'audio', 'video'],\n output: ['text', 'audio'],\n capabilities: [\n 'audio_generation',\n 'file_search',\n 'function_calling',\n 'live_api',\n 'search_grounding',\n 'thinking',\n ],\n },\n pricing: {\n // todo find this info\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions &\n GeminiThinkingOptions\n>\n*/\nconst GEMINI_2_5_FLASH_TTS = {\n name: 'gemini-2.5-flash-preview-tts',\n max_input_tokens: 8_192,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation', 'batch_api'],\n },\n pricing: {\n input: {\n normal: 1,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Gemini 3.1 Flash TTS Preview - latest expressive TTS model with\n * 200+ audio tags, 70+ languages, and multi-speaker dialogue support.\n * @see https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-tts-preview\n */\nconst GEMINI_3_1_FLASH_TTS = {\n name: 'gemini-3.1-flash-tts-preview',\n max_input_tokens: 32_768,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation', 'batch_api'],\n },\n pricing: {\n input: {\n normal: 0.5,\n },\n output: {\n normal: 10,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Lyria 3 Pro Preview — Google's flagship music generation model.\n * Generates full-length songs with multiple verses, choruses, and bridges.\n * Outputs MP3 or WAV at 48 kHz stereo.\n * @see https://ai.google.dev/gemini-api/docs/models/lyria-3-pro-preview\n */\nconst LYRIA_3_PRO = {\n name: 'lyria-3-pro-preview',\n max_input_tokens: 131_072,\n supports: {\n input: ['text', 'image'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta\n\n/**\n * Lyria 3 Clip Preview — 30-second music clips in MP3 format.\n * @see https://ai.google.dev/gemini-api/docs/music-generation\n */\nconst LYRIA_3_CLIP = {\n name: 'lyria-3-clip-preview',\n max_input_tokens: 131_072,\n supports: {\n input: ['text', 'image'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta\n\nconst GEMINI_2_5_FLASH_LITE = {\n name: 'gemini-2.5-flash-lite',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'google_maps', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.1,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_FLASH_LITE_PREVIEW = {\n name: 'gemini-2.5-flash-lite-preview-09-2025',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.1,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_FLASH = {\n name: 'gemini-2.0-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2024-08-01',\n supports: {\n input: ['text', 'image', 'audio', 'video'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'live_api',\n 'structured_output',\n ],\n tools: ['code_execution', 'google_maps', 'google_search'],\n },\n pricing: {\n input: {\n normal: 0.1,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions\n>\n\nconst GEMINI_2_FLASH_IMAGE = {\n name: 'gemini-2.0-flash-preview-image-generation',\n max_input_tokens: 32_768,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2024-08-01',\n supports: {\n input: ['text', 'image', 'audio', 'video'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'caching', 'structured_output'],\n tools: [],\n },\n pricing: {\n input: {\n normal: 0.1,\n },\n output: {\n normal: 0.039,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n/* \nconst GEMINI_2_FLASH_LIVE = {\n name: 'gemini-2.0-flash-live-001',\n max_input_tokens: 1_048_576,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2024-08-01',\n supports: {\n input: ['text', 'audio', 'video'],\n output: ['text', 'audio'],\n capabilities: [\n 'audio_generation',\n 'code_execution',\n 'function_calling',\n 'live_api',\n 'search_grounding',\n 'structured_output',\n 'url_context',\n ],\n },\n pricing: {\n // todo find this info\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n> */\n\nconst GEMINI_2_FLASH_LITE = {\n name: 'gemini-2.0-flash-lite',\n max_input_tokens: 1_048_576,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2024-08-01',\n supports: {\n input: ['text', 'audio', 'video', 'image'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n ],\n tools: [],\n },\n pricing: {\n input: {\n normal: 0.075,\n },\n output: {\n normal: 0.3,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions\n>\n\nconst IMAGEN_4_GENERATE = {\n name: 'imagen-4.0-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst IMAGEN_4_GENERATE_ULTRA = {\n name: 'imagen-4.0-ultra-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.6,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst IMAGEN_4_GENERATE_FAST = {\n name: 'imagen-4.0-fast-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.2,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst IMAGEN_3 = {\n name: 'imagen-3.0-generate-002',\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.03,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n/** \nconst VEO_3_1_PREVIEW = {\n name: 'veo-3.1-generate-preview',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_3_1_FAST_PREVIEW = {\n name: 'veo-3.1-fast-generate-preview',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_3 = {\n name: 'veo-3.0-generate-001',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_3_FAST = {\n name: 'veo-3.0-fast-generate-001',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_2 = {\n name: 'veo-2.0-generate-001',\n max_output_tokens: 2,\n supports: {\n input: ['text', 'image'],\n output: ['video'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.35,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions\n> */\n\n/* const GEMINI_MODEL_META = {\n [GEMINI_3_PRO.name]: GEMINI_3_PRO,\n [GEMINI_2_5_PRO.name]: GEMINI_2_5_PRO,\n [GEMINI_2_5_PRO_TTS.name]: GEMINI_2_5_PRO_TTS,\n [GEMINI_2_5_FLASH.name]: GEMINI_2_5_FLASH,\n [GEMINI_2_5_FLASH_PREVIEW.name]: GEMINI_2_5_FLASH_PREVIEW,\n [GEMINI_2_5_FLASH_IMAGE.name]: GEMINI_2_5_FLASH_IMAGE,\n [GEMINI_2_5_FLASH_LIVE.name]: GEMINI_2_5_FLASH_LIVE,\n [GEMINI_2_5_FLASH_TTS.name]: GEMINI_2_5_FLASH_TTS,\n [GEMINI_2_5_FLASH_LITE.name]: GEMINI_2_5_FLASH_LITE,\n [GEMINI_2_5_FLASH_LITE_PREVIEW.name]: GEMINI_2_5_FLASH_LITE_PREVIEW,\n [GEMINI_2_FLASH.name]: GEMINI_2_FLASH,\n [GEMINI_2_FLASH_IMAGE.name]: GEMINI_2_FLASH_IMAGE,\n [GEMINI_2_FLASH_LIVE.name]: GEMINI_2_FLASH_LIVE,\n [GEMINI_2_FLASH_LITE.name]: GEMINI_2_FLASH_LITE,\n [IMAGEN_4_GENERATE.name]: IMAGEN_4_GENERATE,\n [IMAGEN_4_GENERATE_ULTRA.name]: IMAGEN_4_GENERATE_ULTRA,\n [IMAGEN_4_GENERATE_FAST.name]: IMAGEN_4_GENERATE_FAST,\n [IMAGEN_3.name]: IMAGEN_3,\n [VEO_3_1_PREVIEW.name]: VEO_3_1_PREVIEW,\n [VEO_3_1_FAST_PREVIEW.name]: VEO_3_1_FAST_PREVIEW,\n [VEO_3.name]: VEO_3,\n [VEO_3_FAST.name]: VEO_3_FAST,\n [VEO_2.name]: VEO_2,\n} as const */\n\nconst GEMINI_3_5_FLASH = {\n name: 'gemini-3.5-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n supports: {\n input: ['text', 'image', 'video', 'document', 'audio'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 1.5,\n cached: 0.15,\n },\n output: {\n normal: 9,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nexport const GEMINI_MODELS = [\n GEMINI_3_1_PRO.name,\n GEMINI_3_PRO.name,\n GEMINI_3_FLASH.name,\n GEMINI_3_1_FLASH_LITE.name,\n GEMINI_2_5_PRO.name,\n GEMINI_2_5_FLASH.name,\n GEMINI_2_5_FLASH_PREVIEW.name,\n GEMINI_2_5_FLASH_LITE.name,\n GEMINI_2_5_FLASH_LITE_PREVIEW.name,\n GEMINI_2_FLASH.name,\n GEMINI_2_FLASH_LITE.name,\n\n GEMINI_3_5_FLASH.name,\n] as const\n\n/**\n * Gemini models that support combining `tools` + `responseSchema` in a\n * single streaming `generateContent` call (per issue #605). Per the\n * provider matrix, Gemini 3.x natively interleaves the schema-constrained\n * answer with function-calling on one pass; Gemini 2.x is unsupported /\n * brittle and keeps the engine's legacy finalization fallback.\n */\nexport const GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS = new Set<string>([\n GEMINI_3_1_PRO.name,\n GEMINI_3_PRO.name,\n GEMINI_3_FLASH.name,\n GEMINI_3_1_FLASH_LITE.name,\n GEMINI_3_5_FLASH.name,\n])\n\nexport type GeminiModels = (typeof GEMINI_MODELS)[number]\n\nexport type GeminiImageModels = (typeof GEMINI_IMAGE_MODELS)[number]\n\nexport const GEMINI_IMAGE_MODELS = [\n GEMINI_3_1_FLASH_IMAGE.name,\n GEMINI_3_PRO_IMAGE.name,\n GEMINI_2_5_FLASH_IMAGE.name,\n GEMINI_2_FLASH_IMAGE.name,\n IMAGEN_3.name,\n IMAGEN_4_GENERATE.name,\n IMAGEN_4_GENERATE_FAST.name,\n IMAGEN_4_GENERATE_ULTRA.name,\n] as const\n\n/**\n * Text-to-speech models\n * @experimental Gemini TTS is an experimental feature and may change.\n */\nexport const GEMINI_TTS_MODELS = [\n GEMINI_3_1_FLASH_TTS.name,\n GEMINI_2_5_FLASH_TTS.name,\n GEMINI_2_5_PRO_TTS.name,\n] as const\n\n/**\n * Audio generation models (Lyria music generation).\n * @experimental Lyria music generation is an experimental feature and may change.\n */\nexport const GEMINI_AUDIO_MODELS = [\n LYRIA_3_PRO.name,\n LYRIA_3_CLIP.name,\n] as const\n\n/**\n * Available voice names for Gemini TTS\n * @see https://ai.google.dev/gemini-api/docs/speech-generation\n */\nexport const GEMINI_TTS_VOICES = [\n 'Zephyr',\n 'Puck',\n 'Charon',\n 'Kore',\n 'Fenrir',\n 'Leda',\n 'Orus',\n 'Aoede',\n 'Callirrhoe',\n 'Autonoe',\n 'Enceladus',\n 'Iapetus',\n 'Umbriel',\n 'Algieba',\n 'Despina',\n 'Erinome',\n 'Algenib',\n 'Rasalgethi',\n 'Laomedeia',\n 'Achernar',\n 'Alnilam',\n 'Schedar',\n 'Gacrux',\n 'Pulcherrima',\n 'Achird',\n 'Zubenelgenubi',\n 'Vindemiatrix',\n 'Sadachbia',\n 'Sadaltager',\n 'Sulafat',\n] as const\n\nexport type GeminiTTSVoice = (typeof GEMINI_TTS_VOICES)[number]\n\n/* const GEMINI_AUDIO_MODELS = [\n GEMINI_2_5_PRO_TTS.name,\n GEMINI_2_5_FLASH_TTS.name,\n GEMINI_2_5_FLASH_LIVE.name,\n GEMINI_2_FLASH_LIVE.name,\n] as const\n\n const GEMINI_VIDEO_MODELS = [\n VEO_3_1_PREVIEW.name,\n VEO_3_1_FAST_PREVIEW.name,\n VEO_3.name,\n VEO_3_FAST.name,\n VEO_2.name,\n] as const */\n\n// Manual type map for per-model provider options\nexport type GeminiChatModelProviderOptionsByName = {\n // Models with thinking and structured output support\n [GEMINI_3_1_PRO.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_PRO.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_1_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_PRO.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH_PREVIEW.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH_LITE_PREVIEW.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n // Models with structured output but no thinking support\n [GEMINI_2_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions\n [GEMINI_2_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions\n [GEMINI_3_5_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n}\n\n/**\n * Type-only map from chat model name to its supported tool capabilities.\n * Based on the 'supports.tools' arrays defined for each model.\n */\nexport type GeminiChatModelToolCapabilitiesByName = {\n [GEMINI_3_1_PRO.name]: typeof GEMINI_3_1_PRO.supports.tools\n [GEMINI_3_PRO.name]: typeof GEMINI_3_PRO.supports.tools\n [GEMINI_3_FLASH.name]: typeof GEMINI_3_FLASH.supports.tools\n [GEMINI_3_1_FLASH_LITE.name]: typeof GEMINI_3_1_FLASH_LITE.supports.tools\n [GEMINI_2_5_PRO.name]: typeof GEMINI_2_5_PRO.supports.tools\n [GEMINI_2_5_FLASH.name]: typeof GEMINI_2_5_FLASH.supports.tools\n [GEMINI_2_5_FLASH_PREVIEW.name]: typeof GEMINI_2_5_FLASH_PREVIEW.supports.tools\n [GEMINI_2_5_FLASH_LITE.name]: typeof GEMINI_2_5_FLASH_LITE.supports.tools\n [GEMINI_2_5_FLASH_LITE_PREVIEW.name]: typeof GEMINI_2_5_FLASH_LITE_PREVIEW.supports.tools\n [GEMINI_2_FLASH.name]: typeof GEMINI_2_FLASH.supports.tools\n [GEMINI_2_FLASH_LITE.name]: typeof GEMINI_2_FLASH_LITE.supports.tools\n}\n\n/**\n * Type-only map from chat model name to its supported input modalities.\n * Based on the 'supports.input' arrays defined for each model.\n * Note: 'document' in the model meta is mapped to 'document' modality.\n * Used by the core AI types to constrain ContentPart types based on the selected model.\n * Note: These must be inlined as readonly arrays (not typeof) because the model\n * constants are not exported and typeof references don't work in .d.ts files\n * when consumed by external packages.\n *\n * @see https://ai.google.dev/gemini-api/docs/vision\n * @see https://ai.google.dev/gemini-api/docs/audio\n * @see https://ai.google.dev/gemini-api/docs/document-processing\n */\nexport type GeminiModelInputModalitiesByName = {\n // Models with full multimodal support (text, image, audio, video, document)\n [GEMINI_3_1_PRO.name]: typeof GEMINI_3_1_PRO.supports.input\n [GEMINI_3_PRO.name]: typeof GEMINI_3_PRO.supports.input\n [GEMINI_3_FLASH.name]: typeof GEMINI_3_FLASH.supports.input\n [GEMINI_3_1_FLASH_LITE.name]: typeof GEMINI_3_1_FLASH_LITE.supports.input\n [GEMINI_2_5_PRO.name]: typeof GEMINI_2_5_PRO.supports.input\n [GEMINI_2_5_FLASH_LITE.name]: typeof GEMINI_2_5_FLASH_LITE.supports.input\n [GEMINI_2_5_FLASH_LITE_PREVIEW.name]: typeof GEMINI_2_5_FLASH_LITE_PREVIEW.supports.input\n\n // Models with text, image, audio, video (no document)\n [GEMINI_2_5_FLASH.name]: typeof GEMINI_2_5_FLASH.supports.input\n [GEMINI_2_5_FLASH_PREVIEW.name]: typeof GEMINI_2_5_FLASH_PREVIEW.supports.input\n [GEMINI_2_FLASH.name]: typeof GEMINI_2_FLASH.supports.input\n [GEMINI_2_FLASH_LITE.name]: typeof GEMINI_2_FLASH_LITE.supports.input\n [GEMINI_3_5_FLASH.name]: typeof GEMINI_3_5_FLASH.supports.input\n}\n"],"names":[],"mappings":"AAmDA,MAAM,iBAAiB;AAAA,EACrB,MAAM;AAwBR;AASA,MAAM,eAAe;AAAA,EACnB,MAAM;AAwBR;AASA,MAAM,iBAAiB;AAAA,EACrB,MAAM;AAwBR;AASA,MAAM,qBAAqB;AAAA,EACzB,MAAM;AAkBR;AASA,MAAM,yBAAyB;AAAA,EAC7B,MAAM;AAkBR;AASA,MAAM,wBAAwB;AAAA,EAC5B,MAAM;AAwBR;AASA,MAAM,iBAAiB;AAAA,EACrB,MAAM;AA8BR;AASA,MAAM,qBAAqB;AAAA,EACzB,MAAM;AAiBR;AAOA,MAAM,mBAAmB;AAAA,EACvB,MAAM;AA8BR;AASA,MAAM,2BAA2B;AAAA,EAC/B,MAAM;AAwBR;AASA,MAAM,yBAAyB;AAAA,EAC7B,MAAM;AAkBR;AAyCA,MAAM,uBAAuB;AAAA,EAC3B,MAAM;AAiBR;AAYA,MAAM,uBAAuB;AAAA,EAC3B,MAAM;AAiBR;AAaA,MAAM,cAAc;AAAA,EAClB,MAAM;AAeR;AAMA,MAAM,eAAe;AAAA,EACnB,MAAM;AAeR;AAEA,MAAM,wBAAwB;AAAA,EAC5B,MAAM;AAwBR;AASA,MAAM,gCAAgC;AAAA,EACpC,MAAM;AAwBR;AASA,MAAM,iBAAiB;AAAA,EACrB,MAAM;AAwBR;AAQA,MAAM,uBAAuB;AAAA,EAC3B,MAAM;AAkBR;AAyCA,MAAM,sBAAsB;AAAA,EAC1B,MAAM;AAuBR;AAQA,MAAM,oBAAoB;AAAA,EACxB,MAAM;AAeR;AAOA,MAAM,0BAA0B;AAAA,EAC9B,MAAM;AAeR;AAOA,MAAM,yBAAyB;AAAA,EAC7B,MAAM;AAeR;AAOA,MAAM,WAAW;AAAA,EACf,MAAM;AAcR;AAmJA,MAAM,mBAAmB;AAAA,EACvB,MAAM;AAwBR;AASO,MAAM,gBAAgB;AAAA,EAC3B,eAAe;AAAA,EACf,aAAa;AAAA,EACb,eAAe;AAAA,EACf,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,yBAAyB;AAAA,EACzB,sBAAsB;AAAA,EACtB,8BAA8B;AAAA,EAC9B,eAAe;AAAA,EACf,oBAAoB;AAAA,EAEpB,iBAAiB;AACnB;AASO,MAAM,8DAA8C,IAAY;AAAA,EACrE,eAAe;AAAA,EACf,aAAa;AAAA,EACb,eAAe;AAAA,EACf,sBAAsB;AAAA,EACtB,iBAAiB;AACnB,CAAC;AAMM,MAAM,sBAAsB;AAAA,EACjC,uBAAuB;AAAA,EACvB,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,SAAS;AAAA,EACT,kBAAkB;AAAA,EAClB,uBAAuB;AAAA,EACvB,wBAAwB;AAC1B;AAMO,MAAM,oBAAoB;AAAA,EAC/B,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,mBAAmB;AACrB;AAMO,MAAM,sBAAsB;AAAA,EACjC,YAAY;AAAA,EACZ,aAAa;AACf;AAMO,MAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/ai-gemini",
3
- "version": "0.10.9",
4
- "description": "Google Gemini adapter for TanStack AI",
3
+ "version": "0.11.0",
4
+ "description": "Google Gemini adapter for TanStack AI chat, images, speech, audio generation, and structured outputs.",
5
5
  "author": "",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -28,23 +28,31 @@
28
28
  ],
29
29
  "keywords": [
30
30
  "ai",
31
+ "ai-sdk",
32
+ "typescript",
33
+ "tanstack",
31
34
  "gemini",
32
35
  "google",
33
- "tanstack",
34
- "adapter"
36
+ "adapter",
37
+ "chat",
38
+ "tool-calling",
39
+ "structured-outputs",
40
+ "image-generation",
41
+ "audio-generation",
42
+ "text-to-speech"
35
43
  ],
36
44
  "dependencies": {
37
45
  "@google/genai": "^1.43.0",
38
- "@tanstack/ai-utils": "0.2.0"
46
+ "@tanstack/ai-utils": "0.2.1"
39
47
  },
40
48
  "peerDependencies": {
41
- "@tanstack/ai": "^0.21.0"
49
+ "@tanstack/ai": "^0.22.0"
42
50
  },
43
51
  "devDependencies": {
44
52
  "@vitest/coverage-v8": "4.0.14",
45
53
  "vite": "^7.3.3",
46
54
  "zod": "^4.2.0",
47
- "@tanstack/ai": "0.21.0"
55
+ "@tanstack/ai": "0.22.0"
48
56
  },
49
57
  "scripts": {
50
58
  "build": "vite build",
@@ -7,6 +7,7 @@ import {
7
7
  generateId,
8
8
  getGeminiApiKeyFromEnv,
9
9
  } from '../utils'
10
+ import { GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS } from '../model-meta'
10
11
  import type {
11
12
  GEMINI_MODELS,
12
13
  GeminiChatModelProviderOptionsByName,
@@ -857,6 +858,25 @@ export class GeminiTextAdapter<
857
858
  ? normalizedPrompts.map((p) => p.content).join('\n')
858
859
  : undefined
859
860
 
861
+ // Native combined mode (issue #605): when the engine threads
862
+ // `outputSchema` through TextOptions, the adapter declared
863
+ // `supportsCombinedToolsAndSchema` (Gemini 3.x only). The schema is
864
+ // already JSON Schema (pre-converted at the activity boundary). Wire
865
+ // it into `config.responseSchema` + `responseMimeType: 'application/json'`
866
+ // alongside any `tools` — the model emits function calls during the
867
+ // agent loop and the schema-constrained JSON on its natural final
868
+ // turn, so the engine can harvest it without the separate
869
+ // `structuredOutput` finalization round-trip.
870
+ const combinedSchema = options.outputSchema as
871
+ | Record<string, unknown>
872
+ | undefined
873
+ const combinedSchemaConfig = combinedSchema
874
+ ? {
875
+ responseMimeType: 'application/json' as const,
876
+ responseSchema: combinedSchema,
877
+ }
878
+ : undefined
879
+
860
880
  // Vendor `GenerateContentConfig` fields are `field?: T` (no `| undefined`)
861
881
  // under EOPT, so spread each common option only when present rather than
862
882
  // emitting `field: undefined`s into the wire payload.
@@ -877,11 +897,22 @@ export class GeminiTextAdapter<
877
897
  }),
878
898
  ...(systemInstruction !== undefined && { systemInstruction }),
879
899
  tools: convertToolsToProviderFormat(options.tools),
900
+ ...(combinedSchemaConfig ?? {}),
880
901
  },
881
902
  }
882
903
 
883
904
  return requestOptions
884
905
  }
906
+
907
+ /**
908
+ * Gemini 3.x natively combines `tools` + `responseSchema` in a single
909
+ * streaming `generateContentStream` call (issue #605). Gemini 2.x is
910
+ * documented as brittle for the combination and keeps the engine's
911
+ * legacy finalization path.
912
+ */
913
+ supportsCombinedToolsAndSchema(): boolean {
914
+ return GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS.has(this.model)
915
+ }
885
916
  }
886
917
 
887
918
  /**
package/src/index.ts CHANGED
@@ -62,7 +62,10 @@ export {
62
62
  } from './adapters/audio'
63
63
 
64
64
  // Re-export models from model-meta for convenience
65
- export { GEMINI_MODELS } from './model-meta'
65
+ export {
66
+ GEMINI_MODELS,
67
+ GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS,
68
+ } from './model-meta'
66
69
  export { GEMINI_MODELS as GeminiTextModels } from './model-meta'
67
70
  export { GEMINI_IMAGE_MODELS as GeminiImageModels } from './model-meta'
68
71
  export { GEMINI_TTS_MODELS as GeminiTTSModels } from './model-meta'
package/src/model-meta.ts CHANGED
@@ -1015,6 +1015,21 @@ export const GEMINI_MODELS = [
1015
1015
  GEMINI_3_5_FLASH.name,
1016
1016
  ] as const
1017
1017
 
1018
+ /**
1019
+ * Gemini models that support combining `tools` + `responseSchema` in a
1020
+ * single streaming `generateContent` call (per issue #605). Per the
1021
+ * provider matrix, Gemini 3.x natively interleaves the schema-constrained
1022
+ * answer with function-calling on one pass; Gemini 2.x is unsupported /
1023
+ * brittle and keeps the engine's legacy finalization fallback.
1024
+ */
1025
+ export const GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS = new Set<string>([
1026
+ GEMINI_3_1_PRO.name,
1027
+ GEMINI_3_PRO.name,
1028
+ GEMINI_3_FLASH.name,
1029
+ GEMINI_3_1_FLASH_LITE.name,
1030
+ GEMINI_3_5_FLASH.name,
1031
+ ])
1032
+
1018
1033
  export type GeminiModels = (typeof GEMINI_MODELS)[number]
1019
1034
 
1020
1035
  export type GeminiImageModels = (typeof GEMINI_IMAGE_MODELS)[number]