fix(openai): ask_user_choice-Intercept, Interleaved Thinking in History, MiniMax-Debug-Log

OpenAI-kompatible Provider (MiniMax, DeepSeek-R1, QwQ, Kimi K2, etc.) verloren bei
jeder neuen Iteration ihre eigene Chain-of-Thought, weil thinking-Messages aus der
DB-History rausgefiltert wurden. Folge: Modell kündigt Action an, ruft Tool aber
nicht auf → Stream endet mit finish_reason:stop ohne Tool-Call. Zusätzlich fehlte
der ask_user_choice-Intercept (Tool wurde als unbekannt abgelehnt) und es gab keine
Diagnose-Möglichkeit für unerwartete Stop-Verhalten.

- ChatService.BuildHistoryFromDb(includeThinking): neuer Parameter, fügt
  thinking-Messages als {type:"thinking"}-Block in die Assistant-Message ein.
  Text/Tool-Use derselben Iteration werden via AppendToLastMessage angehängt
  → ein Assistant-Block enthält [thinking, text, tool_use…].
- OpenAiService.ConvertMessage + StreamChat + ChatNonStreaming: Thinking als
  <think>...</think>-Prefix dem Assistant-Content voranstellen (Per-Iteration und
  beim DB-History-Aufbau). Konvention von MiniMax-Docs, wird auch von DeepSeek-R1,
  QwQ, Kimi K2 verstanden.
- ChatEndpoint, SessionEndpoint, CronjobService, TelegramService, ToolService:
  includeThinking je nach Provider durchgereicht. Anthropic-Pfad bleibt aus
  (lehnt thinking-Blöcke ohne Signature ab).
- OpenAiService: ask_user_choice-Intercept im Streaming-Pfad (analog
  AnthropicService.Streaming) – Tool nicht ausführen, SSE user_choice rausschicken,
  mit stopReason awaiting_user_choice finalisieren und return.
- OpenAiService: MiniMax-SSE-Debug-Log in data/openai-debug/ (JSONL: Header +
  Roh-data-Zeilen + Summary mit promptTokens/completionTokens/finishReason/
  toolCalls/duration) – nur für provider=="minimax", nicht-blockierend.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
MelekiandClaude Opus 4.7 committed 2026-05-14 10:19:06 +02:00
1 parent d66f8d2e73
commit ecddbe5f36
7 files changed
+226 -20

No files matched your search

+7 -2
View File
@@ -185,8 +185,13 @@ public static class ChatEndpoint
} // !skipPreparer
}
// History für den Session-Agent: OHNE Sub-Agent-Messages (saubere History)
var messages = await chatService.BuildHistoryFromDb(sessionId, promptBlocks: ctx.PromptBlocks);
// History für den Session-Agent: OHNE Sub-Agent-Messages (saubere History).
// includeThinking=true für OpenAI-kompatible Provider → Thinking wird als
// <think>...</think>-Prefix dem Assistant-Text vorangestellt (laut MiniMax-Docs essentiell
// für Tool-Calling-Reliability / Interleaved Thinking). Anthropic-Pfad braucht das nicht,
// weil dort thinking-Blöcke nur mit Signatur akzeptiert werden (die wir nicht persistieren).
var includeThinkingForProvider = !string.IsNullOrEmpty(ctx.Provider);
var messages = await chatService.BuildHistoryFromDb(sessionId, promptBlocks: ctx.PromptBlocks, includeThinking: includeThinkingForProvider);
// Bei Choice-Response: toolChoice auf "auto" setzen, damit Claude frei antworten kann
var effectiveToolChoice = isChoiceResponse ? "auto" : ctx.ToolChoice;
+3 -2
View File
@@ -287,8 +287,9 @@ public static class SessionEndpoint
// Model
var modelId = ChatService.ResolveModelId(agent.Model);
// Messages aus der DB (exakt wie beim Chat)
var messages = await chatService.BuildHistoryFromDb(session.SessionId, promptBlocks: promptBlocks);
// Messages aus der DB (exakt wie beim Chat) – includeThinking je nach Provider
var previewProvider = OpenAiService.IsOpenAiProvider(modelId);
var messages = await chatService.BuildHistoryFromDb(session.SessionId, promptBlocks: promptBlocks, includeThinking: previewProvider);
// Tools (exakt wie AddToolsToPayload es machen würde)
var hasWorkspace = !string.IsNullOrEmpty(workspace);