132 lines
4.1 KiB
Markdown
132 lines
4.1 KiB
Markdown
|
|
# 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)
|
||
|
|
```js
|
||
|
|
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)
|
||
|
|
```js
|
||
|
|
think: false
|
||
|
|
```
|
||
|
|
Hardcoded in `convertAnthropicToOllama()`.
|
||
|
|
|
||
|
|
### 3. Tool-Schema-Konvertierung (korrekt)
|
||
|
|
Anthropic `input_schema` → Ollama `function.parameters`:
|
||
|
|
```js
|
||
|
|
{ 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:
|
||
|
|
```js
|
||
|
|
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)
|
||
|
|
```js
|
||
|
|
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)
|
||
|
|
|
||
|
|
```js
|
||
|
|
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)
|
||
|
|
|
||
|
|
```js
|
||
|
|
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)
|
||
|
|
```
|