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

# Gemini OpenAI 형식(Chat)

> OpenAI 호환 /v1/chat/completions로 Gemini 채팅 및 멀티모달 모델 호출

<Note>
  Google Gemini 네이티브 프로토콜은 [Gemini Native](/ko/api-reference/endpoint/gemini-native)를 참조하세요. 일반 다중 모델 Chat Completions는 [OpenAI Chat Completions](/ko/api-reference/endpoint/chat-openai)를 참조하세요.
</Note>

> 엔드포인트: `POST https://api.gravitex.ai/v1/chat/completions`

***

## 1. 모델 카테고리

| Category  | Example models                                                                                       | Routing                                        | Notes                        |
| --------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ---------------------------- |
| 채팅 / 멀티모달 | `gemini-3.5-flash`、`gemini-3.1-pro-preview`、`gemini-3-flash-preview`、`gemini-3.1-flash-lite-preview` | `:generateContent` 또는 `:streamGenerateContent` | 스트리밍은 클라이언트 `stream` 플래그를 따름 |

***

## 2. 엔드포인트 및 인증

```
POST https://api.gravitex.ai/v1/chat/completions
Authorization: Bearer sk-<your-token>
Content-Type: application/json
```

***

## 3. OpenAI 필드 → Gemini 매핑

| OpenAI field                                                          | Gemini field                                                                | Description                                                                                                                        |
| --------------------------------------------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `model`                                                               | URL 경로의 `models/<model>`                                                    | 모델 이름이 Gemini로 그대로 전달됨                                                                                                             |
| `messages`                                                            | `contents[]` + `systemInstruction`                                          | `system`/`developer` 역할 → `systemInstruction`; `assistant` → `model`; `tool`/`function` → `functionResponse`                       |
| `stream`                                                              | URL `:streamGenerateContent` vs `:generateContent`                          |                                                                                                                                    |
| `temperature`                                                         | `generationConfig.temperature`                                              |                                                                                                                                    |
| `top_p`                                                               | `generationConfig.topP`                                                     |                                                                                                                                    |
| `max_tokens` / `max_completion_tokens`                                | `generationConfig.maxOutputTokens`                                          |                                                                                                                                    |
| `seed`                                                                | `generationConfig.seed`                                                     |                                                                                                                                    |
| `stop`                                                                | `generationConfig.stopSequences`                                            | 최대 5개; 초과 항목은 잘림                                                                                                                   |
| `response_format.type = "json_schema"/"json_object"`                  | `generationConfig.responseMimeType = "application/json"` + `responseSchema` | Gemini가 인식하지 못하는 `additionalProperties` 등 필드는 자동 제거됨                                                                               |
| `tools`의 `function`                                                   | `tools[].functionDeclarations`                                              | §4 참조                                                                                                                              |
| `tools`의 세 가지 특수 이름 (`googleSearch` / `codeExecution` / `urlContext`) | `tools[].googleSearch` / `tools[].codeExecution` / `tools[].urlContext`     | §4 참조                                                                                                                              |
| `tool_choice`                                                         | `toolConfig.functionCallingConfig`                                          | `"auto"→AUTO`、`"none"→NONE`、`"required"→ANY`; 객체 형식 `{type:"function",function:{name:"X"}}` → `ANY` + `allowedFunctionNames=["X"]` |
| `frequency_penalty`                                                   | `generationConfig.frequencyPenalty`                                         |                                                                                                                                    |
| `presence_penalty`                                                    | `generationConfig.presencePenalty`                                          |                                                                                                                                    |
| `top_k`                                                               | `generationConfig.topK`                                                     |                                                                                                                                    |
| `n`                                                                   | `generationConfig.candidateCount`                                           | `n > 1`일 때만 적용, 후보 응답 수 제어                                                                                                         |
| `logprobs`                                                            | `generationConfig.responseLogprobs`                                         | logprobs 반환 여부                                                                                                                     |
| `top_logprobs`                                                        | `generationConfig.logprobs`                                                 | top logprobs 수                                                                                                                     |
| `modalities`                                                          | `generationConfig.responseModalities`                                       | JSON 배열(예: `["text","audio"]`)                                                                                                     |
| `audio`                                                               | `generationConfig.speechConfig`                                             | TTS 음성 설정, Gemini speechConfig로 직접 전달                                                                                              |

