9.1 KiB
Gemini-Compatible API Contract
계약 메타
- id:
iop.gemini-compatible-api - boundary:
outer - status: active
- 원본 경로:
apps/edge/internal/openai/routes.goapps/edge/internal/openai/principal.goapps/edge/internal/openai/chat_handler.go
- external caller surface: official Antigravity CLI
agy1.1.12 Gemini API-key provider
읽는 조건
- Gemini Developer API
streamGenerateContent,x-goog-api-key,GOOGLE_GEMINI_BASE_URL,GEMINI_API_KEY,agyAPI-key provider, Gemini-native tool/function call, 또는 Gemini-native SSE ingress를 구현·검증할 때 읽는다. - IOP execution preset을 Gemini-native caller에 노출하거나 공식
agytransport를 변경할 때 읽는다.
범위
IOP Edge가 외부 Gemini-native caller에게 제공하는 초기 호환 표면은 다음 streaming endpoint다.
POST /gemini/{route-id}/v1beta/models/{caller-model}:streamGenerateContent?alt=sse
Content-Type: application/json
x-goog-api-key: <IOP principal token>
{route-id}는 Edge가 인증된 principal에 대해 해석할 direct route 또는 virtual execution-preset id다. URL path segment 하나의 canonical token이어야 한다.{caller-model}은 caller가 선택한 Gemini 모델 id 또는 공식 CLI의 bounded 표시 label이며 관측·호환성 검증 대상이다. URL-encoded space를 포함한Gemini 3.6 Flashlabel을 허용하지만 앞뒤 공백, slash, control character는 거부한다. provider/credential 또는 execution preset 선택 권한은 갖지 않는다.- 현재
agy호환 표면은alt=sse인streamGenerateContent만 지원한다.generateContent, batch, files, cached content, tuning API는 비범위다. - direct route와 marked single-request preset은 모두 기존 Edge route resolution, managed admission, provider-pool, preset coordinator를 사용한다. 별도 Gemini 전용 우회 dispatch를 만들지 않는다.
Caller 설정
공식 agy 1.1.12 API-key provider는 다음 값으로 실행한다.
~/.gemini/antigravity-cli/settings.json:modelProvider는gemini다.GEMINI_API_KEY: upstream provider key가 아니라 IOP principal token이다.GOOGLE_GEMINI_BASE_URL:https://<edge>/gemini/{route-id}다.- 사설 dev CA를 사용하는 경우 caller child에는 표준
SSL_CERT_FILE과NODE_EXTRA_CA_CERTS만 명시적으로 전달한다. --model: 공식 CLI가 인식하는 Gemini 모델 label을 사용한다. dev 호환 확인에서 사용하는 label은Gemini 3.6 Flash다.--effort는 API-key provider 호출에 전달하지 않는다. 요청된 high effort는 인증된 IOP route/preset의 effective binding으로 검증한다.
GEMINI_BASE_URL, AGY_PROVIDER, AGY_OPENAI_BASE_URL, AGY_OPENAI_API_KEY는 이 계약의 transport가 아니다.
Auth 및 credential 경계
x-goog-api-key는 Gemini ingress에서 IOP caller 인증 헤더다. Edge는 이를 SHA-256 projection match에만 사용하고 raw 값을 log, metric, response, task evidence에 남기지 않는다.Authorization: Bearer를 함께 보내면 두 token은 constant-time 비교로 같아야 한다. 다르거나 malformed이면 provider dispatch 전에 Gemini 오류 envelope로401을 반환한다.- managed mode에서 provider credential은 projected slot과 sealed lease에서만 온다. inbound
x-goog-api-key를 upstreamAuthorization또는 upstreamx-goog-api-key로 전달하지 않는다. - legacy mode에서도 inbound
x-goog-api-key를 provider credential로 재사용하지 않는다. provider auth가 별도로 필요하면 기존 명시적 legacy provider-auth 계약만 적용한다. - marked single-request preset은 managed projection과 고정 stage authorization 없이는 fail closed한다.
요청 변환
초기 호환 범위는 official agy 1.1.12가 보내는 다음 top-level field다.
contents[]:role,parts[].text,parts[].functionCall,parts[].functionResponse, optional opaquethoughtSignature. Officialagy1.1.12가 tool 실행 뒤 독립 content에role: model로 보내는functionResponse도 허용하고 기존 호출과 매칭한 Chattoolmessage로 변환한다. 같은 model content에 assistant text/thought/function call과functionResponse를 섞는 모호한 형식은 거부한다.systemInstruction: officialagy의role: user와parts[].textgenerationConfig:candidateCount,maxOutputTokens,stopSequences,temperature,topK,topP,thinkingConfig.includeThoughts,thinkingConfig.thinkingBudget,responseMimeType, 그리고 상호 배타적인responseSchema/responseJsonSchematools[].functionDeclarations[]:name,description, 상호 배타적인parameters/parametersJsonSchema, optional 상호 배타적인response/responseJsonSchematoolConfig.functionCallingConfig.mode
Edge는 이를 기존 Chat/preset ingress의 system/user/assistant/tool message, tool schema와 output cap으로 변환한다. thinkingConfig는 Gemini OpenAI-compatible upstream이 요구하는 extra_body.google.thinking_config로 보존한다. responseMimeType=application/json은 Chat response_format으로 변환하며 schema가 있으면 고정 이름의 strict json_schema, 없으면 json_object를 사용한다. text/plain은 schema를 허용하지 않는다. Gemini 3.6에서 폐기된 temperature, topP, topK는 형식과 범위만 검증하고 Chat upstream에는 전달하지 않는다. 함수명·JSON argument·opaque thought signature는 caller turn 사이에 의미를 바꾸지 않는다. 동의어 schema 필드가 동시에 존재하거나 schema가 JSON object가 아니면 거부한다. 지원하지 않는 content part, duplicate member, 잘못된 role, malformed function payload 또는 둘 이상의 candidate 요청도 provider dispatch 전에 400 INVALID_ARGUMENT으로 거부한다.
SSE 응답
- 성공 stream은
Content-Type: text/event-stream과data: <Gemini GenerateContentResponse JSON>frame을 사용한다. - text delta는
candidates[0].content.parts[].text, reasoning delta는thought=true인 part, 완성된 tool call은functionCallpart로 투영한다. - tool-call argument fragment는 Edge가 bounded buffer에서 완성된 JSON object로 검증한 뒤 한 번만 공개한다. malformed·oversize argument는 성공 tool call로 내보내지 않는다.
- OpenAI
stop,length,tool_callsterminal은 GeminifinishReason의STOP,MAX_TOKENS,STOP으로 닫는다. stream 종료 뒤 별도 합성system idleevent를 만들지 않는다. - provider-reported usage가 있으면
usageMetadata.promptTokenCount,candidatesTokenCount,thoughtsTokenCount,cachedContentTokenCount,totalTokenCount의 존재하는 값만 투영한다. 누락 값을 0으로 발명하지 않는다. - caller disconnect는 기존 request cancellation 경계를 사용하며 이후 frame을 쓰지 않는다.
공식 agy 수동 호출 확인 기준
agy 1.1.12의 각 JSONL record는 event discriminator와 같은 이름의 중첩 payload를 사용한다.
- init:
{"event":"init","init":{...}} - step:
{"event":"step_update","step_update":{"state":...,"step_type":...,"usage":{...}}} - terminal:
{"event":"result","result":{"status":"SUCCESS","duration_seconds":...,"num_turns":...,"usage":{...}}}
수동 확인에서는 중첩 result.status=SUCCESS 한 건과 process exit 0을 성공 terminal로 본다. 구조가 유효한 result.status=ERROR 또는 non-zero exit는 실패로 남기며 성공으로 재해석하지 않는다. 확인 기록에는 raw 응답, tool payload, conversation id, credential을 남기지 않고 caller가 제공한 usage만 원래 단위로 요약한다.
오류
HTTP commit 전 오류는 다음 Gemini envelope 한 건으로 반환한다.
{"error":{"code":400,"message":"request is invalid","status":"INVALID_ARGUMENT"}}
- 인증 실패는
401 UNAUTHENTICATED, route/요청 검증 실패는400 INVALID_ARGUMENT, runtime/provider 실패는502 UNAVAILABLE의 고정된 caller-safe message를 사용한다. - stream commit 뒤 오류는 Gemini
errorpayload 한 건으로 끝내며 raw provider body, endpoint, route binding, credential/slot/lease id, prompt, tool argument/result를 포함하지 않는다.
변경 시 확인할 코드와 테스트
- route/auth:
apps/edge/internal/openai/routes.go,apps/edge/internal/openai/principal.go - Gemini request/SSE bridge:
apps/edge/internal/openai/gemini_handler.go,apps/edge/internal/openai/gemini_bridge.go,apps/edge/internal/openai/gemini_types.go - Edge regression:
apps/edge/internal/openai/gemini_handler_test.go, existing Chat/preset/auth tests - live proof: official
agy --output-format stream-jsonthrough the dev Edge route-specific base URL, with direct and execution-preset effective binding evidence
2026-08-12 dev 검증에서 normal/boundary/auth/tool/SSE 회귀 테스트와 공식 agy 1.1.12 direct·hybrid 실호출이 통과해 이 계약을 active로 전환했다.
2026-08-12 실호출 보정에서 official agy planner의 structured-output schema field를 Chat response_format으로 변환했다.
2026-08-13 실호출 보정에서 official agy 1.1.12의 model-role tool functionResponse continuation을 허용했다.