zc
This commit is contained in:
Binary file not shown.
@@ -964,6 +964,13 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
return trimmed.Length >= 4;
|
return trimmed.Length >= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddTraceNote(string note)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(note)) return;
|
||||||
|
_history.Add(("trace", note.Trim()));
|
||||||
|
PersistHistory();
|
||||||
|
}
|
||||||
|
|
||||||
private bool IsToolAvailable(string toolName)
|
private bool IsToolAvailable(string toolName)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(toolName)) return false;
|
if (string.IsNullOrWhiteSpace(toolName)) return false;
|
||||||
@@ -1165,6 +1172,10 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (string.Equals(entry.role, "trace", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (string.Equals(entry.role, "tool", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(entry.role, "tool", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (lastUserIndex != -1 && i > lastUserIndex)
|
if (lastUserIndex != -1 && i > lastUserIndex)
|
||||||
@@ -1886,6 +1897,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
if (LooksLikeNaturalReply(normalizedResponse))
|
if (LooksLikeNaturalReply(normalizedResponse))
|
||||||
{
|
{
|
||||||
toolPhaseReplyCandidate = normalizedResponse;
|
toolPhaseReplyCandidate = normalizedResponse;
|
||||||
|
AddTraceNote(normalizedResponse);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1902,6 +1914,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
if (LooksLikeNaturalReply(normalizedFixed))
|
if (LooksLikeNaturalReply(normalizedFixed))
|
||||||
{
|
{
|
||||||
toolPhaseReplyCandidate = normalizedFixed;
|
toolPhaseReplyCandidate = normalizedFixed;
|
||||||
|
AddTraceNote(normalizedFixed);
|
||||||
}
|
}
|
||||||
if (Prefs.DevMode)
|
if (Prefs.DevMode)
|
||||||
{
|
{
|
||||||
@@ -1932,6 +1945,24 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(parsedSource))
|
||||||
|
{
|
||||||
|
string traceText = parsedSource;
|
||||||
|
if (!string.IsNullOrWhiteSpace(jsonFragment))
|
||||||
|
{
|
||||||
|
int idx = traceText.IndexOf(jsonFragment, StringComparison.Ordinal);
|
||||||
|
if (idx >= 0)
|
||||||
|
{
|
||||||
|
traceText = traceText.Remove(idx, jsonFragment.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traceText = traceText.Trim();
|
||||||
|
if (LooksLikeNaturalReply(traceText))
|
||||||
|
{
|
||||||
|
AddTraceNote(traceText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var invalidTools = toolCalls
|
var invalidTools = toolCalls
|
||||||
.Where(c => !IsToolAvailable(c.Name))
|
.Where(c => !IsToolAvailable(c.Name))
|
||||||
.Select(c => c.Name)
|
.Select(c => c.Name)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
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 readonly Dictionary<int, bool> _traceExpandedByAssistantIndex = new Dictionary<int, bool>();
|
private readonly Dictionary<int, bool> _traceExpandedByAssistantIndex = new Dictionary<int, bool>();
|
||||||
|
private readonly Dictionary<int, string> _traceHeaderByAssistantIndex = new Dictionary<int, string>();
|
||||||
|
|
||||||
private class CachedMessage
|
private class CachedMessage
|
||||||
{
|
{
|
||||||
@@ -325,6 +326,11 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
if (Math.Abs(_lastUsedWidth - width) < 0.1f && history.Count == _lastHistoryCount) return;
|
if (Math.Abs(_lastUsedWidth - width) < 0.1f && history.Count == _lastHistoryCount) return;
|
||||||
|
|
||||||
_lastUsedWidth = width;
|
_lastUsedWidth = width;
|
||||||
|
if (_lastHistoryCount >= 0 && history.Count < _lastHistoryCount)
|
||||||
|
{
|
||||||
|
_traceExpandedByAssistantIndex.Clear();
|
||||||
|
_traceHeaderByAssistantIndex.Clear();
|
||||||
|
}
|
||||||
_lastHistoryCount = history.Count;
|
_lastHistoryCount = history.Count;
|
||||||
_cachedMessages.Clear();
|
_cachedMessages.Clear();
|
||||||
_cachedTotalHeight = 0f;
|
_cachedTotalHeight = 0f;
|
||||||
@@ -333,6 +339,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
float contentWidth = width - innerPadding * 2;
|
float contentWidth = width - innerPadding * 2;
|
||||||
var toolcallBuffer = new List<string>();
|
var toolcallBuffer = new List<string>();
|
||||||
var toolResultBuffer = new List<string>();
|
var toolResultBuffer = new List<string>();
|
||||||
|
var traceNoteBuffer = new List<string>();
|
||||||
bool traceEnabled = WulaFallenEmpireMod.settings?.showReactTraceInUI == true;
|
bool traceEnabled = WulaFallenEmpireMod.settings?.showReactTraceInUI == true;
|
||||||
|
|
||||||
for (int i = 0; i < history.Count; i++)
|
for (int i = 0; i < history.Count; i++)
|
||||||
@@ -342,6 +349,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
{
|
{
|
||||||
toolcallBuffer.Clear();
|
toolcallBuffer.Clear();
|
||||||
toolResultBuffer.Clear();
|
toolResultBuffer.Clear();
|
||||||
|
traceNoteBuffer.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.role == "toolcall")
|
if (entry.role == "toolcall")
|
||||||
@@ -362,6 +370,15 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry.role == "trace")
|
||||||
|
{
|
||||||
|
if (traceEnabled)
|
||||||
|
{
|
||||||
|
traceNoteBuffer.Add(entry.message ?? "");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
string messageText = entry.role == "assistant"
|
string messageText = entry.role == "assistant"
|
||||||
? ParseResponseForDisplay(entry.message)
|
? ParseResponseForDisplay(entry.message)
|
||||||
: AIIntelligenceCore.StripContextInfo(entry.message);
|
: AIIntelligenceCore.StripContextInfo(entry.message);
|
||||||
@@ -371,12 +388,12 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
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)
|
if (entry.role == "assistant" && traceEnabled && toolcallBuffer.Count > 0)
|
||||||
{
|
{
|
||||||
var traceLines = BuildTraceLines(toolcallBuffer, toolResultBuffer);
|
var traceLines = BuildTraceLines(toolcallBuffer, toolResultBuffer, traceNoteBuffer);
|
||||||
if (traceLines.Count > 0)
|
if (traceLines.Count > 0)
|
||||||
{
|
{
|
||||||
int traceKey = i;
|
int traceKey = i;
|
||||||
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved;
|
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved;
|
||||||
string header = BuildReactTraceHeader();
|
string header = GetTraceHeader(traceKey, false);
|
||||||
|
|
||||||
Text.Font = GameFont.Tiny;
|
Text.Font = GameFont.Tiny;
|
||||||
float tracePadding = 8f;
|
float tracePadding = 8f;
|
||||||
@@ -415,13 +432,18 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
|
|
||||||
toolcallBuffer.Clear();
|
toolcallBuffer.Clear();
|
||||||
toolResultBuffer.Clear();
|
toolResultBuffer.Clear();
|
||||||
|
traceNoteBuffer.Clear();
|
||||||
}
|
}
|
||||||
else if (entry.role == "assistant" && traceEnabled && toolcallBuffer.Count == 0)
|
else if (entry.role == "assistant" && traceEnabled && toolcallBuffer.Count == 0)
|
||||||
{
|
{
|
||||||
var traceLines = new List<string> { "无工具调用,直接回复" };
|
var traceLines = BuildTraceLines(toolcallBuffer, toolResultBuffer, traceNoteBuffer);
|
||||||
|
if (traceLines.Count == 0)
|
||||||
|
{
|
||||||
|
traceLines.Add("无工具调用,直接回复");
|
||||||
|
}
|
||||||
int traceKey = i;
|
int traceKey = i;
|
||||||
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved;
|
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved;
|
||||||
string header = BuildReactTraceHeader();
|
string header = GetTraceHeader(traceKey, false);
|
||||||
|
|
||||||
Text.Font = GameFont.Tiny;
|
Text.Font = GameFont.Tiny;
|
||||||
float tracePadding = 8f;
|
float tracePadding = 8f;
|
||||||
@@ -456,6 +478,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
traceHeaderHeight = headerHeight
|
traceHeaderHeight = headerHeight
|
||||||
});
|
});
|
||||||
curY += traceHeight + 10f;
|
curY += traceHeight + 10f;
|
||||||
|
traceNoteBuffer.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;
|
||||||
|
|
||||||
@@ -491,9 +514,59 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
float innerPadding = 5f;
|
float innerPadding = 5f;
|
||||||
float contentWidth = rect.width - 16f - innerPadding * 2;
|
float contentWidth = rect.width - 16f - innerPadding * 2;
|
||||||
UpdateCacheIfNeeded(rect.width - 16f);
|
UpdateCacheIfNeeded(rect.width - 16f);
|
||||||
|
bool traceEnabled = WulaFallenEmpireMod.settings?.showReactTraceInUI == true;
|
||||||
|
CachedMessage liveTraceEntry = null;
|
||||||
|
float liveTraceHeight = 0f;
|
||||||
|
if (_isThinking && traceEnabled)
|
||||||
|
{
|
||||||
|
var liveLines = BuildLiveTraceLines();
|
||||||
|
if (liveLines.Count == 0)
|
||||||
|
{
|
||||||
|
liveLines.Add("思考中…");
|
||||||
|
}
|
||||||
|
int traceKey = -1;
|
||||||
|
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) ? saved : true;
|
||||||
|
string header = GetTraceHeader(traceKey, true);
|
||||||
|
|
||||||
|
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 liveLines)
|
||||||
|
{
|
||||||
|
linesHeight += Text.CalcHeight(line, lineWidth) + 2f;
|
||||||
|
}
|
||||||
|
linesHeight += 8f;
|
||||||
|
}
|
||||||
|
float traceHeight = headerHeight + linesHeight;
|
||||||
|
liveTraceHeight = traceHeight + 10f;
|
||||||
|
liveTraceEntry = new CachedMessage
|
||||||
|
{
|
||||||
|
role = "trace",
|
||||||
|
message = "",
|
||||||
|
displayText = "",
|
||||||
|
height = traceHeight,
|
||||||
|
yOffset = 0f,
|
||||||
|
font = GameFont.Tiny,
|
||||||
|
isTrace = true,
|
||||||
|
traceKey = traceKey,
|
||||||
|
traceHeader = header,
|
||||||
|
traceLines = liveLines,
|
||||||
|
traceExpanded = expanded,
|
||||||
|
traceHeaderHeight = headerHeight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
float totalHeight = _cachedTotalHeight;
|
float totalHeight = _cachedTotalHeight;
|
||||||
if (_isThinking) totalHeight += 40f;
|
if (_isThinking)
|
||||||
|
{
|
||||||
|
totalHeight += liveTraceEntry != null ? liveTraceHeight : 40f;
|
||||||
|
}
|
||||||
|
|
||||||
Rect viewRect = new Rect(0f, 0f, rect.width - 16f, totalHeight);
|
Rect viewRect = new Rect(0f, 0f, rect.width - 16f, totalHeight);
|
||||||
if (_scrollToBottom)
|
if (_scrollToBottom)
|
||||||
@@ -541,7 +614,15 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
if (_isThinking)
|
if (_isThinking)
|
||||||
{
|
{
|
||||||
float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 0f;
|
float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 0f;
|
||||||
if (thinkingY + 40f >= viewTop && thinkingY <= viewBottom)
|
if (liveTraceEntry != null)
|
||||||
|
{
|
||||||
|
if (thinkingY + liveTraceEntry.height >= viewTop && thinkingY <= viewBottom)
|
||||||
|
{
|
||||||
|
Rect traceRect = new Rect(innerPadding, thinkingY, contentWidth, liveTraceEntry.height);
|
||||||
|
DrawReactTracePanel(traceRect, liveTraceEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (thinkingY + 40f >= viewTop && thinkingY <= viewBottom)
|
||||||
{
|
{
|
||||||
DrawThinkingIndicator(new Rect(innerPadding, thinkingY, contentWidth, 35f));
|
DrawThinkingIndicator(new Rect(innerPadding, thinkingY, contentWidth, 35f));
|
||||||
}
|
}
|
||||||
@@ -574,19 +655,19 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
return text.Split(new[] { "OPTIONS:" }, StringSplitOptions.None)[0].Trim();
|
return text.Split(new[] { "OPTIONS:" }, StringSplitOptions.None)[0].Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> BuildTraceLines(List<string> toolcallBuffer, List<string> toolResultBuffer)
|
private List<string> BuildTraceLines(List<string> toolcallBuffer, List<string> toolResultBuffer, List<string> traceNotes)
|
||||||
{
|
{
|
||||||
var lines = new List<string>();
|
var lines = new List<string>();
|
||||||
if (toolcallBuffer == null || toolcallBuffer.Count == 0) return lines;
|
bool hasToolCalls = toolcallBuffer != null && toolcallBuffer.Count > 0;
|
||||||
|
|
||||||
int stepIndex = 0;
|
int stepIndex = 0;
|
||||||
int maxSteps = Math.Max(toolcallBuffer.Count, toolResultBuffer.Count);
|
int maxSteps = Math.Max(toolcallBuffer?.Count ?? 0, toolResultBuffer?.Count ?? 0);
|
||||||
for (int i = 0; i < maxSteps; i++)
|
for (int i = 0; i < maxSteps; i++)
|
||||||
{
|
{
|
||||||
bool anyStepContent = false;
|
bool anyStepContent = false;
|
||||||
stepIndex++;
|
stepIndex++;
|
||||||
|
|
||||||
if (i < toolcallBuffer.Count &&
|
if (hasToolCalls && i < toolcallBuffer.Count &&
|
||||||
JsonToolCallParser.TryParseToolCallsFromText(toolcallBuffer[i], out var calls, out _))
|
JsonToolCallParser.TryParseToolCallsFromText(toolcallBuffer[i], out var calls, out _))
|
||||||
{
|
{
|
||||||
foreach (var call in calls)
|
foreach (var call in calls)
|
||||||
@@ -601,7 +682,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < toolResultBuffer.Count)
|
if (toolResultBuffer != null && i < toolResultBuffer.Count)
|
||||||
{
|
{
|
||||||
foreach (string resultLine in ExtractToolResultLines(toolResultBuffer[i], 4))
|
foreach (string resultLine in ExtractToolResultLines(toolResultBuffer[i], 4))
|
||||||
{
|
{
|
||||||
@@ -616,6 +697,16 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (traceNotes != null && traceNotes.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (string note in traceNotes)
|
||||||
|
{
|
||||||
|
string trimmed = (note ?? "").Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(trimmed)) continue;
|
||||||
|
lines.Add($"模型 · {TrimForDisplay(trimmed, 220)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,10 +741,67 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
return text.Substring(0, maxChars) + "...";
|
return text.Substring(0, maxChars) + "...";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string BuildReactTraceHeader()
|
private List<string> BuildLiveTraceLines()
|
||||||
{
|
{
|
||||||
string state = _isThinking ? "思考中" : "已思考";
|
if (_core == null) return new List<string>();
|
||||||
float elapsed = _isThinking
|
var history = _core.GetHistorySnapshot();
|
||||||
|
if (history == null || history.Count == 0) return new List<string>();
|
||||||
|
|
||||||
|
int lastUserIndex = -1;
|
||||||
|
for (int i = history.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (history[i].role == "user")
|
||||||
|
{
|
||||||
|
lastUserIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastUserIndex == -1) return new List<string>();
|
||||||
|
|
||||||
|
var toolcallBuffer = new List<string>();
|
||||||
|
var toolResultBuffer = new List<string>();
|
||||||
|
var traceNoteBuffer = new List<string>();
|
||||||
|
for (int i = lastUserIndex + 1; i < history.Count; i++)
|
||||||
|
{
|
||||||
|
var entry = history[i];
|
||||||
|
if (entry.role == "toolcall")
|
||||||
|
{
|
||||||
|
toolcallBuffer.Add(entry.message ?? "");
|
||||||
|
}
|
||||||
|
else if (entry.role == "tool")
|
||||||
|
{
|
||||||
|
toolResultBuffer.Add(entry.message ?? "");
|
||||||
|
}
|
||||||
|
else if (entry.role == "trace")
|
||||||
|
{
|
||||||
|
traceNoteBuffer.Add(entry.message ?? "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BuildTraceLines(toolcallBuffer, toolResultBuffer, traceNoteBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTraceHeader(int traceKey, bool isLive)
|
||||||
|
{
|
||||||
|
if (isLive)
|
||||||
|
{
|
||||||
|
return BuildReactTraceHeader(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_traceHeaderByAssistantIndex.TryGetValue(traceKey, out string header))
|
||||||
|
{
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
header = BuildReactTraceHeader(false);
|
||||||
|
_traceHeaderByAssistantIndex[traceKey] = header;
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildReactTraceHeader(bool isLive)
|
||||||
|
{
|
||||||
|
string state = isLive ? "思考中" : "已思考";
|
||||||
|
float elapsed = isLive
|
||||||
? 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";
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
private List<CachedMessage> _cachedMessages = new List<CachedMessage>();
|
private List<CachedMessage> _cachedMessages = new List<CachedMessage>();
|
||||||
private float _cachedTotalHeight = 0f;
|
private float _cachedTotalHeight = 0f;
|
||||||
private readonly Dictionary<int, bool> _traceExpandedByAssistantIndex = new Dictionary<int, bool>();
|
private readonly Dictionary<int, bool> _traceExpandedByAssistantIndex = new Dictionary<int, bool>();
|
||||||
|
private readonly Dictionary<int, string> _traceHeaderByAssistantIndex = new Dictionary<int, string>();
|
||||||
|
|
||||||
private class CachedMessage
|
private class CachedMessage
|
||||||
{
|
{
|
||||||
@@ -377,6 +378,11 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
_lastUsedWidth = width;
|
_lastUsedWidth = width;
|
||||||
|
if (_lastHistoryCount >= 0 && history.Count < _lastHistoryCount)
|
||||||
|
{
|
||||||
|
_traceExpandedByAssistantIndex.Clear();
|
||||||
|
_traceHeaderByAssistantIndex.Clear();
|
||||||
|
}
|
||||||
_lastHistoryCount = history.Count;
|
_lastHistoryCount = history.Count;
|
||||||
_cachedMessages.Clear();
|
_cachedMessages.Clear();
|
||||||
_cachedTotalHeight = 0f;
|
_cachedTotalHeight = 0f;
|
||||||
@@ -384,6 +390,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
float curY = 10f;
|
float curY = 10f;
|
||||||
var toolcallBuffer = new List<string>();
|
var toolcallBuffer = new List<string>();
|
||||||
var toolResultBuffer = new List<string>();
|
var toolResultBuffer = new List<string>();
|
||||||
|
var traceNoteBuffer = new List<string>();
|
||||||
bool traceEnabled = WulaFallenEmpireMod.settings?.showReactTraceInUI == true;
|
bool traceEnabled = WulaFallenEmpireMod.settings?.showReactTraceInUI == true;
|
||||||
|
|
||||||
for (int i = 0; i < history.Count; i++)
|
for (int i = 0; i < history.Count; i++)
|
||||||
@@ -394,6 +401,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
{
|
{
|
||||||
toolcallBuffer.Clear();
|
toolcallBuffer.Clear();
|
||||||
toolResultBuffer.Clear();
|
toolResultBuffer.Clear();
|
||||||
|
traceNoteBuffer.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.role == "toolcall")
|
if (msg.role == "toolcall")
|
||||||
@@ -414,6 +422,15 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.role == "trace")
|
||||||
|
{
|
||||||
|
if (traceEnabled)
|
||||||
|
{
|
||||||
|
traceNoteBuffer.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
|
||||||
@@ -431,12 +448,12 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
|
|
||||||
if (msg.role == "assistant" && traceEnabled && toolcallBuffer.Count > 0)
|
if (msg.role == "assistant" && traceEnabled && toolcallBuffer.Count > 0)
|
||||||
{
|
{
|
||||||
var traceLines = BuildTraceLines(toolcallBuffer, toolResultBuffer);
|
var traceLines = BuildTraceLines(toolcallBuffer, toolResultBuffer, traceNoteBuffer);
|
||||||
if (traceLines.Count > 0)
|
if (traceLines.Count > 0)
|
||||||
{
|
{
|
||||||
int traceKey = i;
|
int traceKey = i;
|
||||||
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved;
|
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved;
|
||||||
string header = BuildReactTraceHeader();
|
string header = GetTraceHeader(traceKey, false);
|
||||||
|
|
||||||
Text.Font = GameFont.Tiny;
|
Text.Font = GameFont.Tiny;
|
||||||
float padding = 8f;
|
float padding = 8f;
|
||||||
@@ -474,13 +491,18 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
|
|
||||||
toolcallBuffer.Clear();
|
toolcallBuffer.Clear();
|
||||||
toolResultBuffer.Clear();
|
toolResultBuffer.Clear();
|
||||||
|
traceNoteBuffer.Clear();
|
||||||
}
|
}
|
||||||
else if (msg.role == "assistant" && traceEnabled && toolcallBuffer.Count == 0)
|
else if (msg.role == "assistant" && traceEnabled && toolcallBuffer.Count == 0)
|
||||||
{
|
{
|
||||||
var traceLines = new List<string> { "无工具调用,直接回复" };
|
var traceLines = BuildTraceLines(toolcallBuffer, toolResultBuffer, traceNoteBuffer);
|
||||||
|
if (traceLines.Count == 0)
|
||||||
|
{
|
||||||
|
traceLines.Add("无工具调用,直接回复");
|
||||||
|
}
|
||||||
int traceKey = i;
|
int traceKey = i;
|
||||||
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved;
|
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved;
|
||||||
string header = BuildReactTraceHeader();
|
string header = GetTraceHeader(traceKey, false);
|
||||||
|
|
||||||
Text.Font = GameFont.Tiny;
|
Text.Font = GameFont.Tiny;
|
||||||
float tracePadding = 8f;
|
float tracePadding = 8f;
|
||||||
@@ -514,6 +536,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
traceHeaderHeight = headerHeight
|
traceHeaderHeight = headerHeight
|
||||||
});
|
});
|
||||||
curY += traceHeight + reducedSpacing;
|
curY += traceHeight + reducedSpacing;
|
||||||
|
traceNoteBuffer.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(displayText)) continue;
|
if (string.IsNullOrWhiteSpace(displayText)) continue;
|
||||||
@@ -540,10 +563,57 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
float width = rect.width - 26f; // Scrollbar space
|
float width = rect.width - 26f; // Scrollbar space
|
||||||
UpdateCacheIfNeeded(width);
|
UpdateCacheIfNeeded(width);
|
||||||
|
|
||||||
|
bool traceEnabled = WulaFallenEmpireMod.settings?.showReactTraceInUI == true;
|
||||||
|
CachedMessage liveTraceEntry = null;
|
||||||
|
float liveTraceHeight = 0f;
|
||||||
|
if (_core != null && _core.IsThinking && traceEnabled)
|
||||||
|
{
|
||||||
|
var liveLines = BuildLiveTraceLines();
|
||||||
|
if (liveLines.Count == 0)
|
||||||
|
{
|
||||||
|
liveLines.Add("思考中…");
|
||||||
|
}
|
||||||
|
int traceKey = -1;
|
||||||
|
bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) ? saved : true;
|
||||||
|
string header = GetTraceHeader(traceKey, true);
|
||||||
|
|
||||||
|
Text.Font = GameFont.Tiny;
|
||||||
|
float tracePadding = 8f;
|
||||||
|
float headerWidth = Mathf.Max(10f, width - tracePadding * 2f);
|
||||||
|
string headerLine = $"{(expanded ? "v" : ">")} {header}";
|
||||||
|
float headerHeight = Text.CalcHeight(headerLine, headerWidth) + 10f;
|
||||||
|
float linesHeight = 0f;
|
||||||
|
if (expanded)
|
||||||
|
{
|
||||||
|
float lineWidth = Mathf.Max(10f, width - tracePadding * 2f);
|
||||||
|
foreach (string line in liveLines)
|
||||||
|
{
|
||||||
|
linesHeight += Text.CalcHeight(line, lineWidth) + 2f;
|
||||||
|
}
|
||||||
|
linesHeight += 8f;
|
||||||
|
}
|
||||||
|
float traceHeight = headerHeight + linesHeight;
|
||||||
|
liveTraceHeight = traceHeight + 8f;
|
||||||
|
liveTraceEntry = new CachedMessage
|
||||||
|
{
|
||||||
|
role = "trace",
|
||||||
|
message = "",
|
||||||
|
displayText = "",
|
||||||
|
height = traceHeight,
|
||||||
|
yOffset = 0f,
|
||||||
|
isTrace = true,
|
||||||
|
traceKey = traceKey,
|
||||||
|
traceHeader = header,
|
||||||
|
traceLines = liveLines,
|
||||||
|
traceExpanded = expanded,
|
||||||
|
traceHeaderHeight = headerHeight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
float totalContentHeight = _cachedTotalHeight;
|
float totalContentHeight = _cachedTotalHeight;
|
||||||
if (_core != null && _core.IsThinking)
|
if (_core != null && _core.IsThinking)
|
||||||
{
|
{
|
||||||
totalContentHeight += 40f;
|
totalContentHeight += liveTraceEntry != null ? liveTraceHeight : 40f;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect viewRect = new Rect(0, 0, width, totalContentHeight);
|
Rect viewRect = new Rect(0, 0, width, totalContentHeight);
|
||||||
@@ -593,12 +663,23 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
if (_core != null && _core.IsThinking)
|
if (_core != null && _core.IsThinking)
|
||||||
{
|
{
|
||||||
float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 10f;
|
float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 10f;
|
||||||
|
if (liveTraceEntry != null)
|
||||||
|
{
|
||||||
|
if (thinkingY + liveTraceEntry.height >= viewTop && thinkingY <= viewBottom)
|
||||||
|
{
|
||||||
|
Rect traceRect = new Rect(0, thinkingY, width, liveTraceEntry.height);
|
||||||
|
DrawReactTracePanel(traceRect, liveTraceEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Rect thinkingRect = new Rect(0, thinkingY, width, 30f);
|
Rect thinkingRect = new Rect(0, thinkingY, width, 30f);
|
||||||
if (thinkingY + 30f >= viewTop && thinkingY <= viewBottom)
|
if (thinkingY + 30f >= viewTop && thinkingY <= viewBottom)
|
||||||
{
|
{
|
||||||
DrawThinkingIndicator(thinkingRect);
|
DrawThinkingIndicator(thinkingRect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widgets.EndScrollView();
|
Widgets.EndScrollView();
|
||||||
}
|
}
|
||||||
@@ -616,19 +697,19 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
return text.Remove(index, fragment.Length).Trim();
|
return text.Remove(index, fragment.Length).Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> BuildTraceLines(List<string> toolcallBuffer, List<string> toolResultBuffer)
|
private List<string> BuildTraceLines(List<string> toolcallBuffer, List<string> toolResultBuffer, List<string> traceNotes)
|
||||||
{
|
{
|
||||||
var lines = new List<string>();
|
var lines = new List<string>();
|
||||||
if (toolcallBuffer == null || toolcallBuffer.Count == 0) return lines;
|
bool hasToolCalls = toolcallBuffer != null && toolcallBuffer.Count > 0;
|
||||||
|
|
||||||
int stepIndex = 0;
|
int stepIndex = 0;
|
||||||
int maxSteps = Math.Max(toolcallBuffer.Count, toolResultBuffer.Count);
|
int maxSteps = Math.Max(toolcallBuffer?.Count ?? 0, toolResultBuffer?.Count ?? 0);
|
||||||
for (int i = 0; i < maxSteps; i++)
|
for (int i = 0; i < maxSteps; i++)
|
||||||
{
|
{
|
||||||
bool anyStepContent = false;
|
bool anyStepContent = false;
|
||||||
stepIndex++;
|
stepIndex++;
|
||||||
|
|
||||||
if (i < toolcallBuffer.Count &&
|
if (hasToolCalls && i < toolcallBuffer.Count &&
|
||||||
JsonToolCallParser.TryParseToolCallsFromText(toolcallBuffer[i], out var calls, out _))
|
JsonToolCallParser.TryParseToolCallsFromText(toolcallBuffer[i], out var calls, out _))
|
||||||
{
|
{
|
||||||
foreach (var call in calls)
|
foreach (var call in calls)
|
||||||
@@ -643,7 +724,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < toolResultBuffer.Count)
|
if (toolResultBuffer != null && i < toolResultBuffer.Count)
|
||||||
{
|
{
|
||||||
foreach (string resultLine in ExtractToolResultLines(toolResultBuffer[i], 4))
|
foreach (string resultLine in ExtractToolResultLines(toolResultBuffer[i], 4))
|
||||||
{
|
{
|
||||||
@@ -658,6 +739,16 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (traceNotes != null && traceNotes.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (string note in traceNotes)
|
||||||
|
{
|
||||||
|
string trimmed = (note ?? "").Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(trimmed)) continue;
|
||||||
|
lines.Add($"模型 · {TrimForDisplay(trimmed, 220)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,11 +783,68 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
return text.Substring(0, maxChars) + "...";
|
return text.Substring(0, maxChars) + "...";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string BuildReactTraceHeader()
|
private List<string> BuildLiveTraceLines()
|
||||||
{
|
{
|
||||||
string state = _core != null && _core.IsThinking ? "思考中" : "已思考";
|
if (_core == null) return new List<string>();
|
||||||
|
var history = _core.GetHistorySnapshot();
|
||||||
|
if (history == null || history.Count == 0) return new List<string>();
|
||||||
|
|
||||||
|
int lastUserIndex = -1;
|
||||||
|
for (int i = history.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (history[i].role == "user")
|
||||||
|
{
|
||||||
|
lastUserIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastUserIndex == -1) return new List<string>();
|
||||||
|
|
||||||
|
var toolcallBuffer = new List<string>();
|
||||||
|
var toolResultBuffer = new List<string>();
|
||||||
|
var traceNoteBuffer = new List<string>();
|
||||||
|
for (int i = lastUserIndex + 1; i < history.Count; i++)
|
||||||
|
{
|
||||||
|
var entry = history[i];
|
||||||
|
if (entry.role == "toolcall")
|
||||||
|
{
|
||||||
|
toolcallBuffer.Add(entry.message ?? "");
|
||||||
|
}
|
||||||
|
else if (entry.role == "tool")
|
||||||
|
{
|
||||||
|
toolResultBuffer.Add(entry.message ?? "");
|
||||||
|
}
|
||||||
|
else if (entry.role == "trace")
|
||||||
|
{
|
||||||
|
traceNoteBuffer.Add(entry.message ?? "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BuildTraceLines(toolcallBuffer, toolResultBuffer, traceNoteBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTraceHeader(int traceKey, bool isLive)
|
||||||
|
{
|
||||||
|
if (isLive)
|
||||||
|
{
|
||||||
|
return BuildReactTraceHeader(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_traceHeaderByAssistantIndex.TryGetValue(traceKey, out string header))
|
||||||
|
{
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
header = BuildReactTraceHeader(false);
|
||||||
|
_traceHeaderByAssistantIndex[traceKey] = header;
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildReactTraceHeader(bool isLive)
|
||||||
|
{
|
||||||
|
string state = isLive ? "思考中" : "已思考";
|
||||||
float startTime = _core?.ThinkingStartTime ?? 0f;
|
float startTime = _core?.ThinkingStartTime ?? 0f;
|
||||||
float elapsed = _core != null && _core.IsThinking
|
float elapsed = isLive && _core != null
|
||||||
? Mathf.Max(0f, Time.realtimeSinceStartup - startTime)
|
? Mathf.Max(0f, Time.realtimeSinceStartup - startTime)
|
||||||
: _core?.LastThinkingDuration ?? 0f;
|
: _core?.LastThinkingDuration ?? 0f;
|
||||||
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";
|
||||||
|
|||||||
@@ -99,15 +99,15 @@ namespace WulaFallenEmpire
|
|||||||
|
|
||||||
listingStandard.GapLine();
|
listingStandard.GapLine();
|
||||||
listingStandard.Label("<color=cyan>ReAct Loop Settings</color>");
|
listingStandard.Label("<color=cyan>ReAct Loop Settings</color>");
|
||||||
listingStandard.Label("Default Steps:");
|
listingStandard.Label("Default Steps (min 1):");
|
||||||
Rect stepsRect = listingStandard.GetRect(Text.LineHeight);
|
Rect stepsRect = listingStandard.GetRect(Text.LineHeight);
|
||||||
Widgets.TextFieldNumeric(stepsRect, ref settings.reactMaxSteps, ref _reactMaxStepsBuffer, 1, int.MaxValue);
|
Widgets.TextFieldNumeric(stepsRect, ref settings.reactMaxSteps, ref _reactMaxStepsBuffer, 1, int.MaxValue);
|
||||||
|
|
||||||
listingStandard.Label("Max Steps Limit (step_budget upper bound):");
|
listingStandard.Label("Max Steps Limit (step_budget upper bound, min 1):");
|
||||||
Rect stepsMaxRect = listingStandard.GetRect(Text.LineHeight);
|
Rect stepsMaxRect = listingStandard.GetRect(Text.LineHeight);
|
||||||
Widgets.TextFieldNumeric(stepsMaxRect, ref settings.reactMaxStepsMax, ref _reactMaxStepsMaxBuffer, 1, int.MaxValue);
|
Widgets.TextFieldNumeric(stepsMaxRect, ref settings.reactMaxStepsMax, ref _reactMaxStepsMaxBuffer, 1, int.MaxValue);
|
||||||
|
|
||||||
listingStandard.Label("Max Seconds (2-60):");
|
listingStandard.Label("Max Seconds (min 2):");
|
||||||
Rect secondsRect = listingStandard.GetRect(Text.LineHeight);
|
Rect secondsRect = listingStandard.GetRect(Text.LineHeight);
|
||||||
Widgets.TextFieldNumeric(secondsRect, ref settings.reactMaxSeconds, ref _reactMaxSecondsBuffer, 10f, 600f);
|
Widgets.TextFieldNumeric(secondsRect, ref settings.reactMaxSeconds, ref _reactMaxSecondsBuffer, 10f, 600f);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user