diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index 8cff6e72..b420fa23 100644 Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ diff --git a/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs b/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs index 6b478305..f5035c7b 100644 --- a/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs +++ b/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs @@ -374,6 +374,29 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori return Regex.IsMatch(response, @"<([a-zA-Z0-9_]+)(?:>.*?|/>)", RegexOptions.Singleline); } + private static bool IsNoActionOnly(string response) + { + if (string.IsNullOrWhiteSpace(response)) return false; + var matches = Regex.Matches(response, @"<([a-zA-Z0-9_]+)(?:>.*?|/>)", RegexOptions.Singleline); + return matches.Count == 1 && + matches[0].Groups[1].Value.Equals("no_action", StringComparison.OrdinalIgnoreCase); + } + + private static bool HasActionToolCall(string response) + { + if (string.IsNullOrWhiteSpace(response)) return false; + var matches = Regex.Matches(response, @"<([a-zA-Z0-9_]+)(?:>.*?|/>)", RegexOptions.Singleline); + foreach (Match match in matches) + { + var toolName = match.Groups[1].Value; + if (IsActionToolName(toolName)) + { + return true; + } + } + return false; + } + private static bool ShouldRetryTools(string response) { if (string.IsNullOrWhiteSpace(response)) return false; @@ -412,6 +435,9 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori string sanitized = message; sanitized = Regex.Replace(sanitized, @"Tool\s+'[^']+'\s+Result(?:\s+\(Invisible\))?:", "Query Result:"); sanitized = Regex.Replace(sanitized, @"Tool\s+'[^']+'\s+Result\s+\(Invisible\):", "Query Result:"); + sanitized = Regex.Replace(sanitized, @"(?m)^ToolRunner\s+(Guidance|Guard|Note):.*(\r?\n)?", ""); + sanitized = Regex.Replace(sanitized, @"(?m)^\s+$", ""); + sanitized = sanitized.Trim(); return sanitized; } @@ -587,21 +613,32 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori return; } - if (!IsXmlToolCall(actionResponse)) + bool actionHasXml = IsXmlToolCall(actionResponse); + bool actionIsNoActionOnly = IsNoActionOnly(actionResponse); + bool actionHasActionTool = actionHasXml && HasActionToolCall(actionResponse); + if (!actionHasXml || (!actionHasActionTool && !actionIsNoActionOnly)) { if (Prefs.DevMode) { - WulaLog.Debug("[WulaAI] Turn 2/3 missing XML; attempting XML-only conversion."); + WulaLog.Debug("[WulaAI] Turn 2/3 missing XML or no action tool; attempting XML-only conversion."); } - string fixInstruction = actionInstruction + - "\n\n# FORMAT FIX\n" + - "Your last output was not valid XML.\n" + - "Convert the intended action into VALID XML tool calls or .\n" + - "Output XML only. No commentary.\n" + - "You MUST use only action tools.\n" + + string fixInstruction = "# FORMAT FIX (ACTION XML ONLY)\n" + + "Preserve the intent of the previous output.\n" + + "If the previous output indicates no action is needed or refuses action, output exactly: .\n" + + "Do NOT invent new actions.\n" + + "Output VALID XML tool calls only. No natural language, no commentary.\n" + + "Allowed tags: , , , , .\n" + + "\nAction tool XML formats:\n" + + "- DefNameInt\n" + + "- PawnKindDef: Count, ...\n" + + "- DefNameIntInt\n" + + "- Int\n" + "\nPrevious output:\n" + TrimForPrompt(actionResponse, 600); string fixedResponse = await client.GetChatCompletionAsync(fixInstruction, actionContext, maxTokens: 128, temperature: 0.1f); - if (!string.IsNullOrEmpty(fixedResponse) && IsXmlToolCall(fixedResponse)) + bool fixedHasXml = !string.IsNullOrEmpty(fixedResponse) && IsXmlToolCall(fixedResponse); + bool fixedIsNoActionOnly = fixedHasXml && IsNoActionOnly(fixedResponse); + bool fixedHasActionTool = fixedHasXml && HasActionToolCall(fixedResponse); + if (fixedHasXml && (fixedHasActionTool || fixedIsNoActionOnly)) { actionResponse = fixedResponse; } @@ -651,21 +688,29 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori if (!IsXmlToolCall(retryActionResponse)) { if (Prefs.DevMode) - { - WulaLog.Debug("[WulaAI] Retry action phase missing XML; attempting XML-only conversion."); - } - string retryFixInstruction = retryActionInstruction + - "\n\n# FORMAT FIX\n" + - "Your last output was not valid XML.\n" + - "Convert the intended action into VALID XML tool calls or .\n" + - "Output XML only. No commentary.\n" + - "You MUST use only action tools.\n" + + { + WulaLog.Debug("[WulaAI] Retry action phase missing XML; attempting XML-only conversion."); + } + string retryFixInstruction = "# FORMAT FIX (ACTION XML ONLY)\n" + + "Preserve the intent of the previous output.\n" + + "If the previous output indicates no action is needed or refuses action, output exactly: .\n" + + "Do NOT invent new actions.\n" + + "Output VALID XML tool calls only. No natural language, no commentary.\n" + + "Allowed tags: , , , , .\n" + + "\nAction tool XML formats:\n" + + "- DefNameInt\n" + + "- PawnKindDef: Count, ...\n" + + "- DefNameIntInt\n" + + "- Int\n" + "\nPrevious output:\n" + TrimForPrompt(retryActionResponse, 600); - string retryFixedResponse = await client.GetChatCompletionAsync(retryFixInstruction, retryActionContext, maxTokens: 128, temperature: 0.1f); - if (!string.IsNullOrEmpty(retryFixedResponse) && IsXmlToolCall(retryFixedResponse)) - { - retryActionResponse = retryFixedResponse; - } + string retryFixedResponse = await client.GetChatCompletionAsync(retryFixInstruction, retryActionContext, maxTokens: 128, temperature: 0.1f); + bool retryFixedHasXml = !string.IsNullOrEmpty(retryFixedResponse) && IsXmlToolCall(retryFixedResponse); + bool retryFixedIsNoActionOnly = retryFixedHasXml && IsNoActionOnly(retryFixedResponse); + bool retryFixedHasActionTool = retryFixedHasXml && HasActionToolCall(retryFixedResponse); + if (retryFixedHasXml && (retryFixedHasActionTool || retryFixedIsNoActionOnly)) + { + retryActionResponse = retryFixedResponse; + } else { if (Prefs.DevMode)