4.1 KiB
Proxy Implementierungsanalyse
Was der Proxy macht
Übersetzt Anthropic API (POST /v1/messages) → Ollama API (POST /api/chat)
- Empfängt Anthropic SSE-Format
- Gibt Anthropic SSE-Format zurück
- Routing:
localhost:11435→https://ollama.aquantico.de/api/chat
Korrekte Implementierungen ✓
1. Model-Substitution (korrekt)
if (anthropicBody.model?.startsWith('claude-')) {
anthropicBody.model = 'qwen3.6:35b-a3b-q4_K_M';
}
Alle claude-* Modelle werden durch das lokale Modell ersetzt.
2. Think-Modus deaktiviert (korrekt)
think: false
Hardcoded in convertAnthropicToOllama().
3. Tool-Schema-Konvertierung (korrekt)
Anthropic input_schema → Ollama function.parameters:
{ type: 'function', function: { name, description, parameters: sanitizeToolSchema(tool.input_schema) } }
4. Tool-Call-Parsing in Response (korrekt)
Ollama gibt tc.function.name und tc.function.arguments zurück — genau das liest der Proxy:
const toolName = tc.function?.name;
const toolInput = parseToolArguments(tc.function?.arguments);
5. SSE-Event-Sequenz (korrekt)
Ausgabe entspricht Anthropic-Spec:
message_start → content_block_start → content_block_delta → content_block_stop → message_delta → message_stop
6. stop_reason (korrekt)
stop_reason: emittedToolUse ? 'tool_use' : 'end_turn'
7. Tool-Deduplizierung (korrekt)
Verhindert doppelte Tool-Calls via seenToolCalls Set mit Key name:args.
Bekannte Bugs / Schwachstellen ⚠️
BUG 1: Leerer finaler Buffer-Handler (app.js:350-358)
if (buffer.trim()) {
try {
const data = JSON.parse(buffer.trim());
// gleichen Handling-Code wie oben ausführen ← LEER, nie ausgeführt!
} catch (e) { ... }
}
Problem: Wenn das letzte NDJSON-Chunk von Ollama nicht mit \n endet (was bei einigen Ollama-Versionen vorkommt), bleibt die finale done: true-Zeile im Buffer und wird nicht verarbeitet.
Auswirkung: messageFinished bleibt false, Fallback-Code (Zeile 360-381) sendet die Abschluss-Events ohne eval_count (output_tokens=0).
Fix: Den gleichen Parsing-Code aus der while-Schleife in den finalen Buffer-Handler kopieren.
BUG 2: message_start ohne usage.input_tokens (app.js:200-209)
res.write(`event: message_start\ndata: ${JSON.stringify({
type: 'message_start',
message: {
id: messageId, type: 'message', role: 'assistant', content: [],
model: anthropicBody.model
// fehlt: stop_reason: null, usage: { input_tokens: 0, output_tokens: 0 }
}
})}\n\n`);
Auswirkung: Anthropic-kompatible Clients erwarten usage.input_tokens in message_start. Kann bei strikten Clients zu Parse-Fehlern führen.
BUG 3: tool_use/tool_result als Text im Nachrichten-Verlauf
Wenn Anthropic-Clients tool_use (in Assistant-Nachrichten) und tool_result (in User-Nachrichten) im History senden, werden diese als Text-Strings in den Ollama-Messages eingebettet:
"Previous assistant tool call already made.\nTool name: ...\n..."
Korrekt wäre: Assistant-Nachrichten mit tool_calls senden, Tool-Results als role: "tool" Nachricht.
Auswirkung: Das Modell versteht den Tool-Call-Verlauf semantisch nicht korrekt. Die bestehende Deduplizierungs-Logik kompensiert dies teilweise.
Architektur-Übersicht
Client (Claude SDK)
│ POST /v1/messages (Anthropic Format)
▼
noThinkProxy :11435
│ convertAnthropicToOllama()
│ - system → messages[0] role:system
│ - tool_use → text string
│ - tool_result → text string
│ - model: claude-* → qwen3.6:35b-a3b-q4_K_M
│ - think: false
│ - options: { num_ctx:131072, num_predict, temperature:0.7 }
│
│ POST /api/chat (Ollama NDJSON)
▼
Ollama https://ollama.aquantico.de
│ NDJSON stream: {message:{content, tool_calls}, done}
▼
noThinkProxy
│ handleResponse()
│ - text → content_block_delta (text_delta)
│ - tool_calls → content_block_start/delta/stop (tool_use)
│ - done → message_delta + message_stop
▼
Client (Anthropic SSE)