diff --git a/.vitepress/theme/OfficialNativeApiSidebar.vue b/.vitepress/theme/OfficialNativeApiSidebar.vue index 5aba1c98..ac477c4e 100644 --- a/.vitepress/theme/OfficialNativeApiSidebar.vue +++ b/.vitepress/theme/OfficialNativeApiSidebar.vue @@ -36,6 +36,14 @@ const googleModels = [ text: 'Gemini Omni 1.1 Flash Preview', link: '/model-api-reference/official-native-api/google/gemini-omni-1.1-flash-preview', }, + { + text: 'Nano Banana Pro', + link: '/model-api-reference/official-native-api/google/gemini-3-pro-image', + }, + { + text: 'Nano Banana 2', + link: '/model-api-reference/official-native-api/google/gemini-3.1-flash-image', + }, ] function normalize(path: string) { diff --git a/api-reference/gemini-generate-content.md b/api-reference/gemini-generate-content.md index e8019c87..ea01feeb 100644 --- a/api-reference/gemini-generate-content.md +++ b/api-reference/gemini-generate-content.md @@ -71,6 +71,24 @@ curl -N -X POST \ -d '{"contents":[{"role":"user","parts":[{"text":"Write a short greeting."}]}]}' ``` -## Translation boundaries +## Native image generation -SandBase accepts text, function calls and responses, inline `image/*` data, and public HTTP(S) image references. Unsupported inline MIME types, private Google Files or `gs://` references, multiple candidates, and non-text response modalities are rejected with a Google-style `400 INVALID_ARGUMENT` response instead of being silently discarded. +These image models have dedicated native GenerateContent mappings: + +- [Nano Banana Pro (Gemini 3 Pro Image)](/model-api-reference/official-native-api/google/gemini-3-pro-image) +- [Nano Banana 2 (Gemini 3.1 Flash Image)](/model-api-reference/official-native-api/google/gemini-3.1-flash-image) + +For these mappings, SandBase forwards the request and response payloads without converting them through Chat +Completions. This preserves image parts, response modalities, and provider-defined fields. Use the bare model name in +the URL; for example, `google/gemini-3.1-flash-image` becomes `gemini-3.1-flash-image` in the path. + +## Routing and translation boundaries + +SandBase selects only providers that support the requested native Gemini protocol. If the selected model has no +compatible native mapping, the request fails instead of falling back to a provider with a different protocol. + +Models that use the translated GenerateContent path support text, function calls and responses, inline `image/*` data, +and public HTTP(S) image references. That translated path rejects unsupported inline MIME types, private Google Files or +`gs://` references, multiple candidates, and unsupported response modalities with a Google-style `400 INVALID_ARGUMENT` +response. The image-generation mappings listed above use raw passthrough and are not subject to those translation-only +limits; validate the returned media fields according to the selected provider's response. diff --git a/api-reference/gemini-interactions.md b/api-reference/gemini-interactions.md index 1641627a..6afa9bab 100644 --- a/api-reference/gemini-interactions.md +++ b/api-reference/gemini-interactions.md @@ -53,6 +53,18 @@ apiReference: Use `x-goog-api-key`, `Authorization: Bearer …`, or the `key` query parameter, in that priority order. Prefer a header because query-string credentials can be logged. +## Supported models + +The Interactions endpoint is not a generic route for every model whose catalog name starts with `google/`. The model +must have an Interactions-compatible provider mapping. SandBase currently documents these compatible models: + +- [Gemini Omni Flash Preview](/model-api-reference/official-native-api/google/gemini-omni-flash-preview) +- [Gemini Omni 1.1 Flash Preview](/model-api-reference/official-native-api/google/gemini-omni-1.1-flash-preview) + +Use the endpoint documented on each model page. For example, Nano Banana Pro and Nano Banana 2 use +`/v1beta/models/{model}:generateContent`, not `/v1beta/interactions`. An unsupported model fails instead of being routed +through a different Google protocol. + ## Success and polling Successful submissions return HTTP `200`, including background work and synchronous requests whose bounded wait expires. Always inspect `status`: diff --git a/model-api-reference/official-native-api/google/gemini-3-pro-image.md b/model-api-reference/official-native-api/google/gemini-3-pro-image.md new file mode 100644 index 00000000..5ca508f2 --- /dev/null +++ b/model-api-reference/official-native-api/google/gemini-3-pro-image.md @@ -0,0 +1,96 @@ +--- +title: "Nano Banana Pro Native API Reference" +description: "Native Gemini GenerateContent API reference for google/gemini-3-pro-image (Nano Banana Pro) on SandBase." +aside: false +outline: false +apiReference: + title: Nano Banana Pro (Gemini 3 Pro Image) + operation: Gemini GenerateContent + method: POST + path: /v1beta/models/gemini-3-pro-image:generateContent + description: Generate or edit images with Nano Banana Pro through the native Google Gemini protocol. SandBase preserves the provider request and response payload instead of converting it to Chat Completions. + groups: + - title: Request body + description: Send a native Gemini GenerateContent body. Provider-defined fields are passed through for this model. + fields: + - { name: contents, type: array, required: true, description: Gemini Content objects containing the prompt and optional input images. } + - { name: generationConfig.responseModalities, type: "array", required: true, description: "Requested output modalities. Include IMAGE; include TEXT when you also want a text part." } + - { name: generationConfig.imageConfig, type: object, required: false, description: "Image options such as aspectRatio, when supported by the provider." } + examples: + - label: cURL + language: bash + code: |- + curl -X POST \ + "https://api.sandbase.ai/v1beta/models/gemini-3-pro-image:generateContent" \ + -H "x-goog-api-key: $SANDBASE_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "contents": [{ + "role": "user", + "parts": [{"text": "Create a studio product photo of a translucent blue perfume bottle on white marble."}] + }], + "generationConfig": { + "responseModalities": ["TEXT", "IMAGE"], + "imageConfig": {"aspectRatio": "1:1"} + } + }' + response: + status: 200 OK + code: |- + { + "candidates": [{ + "content": { + "role": "model", + "parts": [ + {"text": "Here is the generated image."}, + {"inlineData": {"mimeType": "image/png", "data": ""}} + ] + }, + "finishReason": "STOP", + "index": 0 + }], + "usageMetadata": {"totalTokenCount": 1290} + } +seo: + modelName: "Nano Banana Pro (Gemini 3 Pro Image)" + modelId: "google/gemini-3-pro-image" + vendor: "Google" + vendorSlug: "google" + modelSlug: "gemini-3-pro-image" + protocol: "Gemini GenerateContent API" + endpoint: "/v1beta/models/gemini-3-pro-image:generateContent" + publishedAt: "2026-08-31T00:00:00Z" + capabilities: ["image_generation", "image_editing"] + category: "Official Native API" +--- + + + +## Edit an image + +Add the source image as an `inlineData` part next to the text instruction. Use the MIME type of the bytes you send: + +```json +{ + "contents": [{ + "role": "user", + "parts": [ + {"text": "Replace the background with a warm sunset gradient."}, + {"inlineData": {"mimeType": "image/jpeg", "data": ""}} + ] + }], + "generationConfig": {"responseModalities": ["IMAGE"]} +} +``` + +## Response handling + +Read generated media from the returned candidate parts. Because this model uses native passthrough, provider-defined +field casing and MIME metadata are returned as received. Decode the base64 payload and verify the actual media bytes +before choosing a file extension. + +## Official Google resources + +- [Gemini API documentation](https://ai.google.dev/gemini-api/docs) +- [Image generation documentation](https://ai.google.dev/gemini-api/docs/image-generation) +- [GenerateContent API reference](https://ai.google.dev/api/generate-content) diff --git a/model-api-reference/official-native-api/google/gemini-3.1-flash-image.md b/model-api-reference/official-native-api/google/gemini-3.1-flash-image.md new file mode 100644 index 00000000..9c9ccc15 --- /dev/null +++ b/model-api-reference/official-native-api/google/gemini-3.1-flash-image.md @@ -0,0 +1,95 @@ +--- +title: "Nano Banana 2 Native API Reference" +description: "Native Gemini GenerateContent API reference for google/gemini-3.1-flash-image (Nano Banana 2) on SandBase." +aside: false +outline: false +apiReference: + title: Nano Banana 2 (Gemini 3.1 Flash Image) + operation: Gemini GenerateContent + method: POST + path: /v1beta/models/gemini-3.1-flash-image:generateContent + description: Generate or edit images with Nano Banana 2 through the native Google Gemini protocol. SandBase preserves the provider request and response payload instead of converting it to Chat Completions. + groups: + - title: Request body + description: Send a native Gemini GenerateContent body. Provider-defined fields are passed through for this model. + fields: + - { name: contents, type: array, required: true, description: Gemini Content objects containing the prompt and optional input images. } + - { name: generationConfig.responseModalities, type: "array", required: true, description: "Requested output modalities. Include IMAGE; include TEXT when you also want a text part." } + - { name: generationConfig.imageConfig, type: object, required: false, description: "Image options such as aspectRatio, when supported by the provider." } + examples: + - label: cURL + language: bash + code: |- + curl -X POST \ + "https://api.sandbase.ai/v1beta/models/gemini-3.1-flash-image:generateContent" \ + -H "x-goog-api-key: $SANDBASE_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "contents": [{ + "role": "user", + "parts": [{"text": "Create a wide editorial illustration of a solar-powered city at sunrise."}] + }], + "generationConfig": { + "responseModalities": ["IMAGE"], + "imageConfig": {"aspectRatio": "16:9"} + } + }' + response: + status: 200 OK + code: |- + { + "candidates": [{ + "content": { + "role": "model", + "parts": [ + {"inline_data": {"mime_type": "image/png", "data": ""}} + ] + }, + "finishReason": "STOP", + "index": 0 + }], + "usageMetadata": {"totalTokenCount": 980} + } +seo: + modelName: "Nano Banana 2 (Gemini 3.1 Flash Image)" + modelId: "google/gemini-3.1-flash-image" + vendor: "Google" + vendorSlug: "google" + modelSlug: "gemini-3.1-flash-image" + protocol: "Gemini GenerateContent API" + endpoint: "/v1beta/models/gemini-3.1-flash-image:generateContent" + publishedAt: "2026-08-31T00:00:00Z" + capabilities: ["image_generation", "image_editing"] + category: "Official Native API" +--- + + + +## Edit an image + +Add the source image as an `inlineData` part next to the text instruction. Use the MIME type of the bytes you send: + +```json +{ + "contents": [{ + "role": "user", + "parts": [ + {"text": "Keep the subject unchanged and turn the background into a paper-cut landscape."}, + {"inlineData": {"mimeType": "image/png", "data": ""}} + ] + }], + "generationConfig": {"responseModalities": ["IMAGE"]} +} +``` + +## Response handling + +Read generated media from the returned candidate parts. Because this model uses native passthrough, provider-defined +field casing and MIME metadata are returned as received. Decode the base64 payload and verify the actual media bytes +before choosing a file extension. + +## Official Google resources + +- [Gemini API documentation](https://ai.google.dev/gemini-api/docs) +- [Image generation documentation](https://ai.google.dev/gemini-api/docs/image-generation) +- [GenerateContent API reference](https://ai.google.dev/api/generate-content) diff --git a/model-api-reference/official-native-api/index.md b/model-api-reference/official-native-api/index.md index a140b2fe..0a927f79 100644 --- a/model-api-reference/official-native-api/index.md +++ b/model-api-reference/official-native-api/index.md @@ -6,7 +6,9 @@ description: Use official provider-native protocols through SandBase, organized # Official Native API Official Native APIs preserve a provider's request and response protocol for easier migration of existing integrations. -Browse by provider, then choose the exact model. SandBase currently documents native protocols for ByteDance, Google Gemini Omni, and OpenAI; other LLMs are documented in the LLM Models section because their provider-native protocol is the default model interface. +Browse by provider, then choose the exact model. SandBase currently documents native protocols for ByteDance, Google, +and OpenAI. Models listed here have a dedicated provider-native request path; other LLMs remain in the LLM Models +section. ## OpenAI @@ -23,8 +25,13 @@ GPT Image 2 uses the native OpenAI Images API at `/v1/images/generations`. Pass - [Gemini Omni Flash Preview](/model-api-reference/official-native-api/google/gemini-omni-flash-preview) - [Gemini Omni 1.1 Flash Preview](/model-api-reference/official-native-api/google/gemini-omni-1.1-flash-preview) +- [Nano Banana Pro (Gemini 3 Pro Image)](/model-api-reference/official-native-api/google/gemini-3-pro-image) +- [Nano Banana 2 (Gemini 3.1 Flash Image)](/model-api-reference/official-native-api/google/gemini-3.1-flash-image) -Gemini Omni models use the native Google Gemini Interactions protocol at `/v1beta/interactions`. +Gemini Omni models use the native Google Gemini Interactions protocol at `/v1beta/interactions`. The two image models +use Google GenerateContent at `/v1beta/models/{model}:generateContent`; their native request and response payloads are +passed through without conversion. See the [GenerateContent protocol reference](/api-reference/gemini-generate-content) +for authentication and shared request fields. ## Seedance protocol diff --git a/public/openapi.yaml b/public/openapi.yaml index 8af6ffa3..ac94f9aa 100644 --- a/public/openapi.yaml +++ b/public/openapi.yaml @@ -451,6 +451,8 @@ paths: Accepts a Google Gemini GenerateContent request and returns a Gemini-compatible response. A bare model name, such as gemini-3.5-flash, is normalized to the SandBase catalog name google/gemini-3.5-flash. This endpoint also accepts x-goog-api-key and the key query parameter for Google SDK compatibility, although headers are safer. + Models with a native passthrough mapping preserve provider-defined request and response fields, including image + response modalities. Other mappings may use the translated capability subset documented in the schema below. security: - GoogleApiKey: [] - BearerAuth: [] @@ -464,14 +466,14 @@ paths: schema: { $ref: '#/components/schemas/GeminiGenerateContentRequest' } responses: '200': { description: Gemini GenerateContent response, content: { application/json: { schema: { $ref: '#/components/schemas/GeminiGenerateContentResponse' } } } } - '400': { description: Invalid JSON or an input field that SandBase cannot safely translate, content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } + '400': { description: Invalid JSON or a request rejected by the selected native or translated mapping, content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } '401': { description: 'Missing, invalid, revoked, or expired API key', content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } '402': { description: Organization balance and credit are exhausted, content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } '403': { description: 'API Key spending limit reached, organization disabled, or upstream permission rejected', content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } '404': { description: 'Model, method, or upstream resource not found', content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } '429': { description: Provider rate limit exceeded, content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } '500': { description: Prediction lifecycle or provider error, content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } - '502': { description: Upstream response could not be translated, content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } + '502': { description: Upstream response failed or could not be processed, content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } '503': { description: Routing failed or all provider candidates were unavailable, content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } '504': { description: Upstream request timed out, content: { application/json: { schema: { $ref: '#/components/schemas/GeminiError' } } } } @@ -3976,7 +3978,7 @@ components: GeminiGenerateContentRequest: type: object additionalProperties: true - description: Google Gemini GenerateContent request. Unknown top-level fields are ignored by the current translator. + description: Google Gemini GenerateContent request. Native passthrough mappings preserve provider-defined fields; translated mappings use the documented capability subset. properties: contents: type: array @@ -3992,7 +3994,7 @@ components: topK: { type: integer } maxOutputTokens: { type: integer } stopSequences: { type: array, items: { type: string } } - candidateCount: { type: integer, description: The current translator accepts only 1 when supplied. } + candidateCount: { type: integer, description: Translated mappings accept only 1 when supplied; native passthrough mappings preserve the provider request. } seed: { type: integer } presencePenalty: { type: number } frequencyPenalty: { type: number } @@ -4001,7 +4003,7 @@ components: responseModalities: type: array items: { type: string } - description: The current translator supports TEXT only. + description: Native image mappings support provider image modalities. Other translated mappings may support TEXT only. thinkingConfig: { type: object, additionalProperties: true } tools: type: array diff --git a/scripts/validate-doc-content.mjs b/scripts/validate-doc-content.mjs index 72f7c28b..75ce26a5 100644 --- a/scripts/validate-doc-content.mjs +++ b/scripts/validate-doc-content.mjs @@ -166,6 +166,17 @@ const geminiOmniReference = readFileSync('model-api-reference/official-native-ap assert.match(geminiOmniReference, /path\\?\":\\?\"\/v1beta\/interactions/, 'Gemini Omni must use the native Interactions endpoint') assert.doesNotMatch(geminiOmniReference, /path\\?\":\\?\"\/v1\/chat\/completions/, 'Gemini Omni must not advertise Chat Completions') +const geminiInteractionsReference = readFileSync('api-reference/gemini-interactions.md', 'utf8') +assert.match(geminiInteractionsReference, /not a generic route for every model/, 'Gemini Interactions must not imply support for every Google model') +assert.match(geminiInteractionsReference, /Nano Banana Pro and Nano Banana 2 use\s+`\/v1beta\/models\/\{model\}:generateContent`/, 'Gemini Interactions must direct image models to GenerateContent') + +for (const filename of ['gemini-3-pro-image.md', 'gemini-3.1-flash-image.md']) { + const geminiImageReference = readFileSync(`model-api-reference/official-native-api/google/${filename}`, 'utf8') + assert.match(geminiImageReference, /path:\s*\/v1beta\/models\//, `${filename} must use the native GenerateContent endpoint`) + assert.match(geminiImageReference, /:generateContent/, `${filename} must document the GenerateContent action`) + assert.doesNotMatch(geminiImageReference, /path:\s*\/v1\/chat\/completions/, `${filename} must not advertise Chat Completions`) +} + const runReference = readFileSync('api-reference/models/run.md', 'utf8') assert.doesNotMatch(runReference, /webhook_url[^\n]*API tasks/i, 'Run reference must not promise callbacks for API capabilities') assert.match(runReference, /webhook_url[^\n]*image, video, or audio tasks/i, 'Run reference must match webhook capability scope') diff --git a/scripts/validate-public-api-surface.mjs b/scripts/validate-public-api-surface.mjs index 3bab6254..49482814 100644 --- a/scripts/validate-public-api-surface.mjs +++ b/scripts/validate-public-api-surface.mjs @@ -673,7 +673,11 @@ assert.match(officialNativeSidebar, /official-native-api\/bytedance\/seedance-2\ assert.match(officialNativeSidebar, /official-native-api\/bytedance\/seedance-2\.0-official/, 'Official Native API sidebar must expose Seedance 2.0') assert.match(officialNativeSidebar, /Gemini Omni Flash Preview/, 'Official Native API sidebar must expose Gemini Omni') assert.match(officialNativeSidebar, /official-native-api\/google\/gemini-omni-flash-preview/, 'Official Native API sidebar must link Gemini Omni') -assert.doesNotMatch(officialNativeSidebar, /Gemini 3\.1/, 'Official Native API sidebar must not duplicate ordinary LLM navigation') +assert.match(officialNativeSidebar, /Nano Banana Pro/, 'Official Native API sidebar must expose Gemini 3 Pro Image') +assert.match(officialNativeSidebar, /official-native-api\/google\/gemini-3-pro-image/, 'Official Native API sidebar must link the Gemini 3 Pro Image native page') +assert.match(officialNativeSidebar, /Nano Banana 2/, 'Official Native API sidebar must expose Gemini 3.1 Flash Image') +assert.match(officialNativeSidebar, /official-native-api\/google\/gemini-3\.1-flash-image/, 'Official Native API sidebar must link the Gemini 3.1 Flash Image native page') +assert.doesNotMatch(officialNativeSidebar, /llm-models\/google\/gemini-3(?:\.1)?-(?:pro|flash)-image/, 'Official Native API sidebar must use dedicated native pages for Gemini image models') assert.match(officialNativeSidebar, /GPT Image 2/, 'Official Native API sidebar must expose GPT Image 2') assert.match(officialNativeSidebar, /official-native-api\/openai\/gpt-image-2/, 'Official Native API sidebar must link GPT Image 2') const scheduleOverview = readFileSync(new URL('../api-reference/deployments/index.md', import.meta.url), 'utf8')