`messages.content`는 멀티모달 배열(OpenAI v2)을 지원합니다:

* `type:"text"` → `parts[].text`
* `type:"image_url"` / `type:"input_audio"` / `type:"file"` → 다운로드/디코딩 후 MIME 허용 목록에 따라 `parts[].inlineData`로 변환:
  * 이미지: `image/png`、`image/jpeg`、`image/jpg`、`image/webp`、`image/heic`、`image/heif`
  * 오디오: `audio/mpeg`、`audio/mp3`、`audio/wav`
  * 비디오: `video/mp4`、`video/mov`、`video/mpeg`、`video/mpg`、`video/avi`、`video/wmv`、`video/mpegps`、`video/flv`
  * 문서: `application/pdf`、`text/plain`
* `content` 문자열에 포함된 `![alt](data:image/...;base64,...)` Markdown 이미지는 `image_url`과 동일하게 `inlineData` part로 변환됩니다.

***

## 4. 도구 전달

```jsonc theme={null}
"tools": [
  { "type": "function", "function": { "name": "googleSearch" } },     // Google Search 활성화
  { "type": "function", "function": { "name": "codeExecution" } },    // 코드 실행 활성화
  { "type": "function", "function": { "name": "urlContext" } },       // URL 컨텍스트 활성화
  { "type": "function", "function": {                                  // 표준 function calling
      "name": "get_weather",
      "description": "Get weather",
      "parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] }
    }
  }
]
```

세 가지 특수 이름은 Gemini 네이티브 도구로 매핑되며, 다른 `function` 항목은 functionDeclarations를 사용합니다.

***

## 5. extra\_body — Gemini 네이티브 매개변수 전달

`extra_body.google.*` 이 네임스페이스 아래의 모든 필드는 Gemini 네이티브 API로 전달됩니다.

```jsonc theme={null}
{
  "model": "gemini-3.5-flash",
  "messages": [{ "role": "user", "content": "Draw a shiba inu" }],
  "extra_body": {
    "google": {
      "generationConfig": { /* ... */ },
      "safetySettings":   [ /* ... */ ],
      "tools":            [ /* ... */ ],
      "systemInstruction": { /* ... */ },
      "thinking_config":  { /* ... */ }
    }
  }
}
```

### 5.1 두 가지 전달 경로

| 경로                           | Field                                     | Behavior                                                                                                                                                                                                                                                                                  |
| ---------------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ① **snake\_case 허용 목록(레거시)** | `extra_body.google.thinking_config`       | 명시적으로 파싱 후 해당 필드에 기록됩니다. **snake\_case 키만** 허용하며, 타입이 맞지 않으면 오류 없이 건너뛰고 ②로 폴백합니다.                                                                                                                                                                                                         |
| ② **전체 전달(스키마 검증 없음)**       | `extra_body.google.*` 아래 **①을 제외한** 임의 필드 | `extra_body.google` 하위 트리 전체(`thinking_config` 제외)를 최종 Gemini 요청 JSON에 **깊은 병합**합니다. 필드명은 **Gemini 공식 네이티브 camelCase**로 작성합니다(`generationConfig`、`safetySettings`、`tools`、`systemInstruction`、`toolConfig`、`cachedContent`、`responseModalities`、`responseSchema`、`responseJsonSchema` 등). |

### 5.2 thinking\_config (snake\_case 허용 목록)

| Field              | Type   | Gemini field      | Description                                               |
| ------------------ | ------ | ----------------- | --------------------------------------------------------- |
| `thinking_budget`  | int    | `thinkingBudget`  | 사고 토큰 예산. > 0이면 `include_thoughts` true; 0 또는 음수면 사고 비활성화 |
| `include_thoughts` | bool   | `includeThoughts` | `reasoning_content`에 사고 추적 반환                             |
| `thinking_level`   | string | `thinkingLevel`   | 사고 수준(예: `"HIGH"`)                                        |

> `extra_body.google`을 전달하면 시스템 자동 사고 체인 어댑터가 비활성화되며, 모든 사고 동작은 호출자가 제어합니다.

### 5.3 깊은 병합 규칙

