Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs-site/src/content/docs/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ eligible Enterprise accounts. Cloud Code Assist serves Argon only to the Antigra
OpenCodex sends Argon requests and model discovery with the CLI User-Agent
(`antigravity/cli/1.3.4`) and keeps the IDE User-Agent for every other model.

On Vertex AI, `gemini-4-argon` receives images returned by tools, such as screenshots, inside
the matching function response. This avoids the upstream 400 error "Requests ending with a
model turn are not supported" after an image-producing tool call while preserving the images.
Images from duplicate results or results without a matching call are preserved in a separate
message after the tool responses, with a marker explaining that they could not be paired.
No configuration change is needed; this handling is specific to Vertex Argon.

For Gemini on Antigravity, including Argon, provider-marked thought summaries stay separate
from the answer: Responses uses reasoning summary items, Chat Completions uses
`reasoning_content`, and Messages uses `thinking` blocks. Clients decide how to display those
Expand Down
7 changes: 7 additions & 0 deletions docs-site/src/content/docs/ko/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ ocx logout <provider>

Google Antigravity 계정·제공자 할당량 확인은 모델 목록 폴백을 포함해 고정된 Google 회계 엔드포인트를 사용합니다. 해당 목적지의 투명 Fake-IP DNS를 지원하며 TLS 검증, 리다이렉트 거부, 사설 주소 검사는 유지합니다. 사용자 지정 base URL은 모델 요청에만 적용되며 할당량 목적지는 바꾸지 않습니다. `NO_PROXY`는 기존 직접 연결 정책을 유지합니다.

Vertex AI의 `gemini-4-argon`에는 스크린샷 같은 도구 결과 이미지를 해당 함수 응답 안에 담아
전송합니다. 이미지가 있는 도구 호출 뒤에 발생하는 400 오류
"Requests ending with a model turn are not supported"를 방지하며 이미지를 보존합니다.
중복된 결과나 일치하는 호출이 없는 결과의 이미지는 도구 응답 뒤의 별도 메시지로 보존하고,
호출과 연결할 수 없었다는 표시를 남깁니다.
별도의 설정 변경은 필요하지 않으며, 이 처리는 Vertex Argon에만 적용됩니다.

### Google 도구 스키마 손실 진단

Google 도구 선언은 선택된 엔드포인트 클래스에 맞춰 컴파일됩니다. `ocx debug provider on`,
Expand Down
23 changes: 18 additions & 5 deletions src/adapters/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,13 +333,20 @@ function geminiToolResultParts(
msg: OcxToolResultMessage,
wireName: string,
wireCallId: string,
nestToolResultImages: boolean,
): unknown[] {
const functionResponse: Record<string, unknown> = {
name: wireName,
response: { result: geminiToolResultText(msg.content) },
id: wireCallId,
};
return [{ functionResponse }, ...toolResultImageParts(msg.content)];
const images = toolResultImageParts(msg.content);
// Vertex Argon rejects image siblings beside a functionResponse as an unsupported model tail.
if (nestToolResultImages && images.length > 0) {
functionResponse.parts = images;
return [{ functionResponse }];
}
return [{ functionResponse }, ...images];
}

