The Gemini provider supports six modalities: text/chat, image generation, video generation, TTS, ASR, and embeddings. Language models are auto-discovered from the API; every other modality uses a hand-maintained array in the provider file.
Audience: coding agents and contributors who need to add a new Gemini, Imagen, or Veo model to NodeTool.
TL;DR
- Text/chat models: nothing to do — they are fetched live from
GET /v1beta/models. - Image models (Imagen +
gemini-*image generation): add one entry togetAvailableImageModels()in the provider. - Video models (Veo): add one entry to
getAvailableVideoModels(). - TTS / ASR / embedding models: add one entry to the matching method.
- Run
npm run checkbefore committing.
Where things live
| Concern | Path |
|---|---|
| Provider (all Gemini logic) | packages/runtime/src/providers/gemini-provider.ts |
| Language model listing (dynamic) | GeminiProvider.getAvailableLanguageModels() |
| Image model listing (static) | GeminiProvider.getAvailableImageModels() |
| TTS model listing (static) | GeminiProvider.getAvailableTTSModels() |
| ASR model listing (static) | GeminiProvider.getAvailableASRModels() |
| Video model listing (static) | GeminiProvider.getAvailableVideoModels() |
| Embedding model listing (static) | GeminiProvider.getAvailableEmbeddingModels() |
| Token/chat cost | @pydantic/genai-prices catalog (automatic — no edit needed) |
| Non-token cost tiers | packages/runtime/src/providers/cost-calculator.ts — PRICING_TIERS / MODEL_TO_TIER |
| Provider registration | packages/runtime/src/providers/index.ts line 211 |
How Gemini models are defined
Language models — dynamic
getAvailableLanguageModels() calls GET https://generativelanguage.googleapis.com/v1beta/models?key=<GEMINI_API_KEY>, filters entries whose supportedGenerationMethods includes "generateContent", and maps each to { id, name, provider: "gemini" }. A new text/chat model becomes available when Google adds it to that endpoint.
Image models — static array
getAvailableImageModels() returns a hardcoded array. Two dispatch paths exist inside textToImage():
- IDs starting with
"gemini-"— callPOST /models/<id>:generateContentwithresponseModalities: ["IMAGE", "TEXT"]. - All other IDs (Imagen:
"imagen-*") callPOST /models/<id>:predict.
Video models — static array
getAvailableVideoModels() returns a hardcoded array. Both textToVideo() and imageToVideo() require a veo-* model ID. Veo calls use the async predictLongRunning endpoint with polling.
TTS / ASR / Embedding — static arrays
Each is a simple array returned from the matching getAvailable* method. TTS models carry a voices field. Embedding models carry a dimensions field.
Add a new model
1. Text/chat model
Nothing to do. The model appears automatically once Google adds it to the list API. Verify it shows up:
curl "https://generativelanguage.googleapis.com/v1beta/models?key=$GEMINI_API_KEY" \
| jq '[.models[] | select(.supportedGenerationMethods[] | contains("generateContent")) | .name]'
Token/chat cost is priced through @pydantic/genai-prices. NodeTool maps the "gemini" provider to "google" in GENAI_PROVIDER_MAP.
2. Image model
Open packages/runtime/src/providers/gemini-provider.ts and add an entry to getAvailableImageModels():
async getAvailableImageModels(): Promise<ImageModel[]> {
return [
{
id: "gemini-3.1-flash-image",
name: "Gemini 3.1 Flash Image",
provider: "gemini"
},
{ id: "gemini-3-pro-image", name: "Gemini 3 Pro Image", provider: "gemini" }
];
}
The "gemini-" prefix uses the native generateContent endpoint. Legacy Imagen IDs use predict. Check Google’s deprecation table before adding an Imagen model.
3. Gemini native image model (gemini-*)
Same as above, but use a "gemini-" prefixed ID. The textToImage() dispatcher routes it to generateContent with responseModalities: ["IMAGE", "TEXT"] automatically.
Use the exact ID returned by Google. Do not add guessed future IDs.
4. Veo video model
Open getAvailableVideoModels() and add an entry:
override async getAvailableVideoModels(): Promise<VideoModel[]> {
return [
{ id: "veo-3.1-generate-preview", name: "Veo 3.1 Preview", provider: "gemini" },
{ id: "veo-3.1-fast-generate-preview", name: "Veo 3.1 Fast Preview", provider: "gemini" },
{ id: "veo-3.1-lite-generate-preview", name: "Veo 3.1 Lite Preview", provider: "gemini" }
];
}
Veo model IDs must start with "veo-". Check supported durations and resolutions for each variant before adding it.
5. ASR model
async getAvailableASRModels(): Promise<ASRModel[]> {
return [
{ id: "gemini-3.5-flash", name: "Gemini 3.5 Flash", provider: "gemini" },
{ id: "gemini-3.1-flash-lite", name: "Gemini 3.1 Flash-Lite", provider: "gemini" }
];
}
6. TTS model
Add the model and its supported voices:
async getAvailableTTSModels(): Promise<TTSModel[]> {
const voices = ["Zephyr", "Puck" /*, ... existing voices ... */];
return [
{ id: "gemini-3.1-flash-tts-preview", name: "Gemini 3.1 Flash TTS Preview", provider: "gemini", voices }
];
}
7. Embedding model
async getAvailableEmbeddingModels(): Promise<EmbeddingModel[]> {
return [
{ id: "gemini-embedding-2", name: "Gemini Embedding 2", provider: "gemini", dimensions: 3072 }
];
}
Verify
# 1. Type-check all packages
npm run typecheck
# 2. Lint
npm run lint
# 3. Run all tests
npm run test
# 4. Smoke-test a new image node (requires GEMINI_API_KEY in env or DB)
npm run dev:nodetool -- node run nodetool.image.TextToImage \
--props '{"prompt": "a red apple", "model": {"type": "image_model", "id": "gemini-3.1-flash-image", "provider": "gemini", "name": "Gemini 3.1 Flash Image"}}'
# 5. Smoke-test via chat agent (text model — auto-discovered, no list change needed)
npm run dev:chat -- --provider gemini --model gemini-3.5-flash
# Combined (typecheck + lint + test):
npm run check
Contributing
Open a PR at https://github.com/nodetool-ai/nodetool. Run npm run check (typecheck + lint + test) before pushing. Join the discussion on Discord.