feat(wire): native Google/Gemini wire codec (#317) - #318
Conversation
[bot] 🏷 Review started on Initial state check:
Now running typecheck + full test suite + build locally and reviewing the codec against the wire contracts. Full report to follow in this thread. |
A Gemini request body (contents[], systemInstruction, generationConfig) had no
codec, so detectWireFormat returned null for it, the proxy forwarded the request
verbatim and the session never entered the core: no identity, no folding, no
compression. This adds the codec.
- src/wire/google.ts: googleToCore/coreToGoogle over contents[]. googleToCore
hoists systemInstruction.parts into GoogleFlat.systemText, maps role:"model"
to assistant and any other role to user, expands functionResponse parts into
tool-result cores BEFORE that content's own user core (a content carrying only
functionResponses yields no user core), maps {functionCall} to a tool-call and
thought:true to reasoning. coreToGoogle merges same-side runs into one content
because Gemini rejects non-alternating roles: an assistant run becomes one
role:"model" part list ordered reasoning, text, tool-call; a user-side run
(user + tool-result) becomes one role:"user" with the functionResponse parts
first. A functionResponse without a wire id pairs with the earliest unmatched
call of the same name in one forward pass, so pairing stays deterministic for
content-derived messages.
- Sidecars rawGoogleParts and googleThoughtSignature carry what the core does
not model (inlineData, executableCode, call ids, thought signatures) so a
rebuilt request stays faithful for Gemini 3, which validates thoughtSignature
on every replayed part.
- formats: WIRE_FORMATS and detectWireFormat learn "google" (an array
`contents`, checked before the input/messages branches).
- strip-images: inlineData/fileData parts are replaced by a text placeholder.
- compress-tools: the ACP tool declarations in Gemini's shape (flat
`parameters`), so the compress/decompress/search_context/acp_status/absorb
tools can be injected on this wire.
6b99de6 to
a2ee01c
Compare
|
Rebased onto current |
[bot] 🏷 Re-reviewed the rebased head Verified locally (head
What I found solid in the codec itself:
One cosmetic nit (non-blocking): the PR introduces prettier drift in 3 files that were clean before — Process notes: this is a Conclusion: code reviewed line-by-line, all checks green on the current master base, no contract concerns (§7.3 untouched — additive wire only). Recommend merge. 中文摘要:逐行审查了 Google/Gemini 原生 wire codec 的 rebase 后版本(a2ee01c),typecheck/test(818/818)/build 全部本地复现通过,diff 干净无无关改动,round-trip 保真设计(raw parts sidecar + 字节级回环测试)扎实,仅发现 3 处可选的 prettier 换行问题(CI 不强制,不阻塞),建议合并。 |
|
human here: tested end 2 end in a live session, not just with mock-up. hope for quick merge and upstream integration because until then i have to maintain a weird local setup where i merge upstream changes with my local fixes. |
[bot] 🏷 Thanks — live end-to-end confirmation against a real session is exactly the piece that mock tests can't cover, so that closes out the last open question on my side. My review stands as posted: all checks green on the current master base ( One thing I can't do: I'm not allowed to merge PRs — that's a human-only operation under this repo's rules, even when asked directly. Please merge it yourself here: So your local fork-setup workaround can go away, the fastest path after the merge is: cut the next release branch ( 中文摘要:用户确认已做真实会话端到端验证;我的审查结论不变(全绿、建议合并),但按仓库规则我不能执行合并,请本人到 PR 页面自行合并,合并后我可以协助发版和适配端升级。 |
…anglei#317) The raw-passthrough path was covered but not the changed-text rebuild (kernel refs/truncation rewrite), which is the production compression flow: text part re-renders from core text + sidecar signature, sibling non-text raw parts preserved verbatim.
|
Both open items are confirmed on our side, so neither gates the merge. 2. URL detection. Correct, it lives in the proxy. 3. Mirror parity. Checked before answering, and it does not affect this PR. Nothing in either consumer calls a On the CI auth item: understood, and the per-PR prerelease is not consumed by anything of ours — the proxy pins a released kernel version and bumps it only after a release — so it does not gate us. The merge decision and the release path you sketched are with the maintainer. Head |
[bot] 🏷 Verified head And thanks for closing out the two design questions with evidence rather than assertion:
So the PR-side work is done and green. What remains is entirely on the maintainer side: merge (human-only operation — I can't do it), then the release path I sketched before (release branch → CI publish → 中文摘要:已本地验证新 head 006f91a(typecheck 干净、819/819、build 通过,与用户结果一致),新增测试正确覆盖了 contentParts 编辑文本重渲染分支;用户对 URL 检测在代理侧、mirror 无消费者两点均给出证据,予以接受,PR 侧工作已完成且全绿,剩余仅维护者合并及后续发版流程。 |
|
继续 |
Solution
Native support for the Gemini wire in the kernel, so a Gemini conversation can be detected, folded and re-emitted instead of passing through the proxy verbatim.
src/wire/google.ts(new):googleToCore/coreToGoogle,googleSystemText,injectGoogleSystem,conversationSignalGoogle, withGooglePart,GoogleContent,GoogleFunctionDeclaration,GoogleTool,GoogleSystemInstruction,GoogleRequestBodytypes and aGoogleFlatview for the parts that differ per wire.src/wire/formats.ts:WIRE_FORMATSgainsgoogle, anddetectWireFormattests an arraycontentsbefore theinputandmessagesshapes.src/wire/bili-message.ts: two sidecars,rawGoogleParts?: unknown[]afterrawResponsesItemandgoogleThoughtSignature?: stringafterthinkingSignature, so a fold round-trips the original part array and its signature.src/wire/strip-images.ts:inlineDataandfileDataparts collapse to[{text:"[image]"}], matching the other wires.src/wire/index.ts: re-export.src/compress-tools.ts: the tool declarations for this wire, in the flat{name, description, parameters}shape Gemini requires. Descriptions are taken from the*_OPENAIdeclarations rather than duplicated.Why native rather than the OpenAI-compatibility endpoint
/v1beta/openai/chat/completionswould be a far smaller change, but that translation dropsthoughtSignatureandextra_content. Gemini 3 requires a signature on every replay of a function-call part, so a folded conversation re-emitted through the compatibility layer is rejected withINVALID_ARGUMENTas soon as the client does tool calls, which is exactly the case this proxy exists to serve. The native wire keeps every part intact.Wire constraints encoded in the codec
modelcontents or two consecutiveusercontents, and an omp-style conversation produces both after a tool round.systemInstructionis the system channel, so system text is hoisted out of the fold space and re-injected there rather than being carried as a content.functionCallandfunctionResponsepair byname; theidis optional and not always present.thought: trueparts are not visible text, so every text-shaped consumer skips them.usageMetadata, where the prompt count includes cached tokens.Tests
tests/wire-google.test.ts(new, 14 tests): round-trips of text, thinking parts, signatures, guarded calls and responses, system hoisting and re-injection, same-side merging, the conversation signal, and malformed bodies.tests/wire-formats.test.ts: the pinned format list gainsgoogle.Verification
npm test806/806 on this branch, 820/820 on the tree merged with currentmaster;npm run typecheckclean. A companionbillion-contextPR consumes the codec and was verified againstgemini-3.8-flashend to end: detection from the URL, a proxy-ownedcompresscall folding a 97k-token conversation, and a re-emitted request carryingsystemInstructionwith valid alternatingcontents.Companion
The proxy side that consumes this codec is filed as billion-context PR #867 (issue #866); it needs a kernel release carrying the codec before its own CI can resolve
acp-kernel/wire.