mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-13 18:52:18 +03:00
fix(mcp): persist and re-attach Gemini thoughtSignature on the direct Claude<->Gemini path (#9448)
The direct Claude<->Gemini translator (claude-to-gemini.ts / gemini-to-claude.ts) never persisted the thoughtSignature Gemini returns on functionCall parts, and never re-attached one on the next turn. Gemini 3+/2.5 strictly reject a native functionCall part with no signature (400), which surfaces whenever a combo falls back onto a Gemini model mid-conversation (the fallback tool_use never went through Gemini, so no signature exists for it). - gemini-to-claude.ts: store the signature (keyed by tool_use id + connection namespace) when Gemini's response carries one, mirroring the existing gemini-to-openai.ts hub-path behavior. - claude-to-gemini.ts: resolve a stored signature for historical tool_use blocks; when none exists and the target model requires one, downgrade the tool_use/tool_result pair to inert text instead of sending a signature-less native part, matching the "context" fallback already used by the OpenAI hub path (#3358) rather than the removed fake-signature injection. Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
9a4cca4bc2
commit
6143da70d1
@@ -142,6 +142,38 @@ test("Gemini -> Claude stream: STOP after prior tool use still maps to tool_use"
|
||||
assert.equal(result[1].type, "message_stop");
|
||||
});
|
||||
|
||||
test("Gemini -> Claude stream: stores thoughtSignature from a standalone part preceding functionCall", async () => {
|
||||
const { getGeminiThoughtSignature, clearGeminiThoughtSignatureMemoryForTests } = await import(
|
||||
"../../open-sse/services/geminiThoughtSignatureStore.ts"
|
||||
);
|
||||
clearGeminiThoughtSignatureMemoryForTests();
|
||||
|
||||
const state: { signatureNamespace: string; pendingThoughtSignature?: string | null } = {
|
||||
signatureNamespace: "conn-claude-1",
|
||||
};
|
||||
const result = geminiToClaudeResponse(
|
||||
{
|
||||
responseId: "resp-sig-1",
|
||||
modelVersion: "gemini-3.1-flash-lite",
|
||||
candidates: [
|
||||
{
|
||||
content: {
|
||||
parts: [
|
||||
{ thoughtSignature: "sig-claude-1" },
|
||||
{ functionCall: { id: "toolu_sig_1", name: "read_file", args: { path: "/a" } } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
state
|
||||
);
|
||||
|
||||
assert.equal(result[1].content_block.id, "toolu_sig_1");
|
||||
assert.equal(state.pendingThoughtSignature, null);
|
||||
assert.equal(getGeminiThoughtSignature("conn-claude-1:toolu_sig_1"), "sig-claude-1");
|
||||
});
|
||||
|
||||
test("Gemini -> Claude stream: response wrapper is supported and promptFeedback-only chunk is ignored", () => {
|
||||
const wrapped = geminiToClaudeResponse(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user