* `extra_body.google`(위 snake\_case 키 제외)을 패치로 취급합니다.
* OpenAI 필드로 구성된 Gemini 요청을 base로 사용합니다.
* **deep merge**:
  * 동일 키, 둘 다 map → 재귀 병합;
  * 기타 타입(스칼라, 배열, null) → 패치가 base를 직접 덮어씀;
  * base에만 있는 키는 유지됩니다.
* 병합된 본문이 업스트림으로 전송됩니다 — 네이티브 Gemini 호출과 동등합니다.

> `extra_body.google.generationConfig.maxOutputTokens`로 OpenAI 필드 `max_tokens`로 설정한 값을 덮어쓸 수 있고, `extra_body.google.safetySettings`로 플랫폼 기본 안전 설정을 완전히 교체할 수 있습니다. 향후 추가되는 Gemini 필드도 코드 변경 없이 바로 사용할 수 있습니다.

### 5.4 전달 예시

```json theme={null}
{
  "model": "gemini-3.5-flash",
  "messages": [{ "role": "user", "content": "Write an article about AI" }],
  "extra_body": {
    "google": {
      "generationConfig": {
        "temperature": 1,
        "topP": 0.95,
        "maxOutputTokens": 32768
      },
      "safetySettings": [
        { "category": "HARM_CATEGORY_HATE_SPEECH",       "threshold": "OFF" },
        { "category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "OFF" },
        { "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "OFF" },
        { "category": "HARM_CATEGORY_HARASSMENT",        "threshold": "OFF" }
      ]
    }
  }
}
```

***

## 6. 응답 형식

### 6.1 비스트리밍 `chat.completion`

```json theme={null}
{
  "id": "sXIFar39H4K0694P2MWmWQ",
  "object": "chat.completion",
  "created": 1747299537,
  "model": "gemini-3.5-flash",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello, how can I help?",
        "reasoning_content": "User is greeting..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": { /* §7 참조 */ }
}
```

* `id` = 업스트림 `responseId`(로그 `request_id`와 일치); 없으면 `chatcmpl-*`로 폴백.
* `reasoning_content`: 사고 텍스트(`include_thoughts:true`일 때만).
* `executable_code` / `code_execution_result`: 텍스트에 markdown 코드 블록으로 삽입.
* 이미지가 아닌 미디어(오디오 등)는 markdown `[media](data:...)` 형식으로 삽입.
* `finish_reason` 매핑: `STOP→stop`, `MAX_TOKENS→length`, safety/recitation/...→`content_filter`, `functionCall`→`tool_calls`.

### 6.2 스트리밍 `chat.completion.chunk`

* 스트리밍에서 `delta.content`는 문자열입니다(배열 아님).
* 이미지는 `delta.content`에 `![image](data:...)` markdown으로 삽입됩니다.
* `id`는 스트리밍 청크 전체에서 안정적입니다.

***

## 7. Usage

`response.usage` 전체 필드:

```jsonc theme={null}
{
  "prompt_tokens": 1127,
  "completion_tokens": 2050,     // includes reasoning tokens
  "total_tokens": 2273,

  "prompt_tokens_details": {
    "cached_tokens": 0,
    "text_tokens":   7,
    "audio_tokens":  0,
    "image_tokens":  1120
  },

  "completion_tokens_details": {
    "text_tokens":      26,
    "audio_tokens":     0,
    "image_tokens":     1120,
    "reasoning_tokens": 904      // thinking tokens, shown separately
  }
}
```

### 7.1 사고 토큰 집계 방식

* `reasoning_tokens`는 가시성을 위해 별도 표시됩니다.
* `completion_tokens`는 `reasoning_tokens`를 **포함**합니다(OpenAI 의미론; 과금은 `completion_tokens` 기준).

### 7.2 출력 토큰 분류

시스템이 출력에 따라 토큰 유형을 자동 분류합니다:

| Output | Bucket           |
| ------ | ---------------- |
| 이미지 출력 | → `image_tokens` |
| 텍스트만   | → `text_tokens`  |

> 분류는 모델명이 아닌 출력 기준입니다 — 모델명에 "image"가 있어도 순수 텍스트 응답은 텍스트로 과금됩니다.

### 7.3 모달리티 대소문자 처리

