> ## Documentation Index
> Fetch the complete documentation index at: https://docs.comfy.org/llms.txt
> Use this file to discover all available pages before exploring further.

# 搭配 Comfy Router 使用 Nano Banana Pro

> 通过 Comfy Router 以 HTTP 方式使用 Nano Banana Pro（Gemini 3 Pro Image）生成图像的 Python、TypeScript 和 cURL 代码片段，以及请求字段和返回结果结构

Nano Banana Pro 的 API 参考。Nano Banana Pro（Gemini 3 Pro Image）是 Google Nano Banana 图像生成家族的 Pro 层级，面向复杂场景和清晰可读的文字。

## 快速开始

在[你的 Comfy 工作区](https://platform.comfy.org/profile/api-keys)中创建一个密钥，并将其导出为 `COMFY_API_KEY`。Python 和 TypeScript 代码片段使用 Comfy SDK（`pip install comfy-sdk`、`npm install @comfyorg/sdk`）；cURL 代码片段是通过原始 HTTP 进行的相同调用。

**模型 ID：** `vertexai/gemini-3-pro-image`

**端点：** `POST https://api.comfy.org/v2/models/vertexai/gemini-3-pro-image`

<Tabs>
  <Tab title="Wait for the result">
    <CodeGroup>
      ```python Python theme={null}
      from comfy_sdk import Comfy

      # Reads COMFY_API_KEY from the environment.
      # The SDK automatically creates an idempotency key and reuses it for automatic retries.
      with Comfy() as client:
          result = client.models.run(
              "vertexai/gemini-3-pro-image",
              {
                  "contents": [
                      {
                          "role": "user",
                          "parts": [
                              {
                                  "text": "a single red maple leaf on a plain white background, studio lighting",
                              },
                          ],
                      },
                  ],
                  "generationConfig": {
                      "responseModalities": ["IMAGE"],
                      "imageConfig": {
                          "aspectRatio": "1:1",
                      },
                  },
              },
          )

      print("image (base64):", result["candidates"][0]["content"]["parts"][0]["inlineData"]["data"])
      ```

      ```typescript TypeScript theme={null}
      import { comfy } from "@comfyorg/sdk";

      // Reads COMFY_API_KEY from the environment.
      // The SDK automatically creates an idempotency key and reuses it for automatic retries.
      type Result = { candidates: { content: { parts: { inlineData: { data: string } }[] } }[] };
      const { data } = await comfy.models.run<Result>("vertexai/gemini-3-pro-image", {
        contents: [
          {
            role: "user",
            parts: [
              {
                text: "a single red maple leaf on a plain white background, studio lighting",
              },
            ],
          },
        ],
        generationConfig: {
          responseModalities: ["IMAGE"],
          imageConfig: {
            aspectRatio: "1:1",
          },
        },
      });

      console.log("image (base64):", data.candidates[0].content.parts[0].inlineData.data);
      ```

      ```bash cURL theme={null}
      curl https://api.comfy.org/v2/models/vertexai/gemini-3-pro-image \
        -H "X-API-Key: $COMFY_API_KEY" \
        -H "Idempotency-Key: $(uuidgen)" \
        -H "Content-Type: application/json" \
        -d "{\"contents\": [{\"role\":\"user\",\"parts\":[{\"text\":\"a single red maple leaf on a plain white background, studio lighting\"}]}], \"generationConfig\": {\"responseModalities\":[\"IMAGE\"],\"imageConfig\":{\"aspectRatio\":\"1:1\"}}}"
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Queue and collect later">
    <CodeGroup>
      ```python Python theme={null}
      from comfy_sdk import Comfy

      # Reads COMFY_API_KEY from the environment.
      # Each submit() call mints its own Idempotency-Key and reuses it for automatic retries.
      with Comfy() as client:
          handle = client.models.submit(
              "vertexai/gemini-3-pro-image",
              {
                  "contents": [
                      {
                          "role": "user",
                          "parts": [
                              {
                                  "text": "a single red maple leaf on a plain white background, studio lighting",
                              },
                          ],
                      },
                  ],
                  "generationConfig": {
                      "responseModalities": ["IMAGE"],
                      "imageConfig": {
                          "aspectRatio": "1:1",
                      },
                  },
              },
          )
          print("request_id:", handle.request_id)  # with the model ID, all another process needs

          # Poll until the request completes, waiting the Retry-After the server names.
          for update in handle.iter_events():
              print(update.status, update.queue_position)

          # The provider's own payload, the same value models.run() returns.
          # A request that failed or was cancelled raises the typed Router error here.
          result = handle.get()

      print("image (base64):", result["candidates"][0]["content"]["parts"][0]["inlineData"]["data"])
      ```

      ```typescript TypeScript theme={null}
      import { comfy } from "@comfyorg/sdk";

      // Reads COMFY_API_KEY from the environment.
      // Each submit() call mints its own Idempotency-Key and reuses it for automatic retries.
      type Result = { candidates: { content: { parts: { inlineData: { data: string } }[] } }[] };
      const handle = await comfy.models.submit<Result>("vertexai/gemini-3-pro-image", {
        contents: [
          {
            role: "user",
            parts: [
              {
                text: "a single red maple leaf on a plain white background, studio lighting",
              },
            ],
          },
        ],
        generationConfig: {
          responseModalities: ["IMAGE"],
          imageConfig: {
            aspectRatio: "1:1",
          },
        },
      });
      console.log("requestId:", handle.requestId); // with the model ID, all another process needs

      // Poll until the request completes, waiting the Retry-After the server names.
      for await (const update of handle.events()) {
        console.log(update.status, update.queuePosition);
      }

      // The same result models.run() returns. A request that failed or was cancelled rejects here.
      const result = await handle.get();
      if (result.kind !== "json") throw new Error("expected a JSON result");

      console.log("image (base64):", result.data.candidates[0].content.parts[0].inlineData.data);
      ```

      ```bash cURL theme={null}
      # 1. Submit. Router answers 201 with request_id, status_url, response_url and cancel_url.
      curl https://api.comfy.org/v2/models/vertexai/gemini-3-pro-image/requests \
        -H "X-API-Key: $COMFY_API_KEY" \
        -H "Idempotency-Key: $(uuidgen)" \
        -H "Content-Type: application/json" \
        -d "{\"contents\": [{\"role\":\"user\",\"parts\":[{\"text\":\"a single red maple leaf on a plain white background, studio lighting\"}]}], \"generationConfig\": {\"responseModalities\":[\"IMAGE\"],\"imageConfig\":{\"aspectRatio\":\"1:1\"}}}"

      # 2. Poll until status is COMPLETED, waiting the Retry-After seconds each response names.
      REQUEST_ID="<request_id from the 201 body>"
      curl -i https://api.comfy.org/v2/models/vertexai/gemini-3-pro-image/requests/$REQUEST_ID/status \
        -H "X-API-Key: $COMFY_API_KEY"

      # 3. Collect. 200 with the model's native output, 202 with the status body while it is still running.
      curl https://api.comfy.org/v2/models/vertexai/gemini-3-pro-image/requests/$REQUEST_ID \
        -H "X-API-Key: $COMFY_API_KEY"
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Schema

### Input

<ParamField body="contents" type="object[]" required>
  The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.
</ParamField>

<ParamField body="contents[].parts" type="object[]" required />

<ParamField body="contents[].parts[].fileData" type="object">
  URI based data.
</ParamField>

<ParamField body="contents[].parts[].fileData.fileUri" type="string">
  URI
</ParamField>

<ParamField body="contents[].parts[].fileData.mimeType" type="string">
  The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.

  Possible values: `application/pdf`, `audio/mpeg`, `audio/mp3`, `audio/wav`, `image/png`, `image/jpeg`, `image/webp`, `text/plain`, `video/mov`, `video/mpeg`, `video/mp4`, `video/mpg`, `video/avi`, `video/wmv`, `video/mpegps`, `video/flv`, `image/heic`, `image/heif`, `audio/flac`, `video/webm`
</ParamField>

<ParamField body="contents[].parts[].inlineData" type="object">
  Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.
</ParamField>

<ParamField body="contents[].parts[].inlineData.data" type="string (byte)">
  The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB

  Format: `byte`
</ParamField>

<ParamField body="contents[].parts[].inlineData.mimeType" type="string">
  The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.

  Possible values: `application/pdf`, `audio/mpeg`, `audio/mp3`, `audio/wav`, `image/png`, `image/jpeg`, `image/webp`, `text/plain`, `video/mov`, `video/mpeg`, `video/mp4`, `video/mpg`, `video/avi`, `video/wmv`, `video/mpegps`, `video/flv`, `image/heic`, `image/heif`, `audio/flac`, `video/webm`
</ParamField>

<ParamField body="contents[].parts[].mediaProcessing" type="string">
  模型读取该部分视频的方式。设置为 "AGENTIC" 可让模型自行决定要检查的片段，而不是按固定速率采样帧。使用默认的固定速率采样时请省略。在 gemini-3.7-flash 及更新的 Flash 模型上受支持。
</ParamField>

<ParamField body="contents[].parts[].text" type="string">
  A text prompt or code snippet.
</ParamField>

<ParamField body="contents[].parts[].thought" type="boolean">
  Indicates this part is a thinking/reasoning step from the model.
</ParamField>

<ParamField body="contents[].role" type="string">
  Possible values: `user`, `model`
</ParamField>

<ParamField body="generationConfig" type="object">
  Sampling, length and output settings for the generation. Every field is optional: the fields below that declare a `default` apply it when omitted, and the rest fall back to the model's own behaviour.
</ParamField>

<ParamField body="generationConfig.imageConfig" type="object">
  Configuration for image generation
</ParamField>

<ParamField body="generationConfig.imageConfig.aspectRatio" type="string">
  Aspect ratio for generated images
</ParamField>

<ParamField body="generationConfig.imageConfig.imageOutputOptions" type="object">
  Optional. The image output format for generated images.
</ParamField>

<ParamField body="generationConfig.imageConfig.imageOutputOptions.compressionQuality" type="integer">
  Optional. The compression quality of the output image.
</ParamField>

<ParamField body="generationConfig.imageConfig.imageOutputOptions.mimeType" type="string">
  Optional. The image format that the output should be saved as.
</ParamField>

<ParamField body="generationConfig.imageConfig.imageSize" type="string">
  Optional. Specifies the size of generated images. Supported values are 1K, 2K, 4K. If not specified, the model will use default value 1K.
</ParamField>

<ParamField body="generationConfig.maxOutputTokens" type="integer">
  Maximum number of tokens that can be generated in the response. A token is approximately 4 characters. 100 tokens correspond to roughly 60-80 words.

  Range: `16` to `65536`
</ParamField>

<ParamField body="generationConfig.responseModalities" type="`TEXT`, `IMAGE`[]" />

<ParamField body="generationConfig.seed" type="integer">
  When seed is fixed to a specific value, the model makes a best effort to provide the same response for repeated requests. Deterministic output isn't guaranteed. Also, changing the model or parameter settings, such as the temperature, can cause variations in the response even when you use the same seed value. By default, a random seed value is used. Available for the following models:, gemini-2.5-flash, gemini-2.5-pro, gemini-2.5-flash-preview-04-1, gemini-2.5-pro-preview-05-0, gemini-2.0-flash-lite-00, gemini-2.0-flash-001
</ParamField>

<ParamField body="generationConfig.stopSequences" type="string[]" />

<ParamField body="generationConfig.temperature" type="number" default="1">
  The temperature is used for sampling during response generation, which occurs when topP and topK are applied. Temperature controls the degree of randomness in token selection. Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. A temperature of 0 means that the highest probability tokens are always selected. In this case, responses for a given prompt are mostly deterministic, but a small amount of variation is still possible. If the model returns a response that's too generic, too short, or the model gives a fallback response, try increasing the temperature

  Range: `0` to `2`

  Format: `float`
</ParamField>

<ParamField body="generationConfig.thinkingConfig" type="object">
  Optional. Configuration for thinking features. Thinking is a process where the model breaks down a complex task into smaller steps to generate a higher-quality response.
</ParamField>

<ParamField body="generationConfig.thinkingConfig.includeThoughts" type="boolean">
  Optional. If true, the model will include its thoughts in the response.
</ParamField>

<ParamField body="generationConfig.thinkingConfig.thinkingBudget" type="integer">
  Optional. The token budget for the model's thinking process. The model will make a best effort to stay within this budget.
</ParamField>

<ParamField body="generationConfig.thinkingConfig.thinkingLevel" type="string">
  Optional. The thinking level for the model.

  Possible values: `THINKING_LEVEL_UNSPECIFIED`, `LOW`, `MEDIUM`, `HIGH`, `MINIMAL`
</ParamField>

<ParamField body="generationConfig.topK" type="integer" default="40">
  Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model's vocabulary. A top-K of 3 means that the next token is selected from among the 3 most probable tokens by using temperature.

  Range: `1` to `…`
</ParamField>

<ParamField body="generationConfig.topP" type="number" default="0.95">
  If specified, nucleus sampling is used.
  Top-P changes how the model selects tokens for output. Tokens are selected from the most (see top-K) to least probable until the sum of their probabilities equals the top-P value. For example, if tokens A, B, and C have a probability of 0.3, 0.2, and 0.1 and the top-P value is 0.5, then the model will select either A or B as the next token by using temperature and excludes C as a candidate.
  Specify a lower value for less random responses and a higher value for more random responses.

  Range: `0` to `1`

  Format: `float`
</ParamField>

<ParamField body="safetySettings" type="object[]">
  Per request settings for blocking unsafe content. Enforced on GenerateContentResponse.candidates.
</ParamField>

<ParamField body="safetySettings[].category" type="string" required>
  Possible values: `HARM_CATEGORY_SEXUALLY_EXPLICIT`, `HARM_CATEGORY_HATE_SPEECH`, `HARM_CATEGORY_HARASSMENT`, `HARM_CATEGORY_DANGEROUS_CONTENT`
</ParamField>

<ParamField body="safetySettings[].threshold" type="string" required>
  Possible values: `OFF`, `BLOCK_NONE`, `BLOCK_LOW_AND_ABOVE`, `BLOCK_MEDIUM_AND_ABOVE`, `BLOCK_ONLY_HIGH`
</ParamField>

<ParamField body="systemInstruction" type="object">
  Instructions for the model to steer it toward better performance. For example, "Answer as concisely as possible" or "Don't use technical terms in your response". The text strings count toward the token limit. The role field of systemInstruction is ignored and doesn't affect the performance of the model. Note: Only text should be used in parts and content in each part should be in a separate paragraph.
</ParamField>

<ParamField body="systemInstruction.parts" type="object[]" required>
  A list of ordered parts that make up a single message. Different parts may have different IANA MIME types. For limits on the inputs, such as the maximum number of tokens or the number of images, see the model specifications on the Google models page.
</ParamField>

<ParamField body="systemInstruction.parts[].text" type="string">
  A text prompt or code snippet.
</ParamField>

<ParamField body="systemInstruction.role" type="string">
  The identity of the entity that creates the message. The following values are supported: user: This indicates that the message is sent by a real person, typically a user-generated message. model: This indicates that the message is generated by the model. The model value is used to insert messages from the model into the conversation during multi-turn conversations. For non-multi-turn conversations, this field can be left blank or unset.

  Possible values: `user`, `model`
</ParamField>

<ParamField body="tools" type="object[]">
  A piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. See Function calling.
</ParamField>

<ParamField body="tools[].functionDeclarations" type="object[]" />

<ParamField body="tools[].functionDeclarations[].description" type="string" />

<ParamField body="tools[].functionDeclarations[].name" type="string" required />

<ParamField body="tools[].functionDeclarations[].parameters" type="object">
  JSON schema for the function parameters
</ParamField>

<ParamField body="uploadImagesToStorage" type="boolean">
  If true, generated images will be uploaded to cloud storage and returned as signed URLs instead of inline base64 data. The URLs expire after 24 hours.
</ParamField>

<ParamField body="videoMetadata" type="object">
  For video input, the start and end offset of the video in Duration format. For example, to specify a 10 second clip starting at 1:00, set "startOffset": \{ "seconds": 60 } and "endOffset": \{ "seconds": 70 }. The metadata should only be specified while the video data is presented in inlineData or fileData.
</ParamField>

<ParamField body="videoMetadata.endOffset" type="object">
  Represents a duration offset for video timeline positions.
</ParamField>

<ParamField body="videoMetadata.endOffset.nanos" type="integer">
  Signed fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values.

  Range: `0` to `999999999`
</ParamField>

<ParamField body="videoMetadata.endOffset.seconds" type="integer">
  Signed seconds of the span of time. Must be from -315,576,000,000 to +315,576,000,000 inclusive.

  Range: `-315576000000` to `315576000000`
</ParamField>

<ParamField body="videoMetadata.startOffset" type="object">
  Represents a duration offset for video timeline positions.
</ParamField>

<ParamField body="videoMetadata.startOffset.nanos" type="integer">
  Signed fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values.

  Range: `0` to `999999999`
</ParamField>

<ParamField body="videoMetadata.startOffset.seconds" type="integer">
  Signed seconds of the span of time. Must be from -315,576,000,000 to +315,576,000,000 inclusive.

  Range: `-315576000000` to `315576000000`
</ParamField>

Generated from the schema Router serves at `GET /v2/models/vertexai/gemini-3-pro-image/openapi.json`, the same document it validates a call against before the request reaches the provider.

### Output

<ResponseField name="candidates" type="object[]" />

<ResponseField name="candidates[].citationMetadata" type="object" />

<ResponseField name="candidates[].citationMetadata.citations" type="object[]" />

<ResponseField name="candidates[].citationMetadata.citations[].authors" type="string[]" />

<ResponseField name="candidates[].citationMetadata.citations[].endIndex" type="integer" />

<ResponseField name="candidates[].citationMetadata.citations[].license" type="string" />

<ResponseField name="candidates[].citationMetadata.citations[].publicationDate" type="string (date)">
  Format: `date`
</ResponseField>

<ResponseField name="candidates[].citationMetadata.citations[].startIndex" type="integer" />

<ResponseField name="candidates[].citationMetadata.citations[].title" type="string" />

<ResponseField name="candidates[].citationMetadata.citations[].uri" type="string" />

<ResponseField name="candidates[].content" type="object">
  The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.
</ResponseField>

<ResponseField name="candidates[].content.parts" type="object[]" required />

<ResponseField name="candidates[].content.parts[].fileData" type="object">
  URI based data.
</ResponseField>

<ResponseField name="candidates[].content.parts[].fileData.fileUri" type="string">
  URI
</ResponseField>

<ResponseField name="candidates[].content.parts[].fileData.mimeType" type="string">
  The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.

  Possible values: `application/pdf`, `audio/mpeg`, `audio/mp3`, `audio/wav`, `image/png`, `image/jpeg`, `image/webp`, `text/plain`, `video/mov`, `video/mpeg`, `video/mp4`, `video/mpg`, `video/avi`, `video/wmv`, `video/mpegps`, `video/flv`, `image/heic`, `image/heif`, `audio/flac`, `video/webm`
</ResponseField>

<ResponseField name="candidates[].content.parts[].inlineData" type="object">
  Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.
</ResponseField>

<ResponseField name="candidates[].content.parts[].inlineData.data" type="string (byte)">
  The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB

  Format: `byte`
</ResponseField>

<ResponseField name="candidates[].content.parts[].inlineData.mimeType" type="string">
  The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.

  Possible values: `application/pdf`, `audio/mpeg`, `audio/mp3`, `audio/wav`, `image/png`, `image/jpeg`, `image/webp`, `text/plain`, `video/mov`, `video/mpeg`, `video/mp4`, `video/mpg`, `video/avi`, `video/wmv`, `video/mpegps`, `video/flv`, `image/heic`, `image/heif`, `audio/flac`, `video/webm`
</ResponseField>

<ResponseField name="candidates[].content.parts[].mediaProcessing" type="string">
  模型读取该部分视频的方式。设置为 "AGENTIC" 可让模型自行决定要检查的片段，而不是按固定速率采样帧。使用默认的固定速率采样时请省略。在 gemini-3.7-flash 及更新的 Flash 模型上受支持。
</ResponseField>

<ResponseField name="candidates[].content.parts[].text" type="string">
  A text prompt or code snippet.
</ResponseField>

<ResponseField name="candidates[].content.parts[].thought" type="boolean">
  Indicates this part is a thinking/reasoning step from the model.
</ResponseField>

<ResponseField name="candidates[].content.role" type="string">
  Possible values: `user`, `model`
</ResponseField>

<ResponseField name="candidates[].finishReason" type="string" />

<ResponseField name="candidates[].safetyRatings" type="object[]" />

<ResponseField name="candidates[].safetyRatings[].category" type="string">
  Possible values: `HARM_CATEGORY_SEXUALLY_EXPLICIT`, `HARM_CATEGORY_HATE_SPEECH`, `HARM_CATEGORY_HARASSMENT`, `HARM_CATEGORY_DANGEROUS_CONTENT`
</ResponseField>

<ResponseField name="candidates[].safetyRatings[].probability" type="string">
  The probability that the content violates the specified safety category

  Possible values: `NEGLIGIBLE`, `LOW`, `MEDIUM`, `HIGH`, `UNKNOWN`
</ResponseField>

<ResponseField name="createTime" type="string">
  Timestamp when the response was created.
</ResponseField>

<ResponseField name="modelVersion" type="string">
  The model version used to generate the response.
</ResponseField>

<ResponseField name="promptFeedback" type="object" />

<ResponseField name="promptFeedback.blockReason" type="string" />

<ResponseField name="promptFeedback.blockReasonMessage" type="string" />

<ResponseField name="promptFeedback.safetyRatings" type="object[]" />

<ResponseField name="promptFeedback.safetyRatings[].category" type="string">
  Possible values: `HARM_CATEGORY_SEXUALLY_EXPLICIT`, `HARM_CATEGORY_HATE_SPEECH`, `HARM_CATEGORY_HARASSMENT`, `HARM_CATEGORY_DANGEROUS_CONTENT`
</ResponseField>

<ResponseField name="promptFeedback.safetyRatings[].probability" type="string">
  The probability that the content violates the specified safety category

  Possible values: `NEGLIGIBLE`, `LOW`, `MEDIUM`, `HIGH`, `UNKNOWN`
</ResponseField>

<ResponseField name="responseId" type="string">
  Unique identifier for the response.
</ResponseField>

<ResponseField name="usageMetadata" type="object" />

<ResponseField name="usageMetadata.cachedContentTokenCount" type="integer">
  Output only. Number of tokens in the cached part in the input (the cached content).
</ResponseField>

<ResponseField name="usageMetadata.candidatesTokenCount" type="integer">
  Number of tokens in the response(s).
</ResponseField>

<ResponseField name="usageMetadata.candidatesTokensDetails" type="object[]">
  Breakdown of candidate tokens by modality.
</ResponseField>

<ResponseField name="usageMetadata.candidatesTokensDetails[].modality" type="string">
  Type of input or output content modality.

  Possible values: `MODALITY_UNSPECIFIED`, `TEXT`, `IMAGE`, `VIDEO`, `AUDIO`, `DOCUMENT`
</ResponseField>

<ResponseField name="usageMetadata.candidatesTokensDetails[].tokenCount" type="integer">
  Number of tokens for the given modality.
</ResponseField>

<ResponseField name="usageMetadata.promptTokenCount" type="integer">
  Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.
</ResponseField>

<ResponseField name="usageMetadata.promptTokensDetails" type="object[]">
  Breakdown of prompt tokens by modality.
</ResponseField>

<ResponseField name="usageMetadata.promptTokensDetails[].modality" type="string">
  Type of input or output content modality.

  Possible values: `MODALITY_UNSPECIFIED`, `TEXT`, `IMAGE`, `VIDEO`, `AUDIO`, `DOCUMENT`
</ResponseField>

<ResponseField name="usageMetadata.promptTokensDetails[].tokenCount" type="integer">
  Number of tokens for the given modality.
</ResponseField>

<ResponseField name="usageMetadata.thoughtsTokenCount" type="integer">
  Number of tokens present in thoughts output.
</ResponseField>

<ResponseField name="usageMetadata.toolUsePromptTokenCount" type="integer">
  Number of tokens present in tool-use prompt(s).
</ResponseField>

<ResponseField name="usageMetadata.toolUsePromptTokensDetails" type="object[]">
  按模态细分的工具使用提示 token。
</ResponseField>

<ResponseField name="usageMetadata.toolUsePromptTokensDetails[].modality" type="string">
  输入或输出内容模态的类型。

  可能的值：`MODALITY_UNSPECIFIED`、`TEXT`、`IMAGE`、`VIDEO`、`AUDIO`、`DOCUMENT`
</ResponseField>

<ResponseField name="usageMetadata.toolUsePromptTokensDetails[].tokenCount" type="integer">
  给定模态的 token 数量。
</ResponseField>

<ResponseField name="usageMetadata.totalTokenCount" type="integer">
  Total number of tokens (prompt + candidates).
</ResponseField>

<ResponseField name="usageMetadata.trafficType" type="string">
  Traffic type used for the request (e.g., PROVISIONED\_THROUGHPUT).
</ResponseField>

## 示例

### 输入

```json theme={null}
{
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "a single red maple leaf on a plain white background, studio lighting"
        }
      ]
    }
  ],
  "generationConfig": {
    "responseModalities": [
      "IMAGE"
    ],
    "imageConfig": {
      "aspectRatio": "1:1"
    }
  }
}
```

### 输出

```json theme={null}
{
  "candidates": [
    {
      "content": {
        "role": "model",
        "parts": [
          {
            "inlineData": {
              "mimeType": "image/png",
              "data": "PGJhc2U2ND4="
            }
          }
        ]
      },
      "finishReason": "STOP"
    }
  ],
  "usageMetadata": {
    "promptTokenCount": 12,
    "candidatesTokenCount": 1290
  }
}
```

默认情况下，已生成图像的 part 会在 `inlineData.data` 中包含 base64 字节，并在 `inlineData.mimeType` 中包含媒体类型。请解码这些字节并将其保存到文件中。使用 `uploadImagesToStorage: true` 时，上传的图像则改用 `fileData.fileUri` 提供签名 URL，并用 `fileData.mimeType` 表示媒体类型。请在 URL 过期之前下载这些图像，即创建后 24 小时内。上传失败会使该图像保持内联形式，因此请检查每个 part 中的 `inlineData` 或 `fileData`；文本 part 也可能出现，并且图像不保证是第一个 part。

## 发布前须知

SDK 会生成 `Idempotency-Key` 并在自动重试中复用它。手动重试时，请复用原始 key。Router 最长可保持连接 10 分钟。

请求失败时，Router 会发送 `X-Comfy-Error-Type` 响应头说明原因。`422` 表示 Router 在调用提供商之前就拒绝了输入。生成的资源请及时下载，因为[结果链接会过期](/zh/development/comfy-router/reference#结果资产)。

<CardGroup cols={3}>
  <Card title="请求头" icon="list" href="/zh/development/comfy-router/quickstart">
    身份验证、幂等性、请求 ID、错误分类、重试节奏、消费限额。
  </Card>

  <Card title="使用 Router API" icon="code" href="/zh/development/comfy-router/quickstart">
    模型发现、校验错误、重试与计费。
  </Card>

  <Card title="限制" icon="triangle-exclamation" href="/zh/development/comfy-router/limitations">
    Router 目前不支持的功能，以及替代方案。
  </Card>
</CardGroup>
