Implement Gemini signature caching and thinking recovery

- Add in-memory signature cache to restore thoughtSignatures stripped by Claude Code
- Implement thinking recovery logic to handle interrupted tool loops for Gemini
- Enhance schema sanitizer to preserve constraints and enums as description hints
- Update CLAUDE.md with new architecture details

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Badri Narayanan S
2025-12-28 14:34:03 +05:30
parent 1eb2329f7c
commit 426acc494a
9 changed files with 473 additions and 20 deletions

View File

@@ -14,7 +14,9 @@ import {
restoreThinkingSignatures,
removeTrailingThinkingBlocks,
reorderAssistantContent,
filterUnsignedThinkingBlocks
filterUnsignedThinkingBlocks,
needsThinkingRecovery,
closeToolLoopForThinking
} from './thinking-utils.js';
/**
@@ -74,9 +76,18 @@ export function convertAnthropicToGoogle(anthropicRequest) {
}
}
// Apply thinking recovery for Gemini thinking models when needed
// This handles corrupted tool loops where thinking blocks are stripped
// Claude models handle this differently and don't need this recovery
let processedMessages = messages;
if (isGeminiModel && isThinking && needsThinkingRecovery(messages)) {
console.log('[RequestConverter] Applying thinking recovery for Gemini');
processedMessages = closeToolLoopForThinking(messages);
}
// Convert messages to contents, then filter unsigned thinking blocks
for (let i = 0; i < messages.length; i++) {
const msg = messages[i];
for (let i = 0; i < processedMessages.length; i++) {
const msg = processedMessages[i];
let msgContent = msg.content;
// For assistant messages, process thinking blocks and reorder content
@@ -90,6 +101,14 @@ export function convertAnthropicToGoogle(anthropicRequest) {
}
const parts = convertContentToParts(msgContent, isClaudeModel, isGeminiModel);
// SAFETY: Google API requires at least one part per content message
// This happens when all thinking blocks are filtered out (unsigned)
if (parts.length === 0) {
console.log('[RequestConverter] WARNING: Empty parts array after filtering, adding placeholder');
parts.push({ text: '' });
}
const content = {
role: convertRole(msg.role),
parts: parts