`image` / `IMAGE` 대소문자 변형을 모두 허용합니다.

***

## 8. 로깅 및 대조

업스트림 토큰 사용량은 요청별로 로깅됩니다:

* `responseId`는 `response.id` 및 로그 `request_id`와 일치합니다.

***

## 9. 예시

### 9.1 사고 모드가 있는 텍스트 채팅

```bash theme={null}
curl -X POST https://api.gravitex.ai/v1/chat/completions \
  -H "Authorization: Bearer sk-xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.5-flash",
    "messages": [{"role": "user", "content": "Prove Fermat's Last Theorem"}],
    "extra_body": {
      "google": {
        "thinking_config": { "thinking_level": "HIGH", "include_thoughts": true }
      }
    }
  }'
```

### 9.2 멀티모달 입력(텍스트 + 이미지 URL)

```bash theme={null}
curl -X POST https://api.gravitex.ai/v1/chat/completions \
  -H "Authorization: Bearer sk-xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.5-flash",
    "messages": [
      { "role": "user", "content": [
          { "type": "text", "text": "What is in this image?" },
          { "type": "image_url", "image_url": { "url": "https://example.com/cat.jpg" } }
        ]
      }
    ]
  }'
```

### 9.3 Google Search + URL 컨텍스트

```bash theme={null}
curl -X POST https://api.gravitex.ai/v1/chat/completions \
  -H "Authorization: Bearer sk-xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.5-flash",
    "messages": [{"role": "user", "content": "Big tech news today?"}],
    "tools": [
      { "type": "function", "function": { "name": "googleSearch" } },
      { "type": "function", "function": { "name": "urlContext" } }
    ]
  }'
```

### 9.4 스트리밍 채팅

```bash theme={null}
curl -N -X POST https://api.gravitex.ai/v1/chat/completions \
  -H "Authorization: Bearer sk-xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.5-flash",
    "messages": [{"role": "user", "content": "Write a five-character quatrain"}],
    "stream": true,
    "stream_options": { "include_usage": true }
  }'
```

***

## 10. OpenAI 전용 매개변수(Gemini에서 무시됨)

다음 OpenAI 표준 매개변수는 Gemini API에 대응 필드가 없어 전달해도 오류 없이 무시됩니다:

| OpenAI field                                  | Description                                    |
| --------------------------------------------- | ---------------------------------------------- |
| `logit_bias`                                  | Gemini에는 logit bias 없음                         |
| `prediction`                                  | 예측 출력(Gemini 대응 없음)                            |
| `user`                                        | OpenAI 사용자 식별자(Gemini 대응 없음)                   |
| `parallel_tool_calls`                         | 병렬 도구 호출 제어 없음                                 |
| `verbosity`                                   | GPT-5 전용                                       |
| `service_tier`                                | OpenAI 서비스 티어                                  |
| `safety_identifier`                           | OpenAI 안전 식별자                                  |
| `store`                                       | OpenAI store 플래그                               |
| `prompt_cache_key` / `prompt_cache_retention` | OpenAI 캐시 제어                                   |
| `web_search_options`                          | 대신 `tools`의 `googleSearch` / `urlContext` 사용   |
| `functions` / `function_call`                 | 레거시 functions API — `tools` + `tool_choice` 사용 |

***

## 11. FAQ — Thinking / Reasoning

### Q1: OpenAI 형식에서 Gemini 사고 길이를 제어할 수 있나요?

**예.** 세 가지 방법이 있습니다:

#### 방법 1: `reasoning_effort`(OpenAI 표준 필드, 가장 간단)

OpenAI `reasoning_effort`를 전달하면 Gemini 사고 설정으로 매핑됩니다:

```json theme={null}
{
  "model": "gemini-3.5-flash",
  "messages": [{"role": "user", "content": "Prove Fermat's Last Theorem"}],
  "reasoning_effort": "high"
}
```

매핑(자동):

| `reasoning_effort` | Gemini 3 시리즈               |
| ------------------ | -------------------------- |
| `"low"`            | `thinkingLevel = "LOW"`    |
| `"medium"`         | `thinkingLevel = "MEDIUM"` |
| `"high"`           | `thinkingLevel = "HIGH"`   |

#### 방법 2: 모델명 접미사