function geminiMissingToolResultPart(wireName: string, wireCallId: string): unknown {
Expand Down Expand Up @@ -370,6 +377,7 @@ function messagesToGeminiFormat(
identityModelId: string,
stripRejectedClaudeSdkParagraph = false,
isCloudCodeAssist = false,
nestToolResultImages = false,
): { systemInstruction?: unknown; contents: unknown[]; replayedCallIds: string[] } {
// Neutralize Codex's GPT-5 identity line (Gemini/Antigravity share this path) so a routed model
// never misreports as GPT-5/OpenAI, and never leaks the proxy identity upstream.
Expand Down Expand Up @@ -551,13 +559,17 @@ function messagesToGeminiFormat(
const responseParts: unknown[] = [];
for (const call of toolCalls) {
const result = resultsById.get(call.wireCallId);
if (result) responseParts.push(...geminiToolResultParts(result, call.wireName, call.wireCallId));
if (result) responseParts.push(...geminiToolResultParts(result, call.wireName, call.wireCallId, nestToolResultImages));
else responseParts.push(geminiMissingToolResultPart(call.wireName, call.wireCallId));
}
for (const orphan of orphanResults) {
responseParts.push(...geminiOrphanToolResultParts(orphan));
}
const orphanParts = orphanResults.flatMap(geminiOrphanToolResultParts);
contents.push({ role: "user", parts: responseParts });
// Orphan images cannot share Argon's response batch or claim another call's ownership.
if (nestToolResultImages && orphanParts.some(part => typeof part === "object" && part !== null && "inline_data" in part)) {
contents.push({ role: "user", parts: orphanParts });
} else {
responseParts.push(...orphanParts);
}
i = j - 1;
}
break;
Expand Down Expand Up @@ -1026,6 +1038,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
identityModelId,
stripRejectedClaudeSdkParagraph,
isCloudCodeAssist,
provider.googleMode === "vertex" && parsed.modelId === "gemini-4-argon",
);
lastInjectedCallIds = [...replayedCallIds];
lastReasoningReplayScope = parsed._reasoningReplayScope;
Expand Down
16 changes: 16 additions & 0 deletions structure/decisions/ADR-7279-vertex-argon-tool-images.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# ADR-7279: decision recorded under "Google tool-result adjacency repair"

- Contract owner: [providers/google.md](../providers/google.md#google-tool-result-adjacency-repair)
- Purpose: allow Vertex `gemini-4-argon` to continue after a tool returns an image (#7279).
- Prior constraint: tool images were sibling `inline_data` parts beside `functionResponse`.
A synthetic live request ending in `user` still received HTTP 400, "Requests ending with
a model turn are not supported"; adding final user text did not repair it.
- Alternatives: discard images, move them to a separate user turn, or nest them inside the
response. Discarding loses information; separate turns obscure ownership in parallel results.
- Choice: nest matched result images in `functionResponse.parts` only for Vertex
`gemini-4-argon`, preserving the existing text, IDs, names, order and image bytes. The nested
synthetic request succeeded and reported IMAGE input tokens. Other routes retain their existing
representation because this incompatibility was reproduced only on Vertex Argon.
- Consequences: no new configuration, retry or shared-history mutation. Orphan results still
degrade to marked text and sibling images; existing adjacency and thought-signature repairs
remain intact. Revisit the model scope when another route has equivalent upstream evidence.
15 changes: 15 additions & 0 deletions structure/decisions/ADR-7280-vertex-argon-orphan-images.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# ADR-7280: decision recorded under "Google tool-result adjacency repair"

- Contract owner: [providers/google.md](../providers/google.md#google-tool-result-adjacency-repair)
- Purpose: preserve adjacent orphan images without reopening the Vertex Argon 400 from #7279.
- Prior constraint: ADR-7279 nested matched images, but duplicate or mismatched result images
still shared their user turn with the function responses. Both cases reproduced the same
upstream 400; moving the orphan content to a following user turn succeeded.
- Alternatives: discard orphan images, attach them to a matched response, or preserve them in
a separate turn. The first loses content; the second invents ownership.
- Choice: for Vertex `gemini-4-argon` only, move the entire adjacent orphan batch to a following
user turn when it contains a representable image. Keep the complete response batch adjacent
to its calls and preserve the orphan markers, image bytes, and orphan result order.
- Consequences: no history mutation, invented success, configuration or retry. Text-only and
standalone orphans keep their prior shape; other Google routes are unchanged. The existing
Vertex regression file covers duplicates, mismatches, missing results and route isolation.
11 changes: 10 additions & 1 deletion structure/providers/google.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,18 @@ Google-family requests serialize a model tool-call turn and its results as one a
`model -> user` pair. The user turn contains exactly one `functionResponse` for every representable
call in original call order. Missing results use an explicit unknown-history marker; duplicate,
mismatched, and standalone results become marked text instead of unpaired function responses.
Representable data-URL images remain sibling `inline_data` parts in either case.
For Vertex `gemini-4-argon`, matched results carry representable data-URL images inside
`functionResponse.parts`, preserving each image's call ownership. Sibling images on that route can
produce a misleading "Requests ending with a model turn are not supported" error even when the
last turn is `user`. When an adjacent orphan batch contains representable images on that route,
the entire marked orphan batch follows the complete function-response batch in a separate user
turn, preserving orphan result order and image bytes without assigning an orphan image to another call. Text-only
orphans, standalone orphans and other routes retain their existing representation.
`tests/adapters/google/google-vertex-thought-signature.test.ts` covers the scoped wire format.

> Decision record: [ADR-0058](../decisions/ADR-0058-google-tool-result-adjacency-repair.md)
> Decision record: [ADR-7279](../decisions/ADR-7279-vertex-argon-tool-images.md)
> Decision record: [ADR-7280](../decisions/ADR-7280-vertex-argon-orphan-images.md)

## Google opening functionCall repair

Expand Down
Loading
Loading