Gemini OpenAI 형식(Chat)
curl --request POST \
--url https://api.gravitex.ai/v1/chat/completionsimport requests
url = "https://api.gravitex.ai/v1/chat/completions"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.gravitex.ai/v1/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.gravitex.ai/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.gravitex.ai/v1/chat/completions"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.gravitex.ai/v1/chat/completions")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gravitex.ai/v1/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_body대화 및 텍스트
Gemini OpenAI 형식(Chat)
OpenAI 호환 /v1/chat/completions로 Gemini 채팅 및 멀티모달 모델 호출
POST
/
v1
/
chat
/
completions
Gemini OpenAI 형식(Chat)
curl --request POST \
--url https://api.gravitex.ai/v1/chat/completionsimport requests
url = "https://api.gravitex.ai/v1/chat/completions"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.gravitex.ai/v1/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.gravitex.ai/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.gravitex.ai/v1/chat/completions"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.gravitex.ai/v1/chat/completions")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gravitex.ai/v1/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_bodyGoogle Gemini 네이티브 프로토콜은 Gemini Native를 참조하세요. 일반 다중 모델 Chat Completions는 OpenAI Chat Completions를 참조하세요.
엔드포인트: 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[].texttype:"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문자열에 포함된Markdown 이미지는image_url과 동일하게inlineDatapart로 변환됩니다.
4. 도구 전달
"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"] }
}
}
]
function 항목은 functionDeclarations를 사용합니다.
5. extra_body — Gemini 네이티브 매개변수 전달
extra_body.google.* 이 네임스페이스 아래의 모든 필드는 Gemini 네이티브 API로 전달됩니다.
{
"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 전달 예시
{
"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
{
"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에markdown으로 삽입됩니다. id는 스트리밍 청크 전체에서 안정적입니다.
7. Usage
response.usage 전체 필드:
{
"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 사고 모드가 있는 텍스트 채팅
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)
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 컨텍스트
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 스트리밍 채팅
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 사고 설정으로 매핑됩니다:
{
"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(전체 제어)
{
"model": "gemini-3.5-flash",
"messages": [{"role": "user", "content": "..."}],
"extra_body": {
"google": {
"thinking_config": {
"thinking_level": "HIGH",
"include_thoughts": true
}
}
}
}
thinking_budget 대신 thinking_level을 사용합니다:
{
"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 필드에 담깁니다:
{
"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으로 | 스트리밍은 로 삽입; 비스트리밍은 멀티모달 배열을 반환할 수 있음 |
| 이미지가 아닌 미디어를 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 설정 |