| Suffix           | Behavior                                                   |
| ---------------- | ---------------------------------------------------------- |
| `-thinking`      | 사고 활성화 + `includeThoughts: true`; 예산은 `max_tokens` 비율에서 결정 |
| `-thinking-<숫자>` | 사고 활성화; 예산 = 숫자(클램핑됨)                                      |
| `-nothinking`    | 사고 비활성화(`thinkingBudget = 0`); Gemini 2.5만 지원              |

예: `gemini-3.5-flash-thinking-16384` → 사고 활성화, 예산 16384.

#### 방법 3: `extra_body.google.thinking_config`(전체 제어)

```json theme={null}
{
  "model": "gemini-3.5-flash",
  "messages": [{"role": "user", "content": "..."}],
  "extra_body": {
    "google": {
      "thinking_config": {
        "thinking_level": "HIGH",
        "include_thoughts": true
      }
    }
  }
}
```

Gemini 3 시리즈는 `thinking_budget` 대신 `thinking_level`을 사용합니다:

```json theme={null}
{
  "extra_body": {
    "google": {
      "thinking_config": {
        "thinking_level": "HIGH",
        "include_thoughts": true
      }
    }
  }
}
```

> **우선순위**: `extra_body.google.thinking_config` > `reasoning_effort` > 접미사. `extra_body.google`을 전달하면 시스템 자동 사고 체인 어댑터가 비활성화되며, 모든 사고 동작은 호출자가 완전히 제어합니다.

### Q2: 사고 출력을 어떻게 읽나요?

`include_thoughts: true`로 설정하면 사고 과정이 응답의 `reasoning_content` 필드에 담깁니다:

```json theme={null}
{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "Final answer...",
      "reasoning_content": "Thinking trace..."
    }
  }]
}
```

스트리밍에서는 `delta.reasoning_content`로 사고 내용이 도착합니다.

### Q3: Gemini 2.5 vs 3 사고 차이

| Feature                    | Gemini 2.5                 | Gemini 3                                      |
| -------------------------- | -------------------------- | --------------------------------------------- |
| 제어                         | `thinkingBudget`(정수 토큰 예산) | `thinkingLevel`(enum MINIMAL/LOW/MEDIUM/HIGH) |
| 사고 비활성화 가능                 | 예 (`thinkingBudget = 0`)   | **비활성화 불가**(플랫폼 제한)                           |
| `reasoning_effort: "none"` | 지원(사고 비활성화)                | **미지원**                                       |

***

## 12. 알려진 제한 사항

| Limitation                                | Description                                                           |
| ----------------------------------------- | --------------------------------------------------------------------- |
| 스트리밍 이미지를 markdown으로                      | 스트리밍은 `![image](data:...)`로 삽입; 비스트리밍은 멀티모달 배열을 반환할 수 있음              |
| 이미지가 아닌 미디어를 markdown 텍스트로                | 오디오 등 비이미지 미디어는 markdown 형식으로 삽입                                      |
| `extra_body.google.*` 완전 전달, **필드 검증 없음** | 필드 오타나 값 타입 오류는 그대로 업스트림에 전달되며 Gemini가 오류를 반환합니다. 호출자가 필드 정확성을 책임집니다. |
| 플랫폼 기본 안전 설정                              | `extra_body.google.safetySettings`로 재정의 가능                            |

***

## 13. 문제 해결

| Issue                            | What to check                                                                                                                               |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Usage가 이상함                       | 로그와 업스트림 usage 대조                                                                                                                           |
| `response.id`가 로그와 불일치           | 발생하지 않아야 함 — 지원팀에 문의                                                                                                                        |
| 이미지가 배열이 아닌 markdown인 이유         | 스트리밍은 markdown 사용; 비스트리밍은 멀티모달 배열을 사용할 수 있음                                                                                                 |
| `extra_body.google.xxx`가 적용되지 않음 | 1) `extra_body`는 JSON 객체여야 하며 문자열이면 안 됨; 2) `xxx`가 `google` 네임스페이스 아래인지 확인; 3) `thinking_config`는 snake\_case, 다른 필드는 Gemini 네이티브 camelCase |
| 기본 안전 설정 재정의                     | `extra_body.google.safetySettings` 설정                                                                                                       |
