diff --git a/Services/OpenAiService.cs b/Services/OpenAiService.cs
index 97876ce..1aadba1 100644
--- a/Services/OpenAiService.cs
+++ b/Services/OpenAiService.cs
@@ -304,6 +304,8 @@ public class OpenAiService
var sw = System.Diagnostics.Stopwatch.StartNew();
var usage = new TokenUsage();
var iteration = 0;
+ var autoContinueCount = 0;
+ const int MaxAutoContinues = 3;
var chain = new ChainRecord
{
@@ -382,10 +384,15 @@ public class OpenAiService
if (provider == "nvidia" && IsNvidiaThinkingModel(modelId))
payload["chat_template_kwargs"] = new { thinking = true };
- // MiniMax-Thinking-Modelle: reasoning_split → Thinking landet in delta.reasoning_details
- // statt als ... inline in delta.content (siehe MiniMax-OpenAI-Compat-Docs)
+ // MiniMax-spezifisch: reasoning_split → Thinking landet in delta.reasoning_details
+ // statt als ... inline in delta.content (siehe MiniMax-OpenAI-Compat-Docs).
+ // Sampling-Parameter: temperature=1.0, top_p=0.95 (MiniMax-Empfehlung für Agentic Performance).
if (provider == "minimax")
+ {
payload["extra_body"] = new { reasoning_split = true };
+ payload["temperature"] = 1.0;
+ payload["top_p"] = 0.95;
+ }
var jsonPayload = JsonSerializer.Serialize(payload);
var httpContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
@@ -827,9 +834,43 @@ public class OpenAiService
});
}
- // Keine Tool-Calls → fertig (stopReason als Fallback, primär Count prüfen)
+ // Keine Tool-Calls → prüfen ob Auto-Continue sinnvoll ist
if (toolCallAccumulators.Count == 0)
{
+ // Auto-Continue: Wenn das Modell mit "stop" beendet hat, kein Tool-Call kam,
+ // aber der Text nach einer Action-Ankündigung aussieht, senden wir automatisch
+ // eine Continuation-Message. Bekanntes MiniMax-M2.7-Verhalten bei langen Kontexten.
+ // Auch bei anderen Modellen kann das vorkommen (DeepSeek-R1, QwQ).
+ // Max 3 Auto-Continues pro Chain um Endlos-Loops zu verhindern.
+ if (autoContinueCount < MaxAutoContinues
+ && !gotLengthFinish
+ && lastFinishReason == "stop"
+ && LooksLikeIncompleteAction(finalText))
+ {
+ autoContinueCount++;
+
+ // Thinking + Text als Assistant-Message in History einfügen
+ var continueContent = string.IsNullOrEmpty(finalText) ? "" : finalText;
+ if (!string.IsNullOrEmpty(finalThinking))
+ continueContent = $"\n{finalThinking}\n\n\n{continueContent}";
+
+ if (!string.IsNullOrEmpty(continueContent))
+ apiMessages.Add(new { role = "assistant", content = continueContent });
+
+ // Synthetische User-Message injizieren
+ apiMessages.Add(new { role = "user", content = "Continue." });
+
+ // SSE-Event an Frontend senden
+ await SseHelper.TryWriteSse(response, "auto_continue", new
+ {
+ reason = "Model stopped without tool call after action announcement",
+ attempt = autoContinueCount,
+ maxAttempts = MaxAutoContinues
+ }, sseCt);
+
+ continue; // Nächste Iteration der while-Loop
+ }
+
await FinalizeChain(chain, usage, iteration, stopReason, sw);
await SseHelper.TryWriteSse(response, "done", new
{
@@ -1409,6 +1450,37 @@ public class OpenAiService
return (provider, modelId, providerConfig);
}
+ ///
+ /// Prüft ob der Text eines Assistant-Turns wie eine unvollständige Action-Ankündigung aussieht.
+ /// Typisches MiniMax-M2.7-Verhalten: Modell schreibt "Lass mich X machen:" und stoppt ohne Tool-Call.
+ ///
+ private static bool LooksLikeIncompleteAction(string text)
+ {
+ if (string.IsNullOrWhiteSpace(text)) return false;
+
+ var trimmed = text.TrimEnd();
+
+ // Heuristik 1: Text endet mit Doppelpunkt → Action-Ankündigung
+ if (trimmed.EndsWith(":")) return true;
+
+ // Heuristik 2: Text endet mit typischen Action-Verben + unvollständiger Phrase
+ // z.B. "Lass mich die Datei anschauen" ohne nachfolgenden Tool-Call
+ var lower = trimmed.ToLowerInvariant();
+ var actionPhrases = new[]
+ {
+ "lass mich", "let me", "i'll ", "i will ", "ich werde ",
+ "schauen wir", "let's ", "weiter mit", "jetzt ", "als nächstes",
+ "now i'll", "now let me", "proceeding to", "going to"
+ };
+
+ foreach (var phrase in actionPhrases)
+ {
+ if (lower.Contains(phrase)) return true;
+ }
+
+ return false;
+ }
+
///
/// Prüft ob ein Nvidia-Modell Thinking-Mode unterstützt (chat_template_kwargs).
///
diff --git a/wwwroot/js/chat.js b/wwwroot/js/chat.js
index 2a6de08..05354d6 100644
--- a/wwwroot/js/chat.js
+++ b/wwwroot/js/chat.js
@@ -131,6 +131,10 @@ function handleStreamEvent(eventType, data) {
addAutoContextBubble(data.context);
break;
+ case 'auto_continue':
+ addWarningBubble(`Auto-Continue (${data.attempt}/${data.maxAttempts}): Modell wird fortgesetzt…`, true);
+ break;
+
case 'warning':
case 'info':
addWarningBubble(data.message, true);