zc
This commit is contained in:
Binary file not shown.
@@ -723,59 +723,199 @@
|
|||||||
</PrefabDef>
|
</PrefabDef>
|
||||||
|
|
||||||
|
|
||||||
<PrefabDef>
|
<PrefabDef>
|
||||||
<defName>WULA_RavenShuttleBase</defName> <!-- rename -->
|
<defName>WULA_PrisonBase</defName> <!-- rename -->
|
||||||
<size>(13,13)</size>
|
<size>(13,13)</size>
|
||||||
<things>
|
<things>
|
||||||
<WulaDoor>
|
<WulaDoor>
|
||||||
<rects>
|
<rects>
|
||||||
<li>(1,6,1,6)</li>
|
<li>(1,4,1,4)</li>
|
||||||
<li>(11,6,11,6)</li>
|
<li>(4,4,4,4)</li>
|
||||||
</rects>
|
<li>(8,4,8,4)</li>
|
||||||
<relativeRotation>Clockwise</relativeRotation>
|
<li>(11,4,11,4)</li>
|
||||||
</WulaDoor>
|
<li>(1,8,1,8)</li>
|
||||||
<WallLamp>
|
<li>(4,8,4,8)</li>
|
||||||
<rects>
|
<li>(8,8,8,8)</li>
|
||||||
<li>(1,2,1,2)</li>
|
<li>(11,8,11,8)</li>
|
||||||
<li>(1,10,1,10)</li>
|
</rects>
|
||||||
</rects>
|
</WulaDoor>
|
||||||
<relativeRotation>Counterclockwise</relativeRotation>
|
<WulaDoor>
|
||||||
</WallLamp>
|
<rects>
|
||||||
<WallLamp>
|
<li>(0,6,0,6)</li>
|
||||||
<rects>
|
<li>(12,6,12,6)</li>
|
||||||
<li>(11,2,11,2)</li>
|
</rects>
|
||||||
<li>(11,10,11,10)</li>
|
<relativeRotation>Clockwise</relativeRotation>
|
||||||
</rects>
|
</WulaDoor>
|
||||||
<relativeRotation>Clockwise</relativeRotation>
|
<HiddenConduit>
|
||||||
</WallLamp>
|
<rects>
|
||||||
<WulaWall>
|
<li>(4,5,4,5)</li>
|
||||||
<rects>
|
<li>(8,5,8,5)</li>
|
||||||
<li>(0,0,1,0)</li>
|
<li>(4,7,4,7)</li>
|
||||||
<li>(11,0,12,0)</li>
|
<li>(8,7,8,7)</li>
|
||||||
<li>(0,1,0,5)</li>
|
</rects>
|
||||||
<li>(12,1,12,5)</li>
|
</HiddenConduit>
|
||||||
<li>(1,5,1,5)</li>
|
<Bed>
|
||||||
<li>(11,5,11,5)</li>
|
<positions>
|
||||||
<li>(0,7,1,7)</li>
|
<li>(2, 0, 1)</li>
|
||||||
<li>(11,7,12,7)</li>
|
<li>(5, 0, 1)</li>
|
||||||
<li>(0,8,0,12)</li>
|
<li>(7, 0, 1)</li>
|
||||||
<li>(12,8,12,12)</li>
|
<li>(10, 0, 1)</li>
|
||||||
<li>(1,12,1,12)</li>
|
</positions>
|
||||||
<li>(11,12,11,12)</li>
|
<stuff>WULA_Alloy</stuff>
|
||||||
</rects>
|
<quality>Normal</quality>
|
||||||
</WulaWall>
|
</Bed>
|
||||||
<WULA_ArmedShuttleWithPocket>
|
<Bed>
|
||||||
<position>(6, 0, 6)</position>
|
<positions>
|
||||||
</WULA_ArmedShuttleWithPocket>
|
<li>(2, 0, 11)</li>
|
||||||
</things>
|
<li>(5, 0, 11)</li>
|
||||||
<terrain>
|
<li>(7, 0, 11)</li>
|
||||||
|
<li>(10, 0, 11)</li>
|
||||||
|
</positions>
|
||||||
|
<relativeRotation>Opposite</relativeRotation>
|
||||||
|
<stuff>WULA_Alloy</stuff>
|
||||||
|
<quality>Normal</quality>
|
||||||
|
</Bed>
|
||||||
|
<WallLamp>
|
||||||
|
<rects>
|
||||||
|
<li>(1,1,1,1)</li>
|
||||||
|
<li>(4,1,4,1)</li>
|
||||||
|
<li>(8,1,8,1)</li>
|
||||||
|
<li>(11,1,11,1)</li>
|
||||||
|
</rects>
|
||||||
|
<relativeRotation>Opposite</relativeRotation>
|
||||||
|
</WallLamp>
|
||||||
|
<WallLamp>
|
||||||
|
<position>(3, 0, 6)</position>
|
||||||
|
<relativeRotation>Clockwise</relativeRotation>
|
||||||
|
</WallLamp>
|
||||||
|
<WallLamp>
|
||||||
|
<position>(9, 0, 6)</position>
|
||||||
|
<relativeRotation>Counterclockwise</relativeRotation>
|
||||||
|
</WallLamp>
|
||||||
|
<WallLamp>
|
||||||
|
<rects>
|
||||||
|
<li>(1,11,1,11)</li>
|
||||||
|
<li>(4,11,4,11)</li>
|
||||||
|
<li>(8,11,8,11)</li>
|
||||||
|
<li>(11,11,11,11)</li>
|
||||||
|
</rects>
|
||||||
|
</WallLamp>
|
||||||
|
<EndTable>
|
||||||
|
<rects>
|
||||||
|
<li>(1,1,1,1)</li>
|
||||||
|
<li>(4,1,4,1)</li>
|
||||||
|
<li>(8,1,8,1)</li>
|
||||||
|
<li>(11,1,11,1)</li>
|
||||||
|
</rects>
|
||||||
|
<stuff>WULA_Alloy</stuff>
|
||||||
|
<quality>Normal</quality>
|
||||||
|
</EndTable>
|
||||||
|
<EndTable>
|
||||||
|
<rects>
|
||||||
|
<li>(1,11,1,11)</li>
|
||||||
|
<li>(4,11,4,11)</li>
|
||||||
|
<li>(8,11,8,11)</li>
|
||||||
|
<li>(11,11,11,11)</li>
|
||||||
|
</rects>
|
||||||
|
<relativeRotation>Opposite</relativeRotation>
|
||||||
|
<stuff>WULA_Alloy</stuff>
|
||||||
|
<quality>Normal</quality>
|
||||||
|
</EndTable>
|
||||||
|
<WulaWall>
|
||||||
|
<rects>
|
||||||
|
<li>(0,0,12,0)</li>
|
||||||
|
<li>(0,1,0,5)</li>
|
||||||
|
<li>(3,1,3,4)</li>
|
||||||
|
<li>(6,1,6,4)</li>
|
||||||
|
<li>(9,1,9,4)</li>
|
||||||
|
<li>(12,1,12,5)</li>
|
||||||
|
<li>(2,4,2,4)</li>
|
||||||
|
<li>(5,4,5,4)</li>
|
||||||
|
<li>(7,4,7,4)</li>
|
||||||
|
<li>(10,4,10,4)</li>
|
||||||
|
<li>(4,6,4,6)</li>
|
||||||
|
<li>(8,6,8,6)</li>
|
||||||
|
<li>(0,7,0,12)</li>
|
||||||
|
<li>(12,7,12,12)</li>
|
||||||
|
<li>(2,8,3,8)</li>
|
||||||
|
<li>(5,8,7,8)</li>
|
||||||
|
<li>(9,8,10,8)</li>
|
||||||
|
<li>(3,9,3,12)</li>
|
||||||
|
<li>(6,9,6,12)</li>
|
||||||
|
<li>(9,9,9,12)</li>
|
||||||
|
<li>(1,12,2,12)</li>
|
||||||
|
<li>(4,12,5,12)</li>
|
||||||
|
<li>(7,12,8,12)</li>
|
||||||
|
<li>(10,12,11,12)</li>
|
||||||
|
</rects>
|
||||||
|
</WulaWall>
|
||||||
|
<WULA_AreaTeleportBeacon>
|
||||||
|
<position>(6, 0, 6)</position>
|
||||||
|
</WULA_AreaTeleportBeacon>
|
||||||
|
</things>
|
||||||
|
<terrain>
|
||||||
<WulaFloor>
|
<WulaFloor>
|
||||||
<rects>
|
<rects>
|
||||||
<li>(0,1,12,12)</li>
|
<li>(0,1,12,12)</li>
|
||||||
</rects>
|
</rects>
|
||||||
</WulaFloor>
|
</WulaFloor>
|
||||||
</terrain>
|
</terrain>
|
||||||
</PrefabDef>
|
</PrefabDef>
|
||||||
|
|
||||||
|
|
||||||
|
<PrefabDef>
|
||||||
|
<defName>WULA_RavenShuttleBase</defName> <!-- rename -->
|
||||||
|
<size>(13,13)</size>
|
||||||
|
<things>
|
||||||
|
<WulaDoor>
|
||||||
|
<rects>
|
||||||
|
<li>(1,6,1,6)</li>
|
||||||
|
<li>(11,6,11,6)</li>
|
||||||
|
</rects>
|
||||||
|
<relativeRotation>Clockwise</relativeRotation>
|
||||||
|
</WulaDoor>
|
||||||
|
<WallLamp>
|
||||||
|
<rects>
|
||||||
|
<li>(1,2,1,2)</li>
|
||||||
|
<li>(1,10,1,10)</li>
|
||||||
|
</rects>
|
||||||
|
<relativeRotation>Counterclockwise</relativeRotation>
|
||||||
|
</WallLamp>
|
||||||
|
<WallLamp>
|
||||||
|
<rects>
|
||||||
|
<li>(11,2,11,2)</li>
|
||||||
|
<li>(11,10,11,10)</li>
|
||||||
|
</rects>
|
||||||
|
<relativeRotation>Clockwise</relativeRotation>
|
||||||
|
</WallLamp>
|
||||||
|
<WulaWall>
|
||||||
|
<rects>
|
||||||
|
<li>(0,0,1,0)</li>
|
||||||
|
<li>(11,0,12,0)</li>
|
||||||
|
<li>(0,1,0,5)</li>
|
||||||
|
<li>(12,1,12,5)</li>
|
||||||
|
<li>(1,5,1,5)</li>
|
||||||
|
<li>(11,5,11,5)</li>
|
||||||
|
<li>(0,7,1,7)</li>
|
||||||
|
<li>(11,7,12,7)</li>
|
||||||
|
<li>(0,8,0,12)</li>
|
||||||
|
<li>(12,8,12,12)</li>
|
||||||
|
<li>(1,12,1,12)</li>
|
||||||
|
<li>(11,12,11,12)</li>
|
||||||
|
</rects>
|
||||||
|
</WulaWall>
|
||||||
|
<WULA_ArmedShuttleWithPocket>
|
||||||
|
<position>(6, 0, 6)</position>
|
||||||
|
</WULA_ArmedShuttleWithPocket>
|
||||||
|
</things>
|
||||||
|
<terrain>
|
||||||
|
<WulaFloor>
|
||||||
|
<rects>
|
||||||
|
<li>(0,1,12,12)</li>
|
||||||
|
</rects>
|
||||||
|
</WulaFloor>
|
||||||
|
</terrain>
|
||||||
|
</PrefabDef>
|
||||||
|
|
||||||
|
|
||||||
<PrefabDef>
|
<PrefabDef>
|
||||||
|
|||||||
@@ -941,6 +941,14 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
return cleaned.Trim();
|
return cleaned.Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool LooksLikeNaturalReply(string response)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(response)) return false;
|
||||||
|
string trimmed = response.Trim();
|
||||||
|
if (JsonToolCallParser.LooksLikeJson(trimmed)) return false;
|
||||||
|
return trimmed.Length >= 4;
|
||||||
|
}
|
||||||
|
|
||||||
private bool IsToolAvailable(string toolName)
|
private bool IsToolAvailable(string toolName)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(toolName)) return false;
|
if (string.IsNullOrWhiteSpace(toolName)) return false;
|
||||||
@@ -1758,6 +1766,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
maxSeconds = Mathf.Clamp(settings.reactMaxSeconds, 2f, 60f);
|
maxSeconds = Mathf.Clamp(settings.reactMaxSeconds, 2f, 60f);
|
||||||
}
|
}
|
||||||
_thinkingPhaseTotal = maxSteps;
|
_thinkingPhaseTotal = maxSteps;
|
||||||
|
string toolPhaseReplyCandidate = null;
|
||||||
for (int step = 1; step <= maxSteps; step++)
|
for (int step = 1; step <= maxSteps; step++)
|
||||||
{
|
{
|
||||||
if (Time.realtimeSinceStartup - startTime > maxSeconds)
|
if (Time.realtimeSinceStartup - startTime > maxSeconds)
|
||||||
@@ -1783,6 +1792,12 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
string normalizedResponse = NormalizeReactResponse(response);
|
string normalizedResponse = NormalizeReactResponse(response);
|
||||||
if (!JsonToolCallParser.TryParseToolCallsFromText(normalizedResponse, out var toolCalls, out string jsonFragment))
|
if (!JsonToolCallParser.TryParseToolCallsFromText(normalizedResponse, out var toolCalls, out string jsonFragment))
|
||||||
{
|
{
|
||||||
|
if (LooksLikeNaturalReply(normalizedResponse))
|
||||||
|
{
|
||||||
|
toolPhaseReplyCandidate = normalizedResponse;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (Prefs.DevMode)
|
if (Prefs.DevMode)
|
||||||
{
|
{
|
||||||
WulaLog.Debug("[WulaAI] ReAct step missing JSON envelope; attempting format fix.");
|
WulaLog.Debug("[WulaAI] ReAct step missing JSON envelope; attempting format fix.");
|
||||||
@@ -1793,6 +1808,10 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
if (string.IsNullOrEmpty(normalizedFixed) ||
|
if (string.IsNullOrEmpty(normalizedFixed) ||
|
||||||
!JsonToolCallParser.TryParseToolCallsFromText(normalizedFixed, out toolCalls, out jsonFragment))
|
!JsonToolCallParser.TryParseToolCallsFromText(normalizedFixed, out toolCalls, out jsonFragment))
|
||||||
{
|
{
|
||||||
|
if (LooksLikeNaturalReply(normalizedFixed))
|
||||||
|
{
|
||||||
|
toolPhaseReplyCandidate = normalizedFixed;
|
||||||
|
}
|
||||||
if (Prefs.DevMode)
|
if (Prefs.DevMode)
|
||||||
{
|
{
|
||||||
WulaLog.Debug("[WulaAI] ReAct format fix failed.");
|
WulaLog.Debug("[WulaAI] ReAct format fix failed.");
|
||||||
@@ -1893,10 +1912,21 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string fallbackReply = string.IsNullOrWhiteSpace(toolPhaseReplyCandidate)
|
||||||
|
? ""
|
||||||
|
: StripToolCallJson(toolPhaseReplyCandidate)?.Trim() ?? "";
|
||||||
bool replyHadToolCalls = IsToolCallJson(reply);
|
bool replyHadToolCalls = IsToolCallJson(reply);
|
||||||
string strippedReply = StripToolCallJson(reply)?.Trim() ?? "";
|
string strippedReply = StripToolCallJson(reply)?.Trim() ?? "";
|
||||||
if (replyHadToolCalls || string.IsNullOrWhiteSpace(strippedReply))
|
if (replyHadToolCalls || string.IsNullOrWhiteSpace(strippedReply))
|
||||||
{
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(fallbackReply))
|
||||||
|
{
|
||||||
|
reply = fallbackReply;
|
||||||
|
replyHadToolCalls = false;
|
||||||
|
strippedReply = fallbackReply;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
string retryReplyInstruction = replyInstruction +
|
string retryReplyInstruction = replyInstruction +
|
||||||
"\n\n# RETRY (REPLY OUTPUT)\n" +
|
"\n\n# RETRY (REPLY OUTPUT)\n" +
|
||||||
"Your last reply included tool call JSON or was empty. Tool calls are DISABLED.\n" +
|
"Your last reply included tool call JSON or was empty. Tool calls are DISABLED.\n" +
|
||||||
@@ -1908,16 +1938,24 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
replyHadToolCalls = IsToolCallJson(reply);
|
replyHadToolCalls = IsToolCallJson(reply);
|
||||||
strippedReply = StripToolCallJson(reply)?.Trim() ?? "";
|
strippedReply = StripToolCallJson(reply)?.Trim() ?? "";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (replyHadToolCalls)
|
if (replyHadToolCalls)
|
||||||
{
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(fallbackReply))
|
||||||
|
{
|
||||||
|
reply = fallbackReply;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
string cleaned = StripToolCallJson(reply)?.Trim() ?? "";
|
string cleaned = StripToolCallJson(reply)?.Trim() ?? "";
|
||||||
if (string.IsNullOrWhiteSpace(cleaned))
|
if (string.IsNullOrWhiteSpace(cleaned))
|
||||||
{
|
{
|
||||||
cleaned = "(system) AI reply returned tool call JSON only and was discarded. Please retry or send /clear to reset context.";
|
cleaned = "(system) AI reply returned tool call JSON only and was discarded. Please retry or send /clear to reset context.";
|
||||||
}
|
}
|
||||||
reply = cleaned;
|
reply = cleaned;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddAssistantMessage(reply);
|
AddAssistantMessage(reply);
|
||||||
|
|||||||
@@ -30,13 +30,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
private Dictionary<int, Texture2D> _portraits = new Dictionary<int, Texture2D>();
|
private Dictionary<int, Texture2D> _portraits = new Dictionary<int, Texture2D>();
|
||||||
private int _currentPortraitId = 0;
|
private int _currentPortraitId = 0;
|
||||||
private static readonly Regex ExpressionTagRegex = new Regex(@"\[EXPR\s*:\s*([1-6])\s*\]", RegexOptions.IgnoreCase);
|
private static readonly Regex ExpressionTagRegex = new Regex(@"\[EXPR\s*:\s*([1-6])\s*\]", RegexOptions.IgnoreCase);
|
||||||
private bool _reactTraceExpanded = false;
|
private readonly Dictionary<int, bool> _traceExpandedByAssistantIndex = new Dictionary<int, bool>();
|
||||||
private bool _hasReactTrace = false;
|
|
||||||
private float _reactTraceHeight = 0f;
|
|
||||||
private float _reactTraceYOffset = 0f;
|
|
||||||
private float _reactTraceHeaderHeight = 0f;
|
|
||||||
private string _reactTraceHeader = "";
|
|
||||||
private List<string> _reactTraceLines = new List<string>();
|
|
||||||
|
|
||||||
private class CachedMessage
|
private class CachedMessage
|
||||||
{
|
{
|
||||||
@@ -46,13 +40,12 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
public float height;
|
public float height;
|
||||||
public float yOffset;
|
public float yOffset;
|
||||||
public GameFont font;
|
public GameFont font;
|
||||||
}
|
public bool isTrace;
|
||||||
|
public int traceKey;
|
||||||
private class ReactTraceStep
|
public string traceHeader;
|
||||||
{
|
public List<string> traceLines;
|
||||||
public int Step;
|
public bool traceExpanded;
|
||||||
public List<string> Calls = new List<string>();
|
public float traceHeaderHeight;
|
||||||
public List<string> Results = new List<string>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dialog_AIConversation Instance { get; private set; }
|
public static Dialog_AIConversation Instance { get; private set; }
|
||||||
@@ -338,15 +331,91 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
float curY = 0f;
|
float curY = 0f;
|
||||||
float innerPadding = 5f;
|
float innerPadding = 5f;
|
||||||
float contentWidth = width - innerPadding * 2;
|
float contentWidth = width - innerPadding * 2;
|
||||||
|
var toolcallBuffer = new List<string>();
|
||||||
|
var toolResultBuffer = new List<string>();
|
||||||
|
bool traceEnabled = WulaFallenEmpireMod.settings?.showReactTraceInUI == true;
|
||||||
|
|
||||||
for (int i = 0; i < history.Count; i++)
|
for (int i = 0; i < history.Count; i++)
|
||||||
{
|
{
|
||||||
var entry = history[i];
|
var entry = history[i];
|
||||||
string messageText = entry.role == "assistant" ? ParseResponseForDisplay(entry.message) : AIIntelligenceCore.StripContextInfo(entry.message);
|
if (entry.role == "user")
|
||||||
|
{
|
||||||
if (entry.role == "tool" || entry.role == "system" || entry.role == "toolcall") continue;
|
toolcallBuffer.Clear();
|
||||||
|
toolResultBuffer.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.role == "toolcall")
|
||||||
|
{
|
||||||
|
if (traceEnabled)
|
||||||
|
{
|
||||||
|
toolcallBuffer.Add(entry.message ?? "");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.role == "tool")
|
||||||
|
{
|
||||||
|
if (traceEnabled)
|
||||||
|
{
|
||||||
|
toolResultBuffer.Add(entry.message ?? "");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string messageText = entry.role == "assistant"
|
||||||
|
? ParseResponseForDisplay(entry.message)
|
||||||
|
: AIIntelligenceCore.StripContextInfo(entry.message);
|
||||||
|
|
||||||
|
if (entry.role == "system") continue;
|
||||||
// Hide auto-commentary system messages (user-side) from display
|
// Hide auto-commentary system messages (user-side) from display
|
||||||
if (entry.role == "user" && entry.message.Contains("[AUTO_COMMENTARY]")) continue;
|
if (entry.role == "user" && entry.message.Contains("[AUTO_COMMENTARY]")) continue;
|
||||||
|
if (entry.role == "assistant" && traceEnabled && toolcallBuffer.Count > 0)
|
||||||
|
{
|
||||||
|
var traceLines = BuildTraceLines(toolcallBuffer, toolResultBuffer);
|
||||||
|
if (traceLines.Count > 0)
|
||||||
|
{
|
||||||
|
int traceKey = i;
|
||||||
|
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved;
|
||||||
|
string header = BuildReactTraceHeader();
|
||||||
|
|
||||||
|
Text.Font = GameFont.Tiny;
|
||||||
|
float tracePadding = 8f;
|
||||||
|
float headerWidth = Mathf.Max(10f, contentWidth - tracePadding * 2f);
|
||||||
|
string headerLine = $"{(expanded ? "v" : ">")} {header}";
|
||||||
|
float headerHeight = Text.CalcHeight(headerLine, headerWidth) + 10f;
|
||||||
|
float linesHeight = 0f;
|
||||||
|
if (expanded)
|
||||||
|
{
|
||||||
|
float lineWidth = Mathf.Max(10f, contentWidth - tracePadding * 2f);
|
||||||
|
foreach (string line in traceLines)
|
||||||
|
{
|
||||||
|
linesHeight += Text.CalcHeight(line, lineWidth) + 2f;
|
||||||
|
}
|
||||||
|
linesHeight += 8f;
|
||||||
|
}
|
||||||
|
float traceHeight = headerHeight + linesHeight;
|
||||||
|
|
||||||
|
_cachedMessages.Add(new CachedMessage
|
||||||
|
{
|
||||||
|
role = "trace",
|
||||||
|
message = "",
|
||||||
|
displayText = "",
|
||||||
|
height = traceHeight,
|
||||||
|
yOffset = curY,
|
||||||
|
font = GameFont.Tiny,
|
||||||
|
isTrace = true,
|
||||||
|
traceKey = traceKey,
|
||||||
|
traceHeader = header,
|
||||||
|
traceLines = traceLines,
|
||||||
|
traceExpanded = expanded,
|
||||||
|
traceHeaderHeight = headerHeight
|
||||||
|
});
|
||||||
|
curY += traceHeight + 10f;
|
||||||
|
}
|
||||||
|
|
||||||
|
toolcallBuffer.Clear();
|
||||||
|
toolResultBuffer.Clear();
|
||||||
|
}
|
||||||
if (string.IsNullOrEmpty(messageText) || (entry.role == "user" && messageText.StartsWith("[Tool Results]"))) continue;
|
if (string.IsNullOrEmpty(messageText) || (entry.role == "user" && messageText.StartsWith("[Tool Results]"))) continue;
|
||||||
|
|
||||||
bool isLastMessage = i == history.Count - 1;
|
bool isLastMessage = i == history.Count - 1;
|
||||||
@@ -368,8 +437,6 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
|
|
||||||
curY += height + 10f;
|
curY += height + 10f;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateReactTraceCache(history, contentWidth, ref curY);
|
|
||||||
_cachedTotalHeight = curY;
|
_cachedTotalHeight = curY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,7 +474,11 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
Text.Font = entry.font;
|
Text.Font = entry.font;
|
||||||
Rect labelRect = new Rect(innerPadding, entry.yOffset, contentWidth, entry.height);
|
Rect labelRect = new Rect(innerPadding, entry.yOffset, contentWidth, entry.height);
|
||||||
|
|
||||||
if (entry.role == "user")
|
if (entry.isTrace)
|
||||||
|
{
|
||||||
|
DrawReactTracePanel(labelRect, entry);
|
||||||
|
}
|
||||||
|
else if (entry.role == "user")
|
||||||
{
|
{
|
||||||
Text.Anchor = TextAnchor.MiddleRight;
|
Text.Anchor = TextAnchor.MiddleRight;
|
||||||
Widgets.Label(labelRect, $"<color=#add8e6>{entry.displayText}</color>");
|
Widgets.Label(labelRect, $"<color=#add8e6>{entry.displayText}</color>");
|
||||||
@@ -426,15 +497,6 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_hasReactTrace)
|
|
||||||
{
|
|
||||||
Rect traceRect = new Rect(innerPadding, _reactTraceYOffset, contentWidth, _reactTraceHeight);
|
|
||||||
if (traceRect.yMax >= viewTop && traceRect.y <= viewBottom)
|
|
||||||
{
|
|
||||||
DrawReactTracePanel(traceRect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_isThinking)
|
if (_isThinking)
|
||||||
{
|
{
|
||||||
float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 0f;
|
float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 0f;
|
||||||
@@ -471,83 +533,20 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
return text.Split(new[] { "OPTIONS:" }, StringSplitOptions.None)[0].Trim();
|
return text.Split(new[] { "OPTIONS:" }, StringSplitOptions.None)[0].Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateReactTraceCache(List<(string role, string message)> history, float contentWidth, ref float curY)
|
private List<string> BuildTraceLines(List<string> toolcallBuffer, List<string> toolResultBuffer)
|
||||||
{
|
{
|
||||||
_hasReactTrace = false;
|
var lines = new List<string>();
|
||||||
_reactTraceLines.Clear();
|
if (toolcallBuffer == null || toolcallBuffer.Count == 0) return lines;
|
||||||
_reactTraceHeader = "";
|
|
||||||
_reactTraceHeight = 0f;
|
|
||||||
_reactTraceYOffset = 0f;
|
|
||||||
_reactTraceHeaderHeight = 0f;
|
|
||||||
|
|
||||||
if (WulaFallenEmpireMod.settings?.showReactTraceInUI != true)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ReactTraceStep> steps = BuildReactTraceSteps(history);
|
|
||||||
if (steps.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_hasReactTrace = true;
|
|
||||||
_reactTraceHeader = BuildReactTraceHeader();
|
|
||||||
foreach (var step in steps)
|
|
||||||
{
|
|
||||||
foreach (string call in step.Calls)
|
|
||||||
{
|
|
||||||
_reactTraceLines.Add($"Step {step.Step}: call {call}");
|
|
||||||
}
|
|
||||||
foreach (string result in step.Results)
|
|
||||||
{
|
|
||||||
_reactTraceLines.Add($"Step {step.Step}: result {result}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var originalFont = Text.Font;
|
|
||||||
Text.Font = GameFont.Tiny;
|
|
||||||
float panelPadding = 6f;
|
|
||||||
float headerWidth = Mathf.Max(10f, contentWidth - panelPadding * 2f);
|
|
||||||
string headerLine = $"{(_reactTraceExpanded ? "v" : ">")} {_reactTraceHeader}";
|
|
||||||
_reactTraceHeaderHeight = Text.CalcHeight(headerLine, headerWidth) + 6f;
|
|
||||||
|
|
||||||
float linesHeight = 0f;
|
|
||||||
if (_reactTraceExpanded && _reactTraceLines.Count > 0)
|
|
||||||
{
|
|
||||||
float lineWidth = Mathf.Max(10f, contentWidth - panelPadding * 2f);
|
|
||||||
foreach (string line in _reactTraceLines)
|
|
||||||
{
|
|
||||||
linesHeight += Text.CalcHeight(line, lineWidth) + 2f;
|
|
||||||
}
|
|
||||||
linesHeight += 4f;
|
|
||||||
}
|
|
||||||
_reactTraceHeight = _reactTraceHeaderHeight + linesHeight;
|
|
||||||
Text.Font = originalFont;
|
|
||||||
|
|
||||||
curY += 6f;
|
|
||||||
_reactTraceYOffset = curY;
|
|
||||||
curY += _reactTraceHeight + 8f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ReactTraceStep> BuildReactTraceSteps(List<(string role, string message)> history)
|
|
||||||
{
|
|
||||||
var steps = new List<ReactTraceStep>();
|
|
||||||
if (history == null || history.Count == 0) return steps;
|
|
||||||
|
|
||||||
int lastUserIndex = history.FindLastIndex(entry => entry.role == "user");
|
|
||||||
if (lastUserIndex < 0) return steps;
|
|
||||||
|
|
||||||
int stepIndex = 0;
|
int stepIndex = 0;
|
||||||
for (int i = lastUserIndex + 1; i < history.Count; i++)
|
int maxSteps = Math.Max(toolcallBuffer.Count, toolResultBuffer.Count);
|
||||||
|
for (int i = 0; i < maxSteps; i++)
|
||||||
{
|
{
|
||||||
var entry = history[i];
|
bool anyStepContent = false;
|
||||||
if (entry.role != "toolcall") continue;
|
|
||||||
|
|
||||||
stepIndex++;
|
stepIndex++;
|
||||||
var step = new ReactTraceStep { Step = stepIndex };
|
|
||||||
|
|
||||||
if (JsonToolCallParser.TryParseToolCallsFromText(entry.message, out var calls, out _))
|
if (i < toolcallBuffer.Count &&
|
||||||
|
JsonToolCallParser.TryParseToolCallsFromText(toolcallBuffer[i], out var calls, out _))
|
||||||
{
|
{
|
||||||
foreach (var call in calls)
|
foreach (var call in calls)
|
||||||
{
|
{
|
||||||
@@ -556,23 +555,27 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
string callText = string.IsNullOrWhiteSpace(args) || args == "{}"
|
string callText = string.IsNullOrWhiteSpace(args) || args == "{}"
|
||||||
? call.Name
|
? call.Name
|
||||||
: $"{call.Name} {TrimForDisplay(args, 160)}";
|
: $"{call.Name} {TrimForDisplay(args, 160)}";
|
||||||
step.Calls.Add(callText);
|
lines.Add($"步骤 {stepIndex} · 调用 {callText}");
|
||||||
|
anyStepContent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i + 1 < history.Count && history[i + 1].role == "tool")
|
if (i < toolResultBuffer.Count)
|
||||||
{
|
{
|
||||||
step.Results.AddRange(ExtractToolResultLines(history[i + 1].message, 4));
|
foreach (string resultLine in ExtractToolResultLines(toolResultBuffer[i], 4))
|
||||||
i++;
|
{
|
||||||
|
lines.Add($"步骤 {stepIndex} · 结果 {resultLine}");
|
||||||
|
anyStepContent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (step.Calls.Count > 0 || step.Results.Count > 0)
|
if (!anyStepContent)
|
||||||
{
|
{
|
||||||
steps.Add(step);
|
stepIndex--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return steps;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<string> ExtractToolResultLines(string toolMessage, int maxLines)
|
private static List<string> ExtractToolResultLines(string toolMessage, int maxLines)
|
||||||
@@ -586,6 +589,13 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
string line = raw.Trim();
|
string line = raw.Trim();
|
||||||
if (string.IsNullOrEmpty(line)) continue;
|
if (string.IsNullOrEmpty(line)) continue;
|
||||||
if (line.StartsWith("[Tool Results]", StringComparison.OrdinalIgnoreCase)) continue;
|
if (line.StartsWith("[Tool Results]", StringComparison.OrdinalIgnoreCase)) continue;
|
||||||
|
if (!line.StartsWith("Tool '", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!line.StartsWith("ToolRunner", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!line.StartsWith("Query Result:", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!line.StartsWith("Error:", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
lines.Add(TrimForDisplay(line, 200));
|
lines.Add(TrimForDisplay(line, 200));
|
||||||
if (lines.Count >= maxLines) break;
|
if (lines.Count >= maxLines) break;
|
||||||
}
|
}
|
||||||
@@ -606,37 +616,43 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
? Mathf.Max(0f, Time.realtimeSinceStartup - (_core?.ThinkingStartTime ?? 0f))
|
? Mathf.Max(0f, Time.realtimeSinceStartup - (_core?.ThinkingStartTime ?? 0f))
|
||||||
: _core?.LastThinkingDuration ?? 0f;
|
: _core?.LastThinkingDuration ?? 0f;
|
||||||
string elapsedText = elapsed > 0f ? elapsed.ToString("0.0", CultureInfo.InvariantCulture) : "0.0";
|
string elapsedText = elapsed > 0f ? elapsed.ToString("0.0", CultureInfo.InvariantCulture) : "0.0";
|
||||||
return $"{state} ({elapsedText}s · Loop {_core?.ThinkingPhaseIndex ?? 0}/{_core?.ThinkingPhaseTotal ?? 0})";
|
return $"{state} (用时 {elapsedText}s · Loop {_core?.ThinkingPhaseIndex ?? 0}/{_core?.ThinkingPhaseTotal ?? 0})";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawReactTracePanel(Rect rect)
|
private void DrawReactTracePanel(Rect rect, CachedMessage traceEntry)
|
||||||
{
|
{
|
||||||
var originalColor = GUI.color;
|
var originalColor = GUI.color;
|
||||||
var originalFont = Text.Font;
|
var originalFont = Text.Font;
|
||||||
var originalAnchor = Text.Anchor;
|
var originalAnchor = Text.Anchor;
|
||||||
|
|
||||||
float padding = 6f;
|
float padding = 8f;
|
||||||
Rect headerRect = new Rect(rect.x, rect.y, rect.width, _reactTraceHeaderHeight);
|
Rect headerRect = new Rect(rect.x, rect.y, rect.width, traceEntry.traceHeaderHeight);
|
||||||
GUI.color = new Color(0.15f, 0.15f, 0.15f, 0.8f);
|
GUI.color = new Color(0.15f, 0.15f, 0.15f, 0.8f);
|
||||||
Widgets.DrawBoxSolid(headerRect, GUI.color);
|
Widgets.DrawBoxSolid(headerRect, GUI.color);
|
||||||
GUI.color = Color.white;
|
GUI.color = Color.white;
|
||||||
|
|
||||||
Text.Font = GameFont.Tiny;
|
Text.Font = GameFont.Tiny;
|
||||||
Text.Anchor = TextAnchor.MiddleLeft;
|
Text.Anchor = TextAnchor.MiddleLeft;
|
||||||
string headerLine = $"{(_reactTraceExpanded ? "v" : ">")} {_reactTraceHeader}";
|
string headerLine = $"{(traceEntry.traceExpanded ? "v" : ">")} {traceEntry.traceHeader}";
|
||||||
Widgets.Label(headerRect.ContractedBy(padding), headerLine);
|
Widgets.Label(headerRect.ContractedBy(padding, 4f), headerLine);
|
||||||
|
|
||||||
if (Widgets.ButtonInvisible(headerRect))
|
if (Widgets.ButtonInvisible(headerRect))
|
||||||
{
|
{
|
||||||
_reactTraceExpanded = !_reactTraceExpanded;
|
traceEntry.traceExpanded = !traceEntry.traceExpanded;
|
||||||
|
_traceExpandedByAssistantIndex[traceEntry.traceKey] = traceEntry.traceExpanded;
|
||||||
_lastHistoryCount = -1;
|
_lastHistoryCount = -1;
|
||||||
_lastUsedWidth = -1f;
|
_lastUsedWidth = -1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_reactTraceExpanded && _reactTraceLines.Count > 0)
|
if (traceEntry.traceExpanded && traceEntry.traceLines != null && traceEntry.traceLines.Count > 0)
|
||||||
{
|
{
|
||||||
float y = headerRect.yMax + 2f;
|
Rect bodyRect = new Rect(rect.x, headerRect.yMax, rect.width, rect.height - traceEntry.traceHeaderHeight);
|
||||||
foreach (string line in _reactTraceLines)
|
GUI.color = new Color(0.12f, 0.12f, 0.12f, 0.45f);
|
||||||
|
Widgets.DrawBoxSolid(bodyRect, GUI.color);
|
||||||
|
GUI.color = Color.white;
|
||||||
|
|
||||||
|
float y = headerRect.yMax + 6f;
|
||||||
|
foreach (string line in traceEntry.traceLines)
|
||||||
{
|
{
|
||||||
float lineHeight = Text.CalcHeight(line, rect.width - padding * 2f) + 2f;
|
float lineHeight = Text.CalcHeight(line, rect.width - padding * 2f) + 2f;
|
||||||
Rect lineRect = new Rect(rect.x + padding, y, rect.width - padding * 2f, lineHeight);
|
Rect lineRect = new Rect(rect.x + padding, y, rect.width - padding * 2f, lineHeight);
|
||||||
|
|||||||
@@ -25,13 +25,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
private float _lastUsedWidth = -1f;
|
private float _lastUsedWidth = -1f;
|
||||||
private List<CachedMessage> _cachedMessages = new List<CachedMessage>();
|
private List<CachedMessage> _cachedMessages = new List<CachedMessage>();
|
||||||
private float _cachedTotalHeight = 0f;
|
private float _cachedTotalHeight = 0f;
|
||||||
private bool _reactTraceExpanded = false;
|
private readonly Dictionary<int, bool> _traceExpandedByAssistantIndex = new Dictionary<int, bool>();
|
||||||
private bool _hasReactTrace = false;
|
|
||||||
private float _reactTraceHeight = 0f;
|
|
||||||
private float _reactTraceYOffset = 0f;
|
|
||||||
private float _reactTraceHeaderHeight = 0f;
|
|
||||||
private string _reactTraceHeader = "";
|
|
||||||
private List<string> _reactTraceLines = new List<string>();
|
|
||||||
|
|
||||||
private class CachedMessage
|
private class CachedMessage
|
||||||
{
|
{
|
||||||
@@ -40,13 +34,12 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
public string displayText;
|
public string displayText;
|
||||||
public float height;
|
public float height;
|
||||||
public float yOffset;
|
public float yOffset;
|
||||||
}
|
public bool isTrace;
|
||||||
|
public int traceKey;
|
||||||
private class ReactTraceStep
|
public string traceHeader;
|
||||||
{
|
public List<string> traceLines;
|
||||||
public int Step;
|
public bool traceExpanded;
|
||||||
public List<string> Calls = new List<string>();
|
public float traceHeaderHeight;
|
||||||
public List<string> Results = new List<string>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -389,11 +382,38 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
_cachedTotalHeight = 0f;
|
_cachedTotalHeight = 0f;
|
||||||
float reducedSpacing = 8f;
|
float reducedSpacing = 8f;
|
||||||
float curY = 10f;
|
float curY = 10f;
|
||||||
|
var toolcallBuffer = new List<string>();
|
||||||
|
var toolResultBuffer = new List<string>();
|
||||||
|
bool traceEnabled = WulaFallenEmpireMod.settings?.showReactTraceInUI == true;
|
||||||
|
|
||||||
foreach (var msg in history)
|
for (int i = 0; i < history.Count; i++)
|
||||||
{
|
{
|
||||||
|
var msg = history[i];
|
||||||
// Filter logic
|
// Filter logic
|
||||||
if (msg.role == "tool" || msg.role == "toolcall") continue;
|
if (msg.role == "user")
|
||||||
|
{
|
||||||
|
toolcallBuffer.Clear();
|
||||||
|
toolResultBuffer.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.role == "toolcall")
|
||||||
|
{
|
||||||
|
if (traceEnabled)
|
||||||
|
{
|
||||||
|
toolcallBuffer.Add(msg.message ?? "");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.role == "tool")
|
||||||
|
{
|
||||||
|
if (traceEnabled)
|
||||||
|
{
|
||||||
|
toolResultBuffer.Add(msg.message ?? "");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.role == "system" && !Prefs.DevMode) continue;
|
if (msg.role == "system" && !Prefs.DevMode) continue;
|
||||||
|
|
||||||
// Hide auto-commentary system messages (user-side) from display
|
// Hide auto-commentary system messages (user-side) from display
|
||||||
@@ -409,6 +429,53 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
displayText = AIIntelligenceCore.StripContextInfo(msg.message);
|
displayText = AIIntelligenceCore.StripContextInfo(msg.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.role == "assistant" && traceEnabled && toolcallBuffer.Count > 0)
|
||||||
|
{
|
||||||
|
var traceLines = BuildTraceLines(toolcallBuffer, toolResultBuffer);
|
||||||
|
if (traceLines.Count > 0)
|
||||||
|
{
|
||||||
|
int traceKey = i;
|
||||||
|
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved;
|
||||||
|
string header = BuildReactTraceHeader();
|
||||||
|
|
||||||
|
Text.Font = GameFont.Tiny;
|
||||||
|
float padding = 8f;
|
||||||
|
float headerWidth = Mathf.Max(10f, width - padding * 2f);
|
||||||
|
string headerLine = $"{(expanded ? "v" : ">")} {header}";
|
||||||
|
float headerHeight = Text.CalcHeight(headerLine, headerWidth) + 10f;
|
||||||
|
float linesHeight = 0f;
|
||||||
|
if (expanded)
|
||||||
|
{
|
||||||
|
float lineWidth = Mathf.Max(10f, width - padding * 2f);
|
||||||
|
foreach (string line in traceLines)
|
||||||
|
{
|
||||||
|
linesHeight += Text.CalcHeight(line, lineWidth) + 2f;
|
||||||
|
}
|
||||||
|
linesHeight += 8f;
|
||||||
|
}
|
||||||
|
float traceHeight = headerHeight + linesHeight;
|
||||||
|
|
||||||
|
_cachedMessages.Add(new CachedMessage
|
||||||
|
{
|
||||||
|
role = "trace",
|
||||||
|
message = "",
|
||||||
|
displayText = "",
|
||||||
|
height = traceHeight,
|
||||||
|
yOffset = curY,
|
||||||
|
isTrace = true,
|
||||||
|
traceKey = traceKey,
|
||||||
|
traceHeader = header,
|
||||||
|
traceLines = traceLines,
|
||||||
|
traceExpanded = expanded,
|
||||||
|
traceHeaderHeight = headerHeight
|
||||||
|
});
|
||||||
|
curY += traceHeight + reducedSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
toolcallBuffer.Clear();
|
||||||
|
toolResultBuffer.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(displayText)) continue;
|
if (string.IsNullOrWhiteSpace(displayText)) continue;
|
||||||
|
|
||||||
float h = CalcMessageHeight(displayText, width);
|
float h = CalcMessageHeight(displayText, width);
|
||||||
@@ -425,7 +492,6 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
curY += h + reducedSpacing;
|
curY += h + reducedSpacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateReactTraceCache(history, width, ref curY);
|
|
||||||
_cachedTotalHeight = curY;
|
_cachedTotalHeight = curY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,8 +527,12 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
if (entry.yOffset > viewBottom + 100f) break;
|
if (entry.yOffset > viewBottom + 100f) break;
|
||||||
|
|
||||||
Rect msgRect = new Rect(0, entry.yOffset, width, entry.height);
|
Rect msgRect = new Rect(0, entry.yOffset, width, entry.height);
|
||||||
|
|
||||||
if (entry.role == "user")
|
if (entry.isTrace)
|
||||||
|
{
|
||||||
|
DrawReactTracePanel(msgRect, entry);
|
||||||
|
}
|
||||||
|
else if (entry.role == "user")
|
||||||
{
|
{
|
||||||
DrawSenseiMessage(msgRect, entry.displayText);
|
DrawSenseiMessage(msgRect, entry.displayText);
|
||||||
}
|
}
|
||||||
@@ -480,15 +550,6 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_hasReactTrace)
|
|
||||||
{
|
|
||||||
Rect traceRect = new Rect(0, _reactTraceYOffset, width, _reactTraceHeight);
|
|
||||||
if (traceRect.yMax >= viewTop && traceRect.y <= viewBottom)
|
|
||||||
{
|
|
||||||
DrawReactTracePanel(traceRect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_core != null && _core.IsThinking)
|
if (_core != null && _core.IsThinking)
|
||||||
{
|
{
|
||||||
float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 10f;
|
float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 10f;
|
||||||
@@ -515,83 +576,20 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
return text.Remove(index, fragment.Length).Trim();
|
return text.Remove(index, fragment.Length).Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateReactTraceCache(List<(string role, string message)> history, float contentWidth, ref float curY)
|
private List<string> BuildTraceLines(List<string> toolcallBuffer, List<string> toolResultBuffer)
|
||||||
{
|
{
|
||||||
_hasReactTrace = false;
|
var lines = new List<string>();
|
||||||
_reactTraceLines.Clear();
|
if (toolcallBuffer == null || toolcallBuffer.Count == 0) return lines;
|
||||||
_reactTraceHeader = "";
|
|
||||||
_reactTraceHeight = 0f;
|
|
||||||
_reactTraceYOffset = 0f;
|
|
||||||
_reactTraceHeaderHeight = 0f;
|
|
||||||
|
|
||||||
if (WulaFallenEmpireMod.settings?.showReactTraceInUI != true)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ReactTraceStep> steps = BuildReactTraceSteps(history);
|
|
||||||
if (steps.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_hasReactTrace = true;
|
|
||||||
_reactTraceHeader = BuildReactTraceHeader();
|
|
||||||
foreach (var step in steps)
|
|
||||||
{
|
|
||||||
foreach (string call in step.Calls)
|
|
||||||
{
|
|
||||||
_reactTraceLines.Add($"Step {step.Step}: call {call}");
|
|
||||||
}
|
|
||||||
foreach (string result in step.Results)
|
|
||||||
{
|
|
||||||
_reactTraceLines.Add($"Step {step.Step}: result {result}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var originalFont = Text.Font;
|
|
||||||
Text.Font = GameFont.Tiny;
|
|
||||||
float panelPadding = 8f;
|
|
||||||
float headerWidth = Mathf.Max(10f, contentWidth - panelPadding * 2f);
|
|
||||||
string headerLine = $"{(_reactTraceExpanded ? "v" : ">")} {_reactTraceHeader}";
|
|
||||||
_reactTraceHeaderHeight = Text.CalcHeight(headerLine, headerWidth) + 6f;
|
|
||||||
|
|
||||||
float linesHeight = 0f;
|
|
||||||
if (_reactTraceExpanded && _reactTraceLines.Count > 0)
|
|
||||||
{
|
|
||||||
float lineWidth = Mathf.Max(10f, contentWidth - panelPadding * 2f);
|
|
||||||
foreach (string line in _reactTraceLines)
|
|
||||||
{
|
|
||||||
linesHeight += Text.CalcHeight(line, lineWidth) + 2f;
|
|
||||||
}
|
|
||||||
linesHeight += 6f;
|
|
||||||
}
|
|
||||||
_reactTraceHeight = _reactTraceHeaderHeight + linesHeight;
|
|
||||||
Text.Font = originalFont;
|
|
||||||
|
|
||||||
curY += 6f;
|
|
||||||
_reactTraceYOffset = curY;
|
|
||||||
curY += _reactTraceHeight + 6f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ReactTraceStep> BuildReactTraceSteps(List<(string role, string message)> history)
|
|
||||||
{
|
|
||||||
var steps = new List<ReactTraceStep>();
|
|
||||||
if (history == null || history.Count == 0) return steps;
|
|
||||||
|
|
||||||
int lastUserIndex = history.FindLastIndex(entry => entry.role == "user");
|
|
||||||
if (lastUserIndex < 0) return steps;
|
|
||||||
|
|
||||||
int stepIndex = 0;
|
int stepIndex = 0;
|
||||||
for (int i = lastUserIndex + 1; i < history.Count; i++)
|
int maxSteps = Math.Max(toolcallBuffer.Count, toolResultBuffer.Count);
|
||||||
|
for (int i = 0; i < maxSteps; i++)
|
||||||
{
|
{
|
||||||
var entry = history[i];
|
bool anyStepContent = false;
|
||||||
if (entry.role != "toolcall") continue;
|
|
||||||
|
|
||||||
stepIndex++;
|
stepIndex++;
|
||||||
var step = new ReactTraceStep { Step = stepIndex };
|
|
||||||
|
|
||||||
if (JsonToolCallParser.TryParseToolCallsFromText(entry.message, out var calls, out _))
|
if (i < toolcallBuffer.Count &&
|
||||||
|
JsonToolCallParser.TryParseToolCallsFromText(toolcallBuffer[i], out var calls, out _))
|
||||||
{
|
{
|
||||||
foreach (var call in calls)
|
foreach (var call in calls)
|
||||||
{
|
{
|
||||||
@@ -600,23 +598,27 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
string callText = string.IsNullOrWhiteSpace(args) || args == "{}"
|
string callText = string.IsNullOrWhiteSpace(args) || args == "{}"
|
||||||
? call.Name
|
? call.Name
|
||||||
: $"{call.Name} {TrimForDisplay(args, 160)}";
|
: $"{call.Name} {TrimForDisplay(args, 160)}";
|
||||||
step.Calls.Add(callText);
|
lines.Add($"步骤 {stepIndex} · 调用 {callText}");
|
||||||
|
anyStepContent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i + 1 < history.Count && history[i + 1].role == "tool")
|
if (i < toolResultBuffer.Count)
|
||||||
{
|
{
|
||||||
step.Results.AddRange(ExtractToolResultLines(history[i + 1].message, 4));
|
foreach (string resultLine in ExtractToolResultLines(toolResultBuffer[i], 4))
|
||||||
i++;
|
{
|
||||||
|
lines.Add($"步骤 {stepIndex} · 结果 {resultLine}");
|
||||||
|
anyStepContent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (step.Calls.Count > 0 || step.Results.Count > 0)
|
if (!anyStepContent)
|
||||||
{
|
{
|
||||||
steps.Add(step);
|
stepIndex--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return steps;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<string> ExtractToolResultLines(string toolMessage, int maxLines)
|
private static List<string> ExtractToolResultLines(string toolMessage, int maxLines)
|
||||||
@@ -630,6 +632,13 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
string line = raw.Trim();
|
string line = raw.Trim();
|
||||||
if (string.IsNullOrEmpty(line)) continue;
|
if (string.IsNullOrEmpty(line)) continue;
|
||||||
if (line.StartsWith("[Tool Results]", StringComparison.OrdinalIgnoreCase)) continue;
|
if (line.StartsWith("[Tool Results]", StringComparison.OrdinalIgnoreCase)) continue;
|
||||||
|
if (!line.StartsWith("Tool '", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!line.StartsWith("ToolRunner", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!line.StartsWith("Query Result:", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!line.StartsWith("Error:", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
lines.Add(TrimForDisplay(line, 200));
|
lines.Add(TrimForDisplay(line, 200));
|
||||||
if (lines.Count >= maxLines) break;
|
if (lines.Count >= maxLines) break;
|
||||||
}
|
}
|
||||||
@@ -653,37 +662,43 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
string elapsedText = elapsed > 0f ? elapsed.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture) : "0.0";
|
string elapsedText = elapsed > 0f ? elapsed.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture) : "0.0";
|
||||||
int phaseIndex = _core?.ThinkingPhaseIndex ?? 0;
|
int phaseIndex = _core?.ThinkingPhaseIndex ?? 0;
|
||||||
int phaseTotal = _core?.ThinkingPhaseTotal ?? 0;
|
int phaseTotal = _core?.ThinkingPhaseTotal ?? 0;
|
||||||
return $"{state} ({elapsedText}s · Loop {phaseIndex}/{phaseTotal})";
|
return $"{state} (用时 {elapsedText}s · Loop {phaseIndex}/{phaseTotal})";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawReactTracePanel(Rect rect)
|
private void DrawReactTracePanel(Rect rect, CachedMessage traceEntry)
|
||||||
{
|
{
|
||||||
var originalColor = GUI.color;
|
var originalColor = GUI.color;
|
||||||
var originalFont = Text.Font;
|
var originalFont = Text.Font;
|
||||||
var originalAnchor = Text.Anchor;
|
var originalAnchor = Text.Anchor;
|
||||||
|
|
||||||
float padding = 8f;
|
float padding = 8f;
|
||||||
Rect headerRect = new Rect(rect.x, rect.y, rect.width, _reactTraceHeaderHeight);
|
Rect headerRect = new Rect(rect.x, rect.y, rect.width, traceEntry.traceHeaderHeight);
|
||||||
GUI.color = new Color(0.12f, 0.12f, 0.12f, 0.8f);
|
GUI.color = new Color(0.12f, 0.12f, 0.12f, 0.8f);
|
||||||
Widgets.DrawBoxSolid(headerRect, GUI.color);
|
Widgets.DrawBoxSolid(headerRect, GUI.color);
|
||||||
GUI.color = Color.white;
|
GUI.color = Color.white;
|
||||||
|
|
||||||
Text.Font = GameFont.Tiny;
|
Text.Font = GameFont.Tiny;
|
||||||
Text.Anchor = TextAnchor.MiddleLeft;
|
Text.Anchor = TextAnchor.MiddleLeft;
|
||||||
string headerLine = $"{(_reactTraceExpanded ? "v" : ">")} {_reactTraceHeader}";
|
string headerLine = $"{(traceEntry.traceExpanded ? "v" : ">")} {traceEntry.traceHeader}";
|
||||||
Widgets.Label(headerRect.ContractedBy(padding), headerLine);
|
Widgets.Label(headerRect.ContractedBy(padding, 4f), headerLine);
|
||||||
|
|
||||||
if (Widgets.ButtonInvisible(headerRect))
|
if (Widgets.ButtonInvisible(headerRect))
|
||||||
{
|
{
|
||||||
_reactTraceExpanded = !_reactTraceExpanded;
|
traceEntry.traceExpanded = !traceEntry.traceExpanded;
|
||||||
|
_traceExpandedByAssistantIndex[traceEntry.traceKey] = traceEntry.traceExpanded;
|
||||||
_lastHistoryCount = -1;
|
_lastHistoryCount = -1;
|
||||||
_lastUsedWidth = -1f;
|
_lastUsedWidth = -1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_reactTraceExpanded && _reactTraceLines.Count > 0)
|
if (traceEntry.traceExpanded && traceEntry.traceLines != null && traceEntry.traceLines.Count > 0)
|
||||||
{
|
{
|
||||||
float y = headerRect.yMax + 2f;
|
Rect bodyRect = new Rect(rect.x, headerRect.yMax, rect.width, rect.height - traceEntry.traceHeaderHeight);
|
||||||
foreach (string line in _reactTraceLines)
|
GUI.color = new Color(0.1f, 0.1f, 0.1f, 0.45f);
|
||||||
|
Widgets.DrawBoxSolid(bodyRect, GUI.color);
|
||||||
|
GUI.color = Color.white;
|
||||||
|
|
||||||
|
float y = headerRect.yMax + 6f;
|
||||||
|
foreach (string line in traceEntry.traceLines)
|
||||||
{
|
{
|
||||||
float lineHeight = Text.CalcHeight(line, rect.width - padding * 2f) + 2f;
|
float lineHeight = Text.CalcHeight(line, rect.width - padding * 2f) + 2f;
|
||||||
Rect lineRect = new Rect(rect.x + padding, y, rect.width - padding * 2f, lineHeight);
|
Rect lineRect = new Rect(rect.x + padding, y, rect.width - padding * 2f, lineHeight);
|
||||||
|
|||||||
Reference in New Issue
Block a user