Merge pull request #1581 from pikasTech/fix/1567-read-side-projection
fix: Workbench 读侧 final text 推理清零
This commit is contained in:
@@ -3,7 +3,7 @@ import test from "node:test";
|
|||||||
|
|
||||||
import type { AgentRunProvenance, ChatMessage, TraceEvent } from "../src/types/index.ts";
|
import type { AgentRunProvenance, ChatMessage, TraceEvent } from "../src/types/index.ts";
|
||||||
import { canCancelMessage, canRetryMessage, messageTraceId, renderSafeMarkdown, traceEventBody, traceEventLabel, traceIdentityText, visibleTraceEvents } from "../src/components/workbench/message-rendering.ts";
|
import { canCancelMessage, canRetryMessage, messageTraceId, renderSafeMarkdown, traceEventBody, traceEventLabel, traceIdentityText, visibleTraceEvents } from "../src/components/workbench/message-rendering.ts";
|
||||||
import { assistantTextFromTraceEvents, mergeRunnerTrace } from "../src/composables/useTraceSubscription.ts";
|
import { mergeRunnerTrace } from "../src/composables/useTraceSubscription.ts";
|
||||||
import { traceDisplayRows, traceNoiseEventCount } from "../../../tools/src/hwlab-cli/trace-renderer.ts";
|
import { traceDisplayRows, traceNoiseEventCount } from "../../../tools/src/hwlab-cli/trace-renderer.ts";
|
||||||
|
|
||||||
test("R1 markdown rendering keeps structure and strips unsafe HTML", () => {
|
test("R1 markdown rendering keeps structure and strips unsafe HTML", () => {
|
||||||
@@ -103,16 +103,6 @@ test("R1 trace API snapshots stay authoritative over compact result traces", ()
|
|||||||
assert.equal(merged.events?.[1]?.message, "OK");
|
assert.equal(merged.events?.[1]?.message, "OK");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("R1 trace event assistant text prefers authoritative final assistant message", () => {
|
|
||||||
const events: TraceEvent[] = [
|
|
||||||
{ seq: 1, label: "agentrun:backend:resource-bundle-materialized", message: "bundle ready" },
|
|
||||||
{ seq: 2, label: "agentrun:assistant:message", type: "assistant", status: "running", replyAuthority: false, final: false, message: "我先检查当前状态。" },
|
|
||||||
{ seq: 3, label: "agentrun:assistant:message", type: "assistant", status: "completed", terminal: true, message: "OK" }
|
|
||||||
];
|
|
||||||
|
|
||||||
assert.equal(assistantTextFromTraceEvents(events), "OK");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("R1 status summary keeps the restored 23-row floor", () => {
|
test("R1 status summary keeps the restored 23-row floor", () => {
|
||||||
const message = agentMessage({
|
const message = agentMessage({
|
||||||
status: "failed",
|
status: "failed",
|
||||||
|
|||||||
@@ -91,9 +91,7 @@ function normalizeWorkbenchTurnResult(result: ApiResult<Record<string, unknown>>
|
|||||||
if (!result.ok || !result.data) return result as ApiResult<AgentChatResultResponse>;
|
if (!result.ok || !result.data) return result as ApiResult<AgentChatResultResponse>;
|
||||||
const turn = recordValue(result.data.turn) ?? result.data;
|
const turn = recordValue(result.data.turn) ?? result.data;
|
||||||
const trace = recordValue(turn.trace);
|
const trace = recordValue(turn.trace);
|
||||||
const envelopeStatus = textValue(result.data.status);
|
const status = normalizeStatus(textValue(turn.status)) ?? "unknown";
|
||||||
const status = normalizeStatus(textValue(turn.status) ?? (envelopeStatus && !["ok", "succeeded", "found"].includes(envelopeStatus) ? envelopeStatus : null)) ?? "unknown";
|
|
||||||
const terminal = turn.terminal === true || isTerminalStatus(status);
|
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
data: {
|
data: {
|
||||||
@@ -103,8 +101,8 @@ function normalizeWorkbenchTurnResult(result: ApiResult<Record<string, unknown>>
|
|||||||
lastProjectedSeq: Number.isFinite(Number(result.data.lastProjectedSeq)) ? Number(result.data.lastProjectedSeq) : undefined,
|
lastProjectedSeq: Number.isFinite(Number(result.data.lastProjectedSeq)) ? Number(result.data.lastProjectedSeq) : undefined,
|
||||||
traceId: textValue(turn.traceId) ?? traceId,
|
traceId: textValue(turn.traceId) ?? traceId,
|
||||||
status,
|
status,
|
||||||
running: !terminal && (turn.running === true || isActiveStatus(status)),
|
running: typeof turn.running === "boolean" ? turn.running : undefined,
|
||||||
terminal,
|
terminal: typeof turn.terminal === "boolean" ? turn.terminal : undefined,
|
||||||
sessionId: textValue(turn.sessionId) ?? undefined,
|
sessionId: textValue(turn.sessionId) ?? undefined,
|
||||||
threadId: textValue(turn.threadId) ?? undefined,
|
threadId: textValue(turn.threadId) ?? undefined,
|
||||||
agentRun: recordValue(turn.agentRun) ?? undefined,
|
agentRun: recordValue(turn.agentRun) ?? undefined,
|
||||||
@@ -117,30 +115,32 @@ function normalizeWorkbenchTraceResult(result: ApiResult<Record<string, unknown>
|
|||||||
if (!result.ok || !result.data) return result as ApiResult<AgentChatResultResponse>;
|
if (!result.ok || !result.data) return result as ApiResult<AgentChatResultResponse>;
|
||||||
const events = Array.isArray(result.data.events) ? result.data.events : [];
|
const events = Array.isArray(result.data.events) ? result.data.events : [];
|
||||||
const nextSeq = Number(result.data.nextSeq ?? result.data.nextSinceSeq);
|
const nextSeq = Number(result.data.nextSeq ?? result.data.nextSinceSeq);
|
||||||
const traceStatus = textValue(result.data.traceStatus, result.data.status) ?? "unknown";
|
const traceStatus = textValue(result.data.traceStatus) ?? undefined;
|
||||||
const eventCount = Number.isFinite(Number(result.data.eventCount)) ? Number(result.data.eventCount) : events.length;
|
const eventCount = Number.isFinite(Number(result.data.eventCount)) ? Number(result.data.eventCount) : events.length;
|
||||||
const normalizedNextSeq = Number.isFinite(nextSeq) ? Math.trunc(nextSeq) : null;
|
const normalizedNextSeq = Number.isFinite(nextSeq) ? Math.trunc(nextSeq) : null;
|
||||||
|
const hasMore = result.data.hasMore === true;
|
||||||
|
const fullTraceLoaded = typeof result.data.fullTraceLoaded === "boolean" ? result.data.fullTraceLoaded : undefined;
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
data: {
|
data: {
|
||||||
...result.data,
|
...result.data,
|
||||||
traceId: textValue(result.data.traceId) ?? traceId,
|
traceId: textValue(result.data.traceId) ?? traceId,
|
||||||
status: traceStatus,
|
status: undefined,
|
||||||
traceStatus,
|
traceStatus,
|
||||||
events,
|
events,
|
||||||
traceEvents: events,
|
traceEvents: events,
|
||||||
eventCount,
|
eventCount,
|
||||||
hasMore: result.data.hasMore === true,
|
hasMore,
|
||||||
fullTraceLoaded: result.data.hasMore !== true,
|
fullTraceLoaded,
|
||||||
nextSinceSeq: normalizedNextSeq,
|
nextSinceSeq: normalizedNextSeq,
|
||||||
range: recordValue(result.data.range) ?? undefined,
|
range: recordValue(result.data.range) ?? undefined,
|
||||||
runnerTrace: {
|
runnerTrace: {
|
||||||
traceId: textValue(result.data.traceId) ?? traceId,
|
traceId: textValue(result.data.traceId) ?? traceId,
|
||||||
status: traceStatus,
|
status: undefined,
|
||||||
events,
|
events,
|
||||||
eventCount,
|
eventCount,
|
||||||
hasMore: result.data.hasMore === true,
|
hasMore,
|
||||||
fullTraceLoaded: result.data.hasMore !== true,
|
fullTraceLoaded,
|
||||||
nextSinceSeq: normalizedNextSeq,
|
nextSinceSeq: normalizedNextSeq,
|
||||||
range: recordValue(result.data.range) ?? undefined,
|
range: recordValue(result.data.range) ?? undefined,
|
||||||
eventSource: "trace-api"
|
eventSource: "trace-api"
|
||||||
@@ -156,14 +156,6 @@ function normalizeStatus(value: unknown): string | null {
|
|||||||
return normalized === "cancelled" ? "canceled" : normalized;
|
return normalized === "cancelled" ? "canceled" : normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isActiveStatus(value: unknown): boolean {
|
|
||||||
return ["accepted", "active", "busy", "creating", "pending", "processing", "running"].includes(normalizeStatus(value) ?? "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTerminalStatus(value: unknown): boolean {
|
|
||||||
return ["blocked", "canceled", "completed", "failed", "stale", "thread-resume-failed", "timeout"].includes(normalizeStatus(value) ?? "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function recordValue(value: unknown): Record<string, unknown> | null {
|
function recordValue(value: unknown): Record<string, unknown> | null {
|
||||||
return value && typeof value === "object" ? value as Record<string, unknown> : null;
|
return value && typeof value === "object" ? value as Record<string, unknown> : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,12 +144,7 @@ export function mergeTraceResults(terminal: AgentChatResultResponse, trace: Trac
|
|||||||
error: trace.error ?? terminal.error
|
error: trace.error ?? terminal.error
|
||||||
};
|
};
|
||||||
const events = Array.isArray(trace.events) ? trace.events : [];
|
const events = Array.isArray(trace.events) ? trace.events : [];
|
||||||
const assistantText = bestAssistantTextCandidate([
|
const assistantText = firstNonEmptyString(finalResponseText(mergedTrace.finalResponse), terminal.assistantText);
|
||||||
finalResponseText(mergedTrace.finalResponse),
|
|
||||||
terminalAssistantTextFromTraceEvents(events),
|
|
||||||
firstNonEmptyResultText(terminal),
|
|
||||||
assistantTextFromTraceEvents(events)
|
|
||||||
]);
|
|
||||||
return {
|
return {
|
||||||
...terminal,
|
...terminal,
|
||||||
traceId: mergedTrace.traceId ?? terminal.traceId,
|
traceId: mergedTrace.traceId ?? terminal.traceId,
|
||||||
@@ -333,12 +328,6 @@ function sleep(ms: number): Promise<void> {
|
|||||||
return new Promise((resolve) => window.setTimeout(resolve, ms));
|
return new Promise((resolve) => window.setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
function firstNonEmptyResultText(result: AgentChatResultResponse): string | null {
|
|
||||||
const reply = result.reply;
|
|
||||||
const replyText = typeof reply === "string" ? reply : typeof reply?.content === "string" ? reply.content : null;
|
|
||||||
return firstNonEmptyString(result.assistantText, replyText);
|
|
||||||
}
|
|
||||||
|
|
||||||
function finalResponseText(value: unknown): string | null {
|
function finalResponseText(value: unknown): string | null {
|
||||||
if (typeof value === "string") return value;
|
if (typeof value === "string") return value;
|
||||||
if (value && typeof value === "object") {
|
if (value && typeof value === "object") {
|
||||||
@@ -347,41 +336,3 @@ function finalResponseText(value: unknown): string | null {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assistantTextFromTraceEvents(events: TraceEvent[]): string | null {
|
|
||||||
const terminal = terminalAssistantTextFromTraceEvents(events);
|
|
||||||
if (terminal) return terminal;
|
|
||||||
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
||||||
const event = events[index];
|
|
||||||
if (!event || !isAssistantTraceEvent(event)) continue;
|
|
||||||
const text = firstNonEmptyString(event.message, event.text, event.outputSummary);
|
|
||||||
if (text) return text;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function terminalAssistantTextFromTraceEvents(events: TraceEvent[]): string | null {
|
|
||||||
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
||||||
const event = events[index];
|
|
||||||
if (!event || !isTerminalAssistantTraceEvent(event)) continue;
|
|
||||||
const text = firstNonEmptyString(event.message, event.text, event.outputSummary);
|
|
||||||
if (text) return text;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function bestAssistantTextCandidate(candidates: Array<string | null | undefined>): string | null {
|
|
||||||
return candidates.find((candidate) => typeof candidate === "string" && candidate.trim())?.trim() ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAssistantTraceEvent(event: TraceEvent): boolean {
|
|
||||||
const label = String(event.label ?? "");
|
|
||||||
return label === "agentrun:assistant:message" || label === "assistant:message" || label === "assistant:completed" || event.type === "assistant" || event.type === "assistant_message";
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTerminalAssistantTraceEvent(event: TraceEvent): boolean {
|
|
||||||
const label = String(event.label ?? "");
|
|
||||||
if (label === "assistant:completed") return true;
|
|
||||||
if (label === "agentrun:assistant:message") return event.replyAuthority === true || event.final === true || event.terminal === true || String(event.status ?? "") === "completed";
|
|
||||||
return event.type === "assistant_message" && (event.status === "completed" || event.final === true || event.terminal === true);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -78,8 +78,9 @@ export function resolveComposerState(input: { messages: ChatMessage[]; sessions?
|
|||||||
const turn = activeTraceId ? input.turnStatusAuthority?.[activeTraceId] : null;
|
const turn = activeTraceId ? input.turnStatusAuthority?.[activeTraceId] : null;
|
||||||
const activeByRequest = Boolean(currentRequest && isActiveStatus(currentRequest.status ?? "running"));
|
const activeByRequest = Boolean(currentRequest && isActiveStatus(currentRequest.status ?? "running"));
|
||||||
const activeByMessage = latestMessage?.role === "agent" && isActiveStatus(latestMessage.status);
|
const activeByMessage = latestMessage?.role === "agent" && isActiveStatus(latestMessage.status);
|
||||||
const activeByStatus = turn?.running === true || isActiveStatus(turn?.status) || activeByRequest || activeByMessage;
|
const terminalByMessage = latestMessage?.role === "agent" && isTerminalStatus(latestMessage.status);
|
||||||
const terminal = turn?.terminal === true || isTerminalStatus(turn?.status);
|
const activeByStatus = !terminalByMessage && (turn?.running === true || isActiveStatus(turn?.status) || activeByRequest || activeByMessage);
|
||||||
|
const terminal = terminalByMessage || turn?.terminal === true || isTerminalStatus(turn?.status);
|
||||||
const active = activeSession(input.sessions ?? [], sessionId);
|
const active = activeSession(input.sessions ?? [], sessionId);
|
||||||
const effectiveSessionId = firstNonEmptyString(turn?.sessionId, currentRequest?.sessionId, active?.sessionId, sessionId);
|
const effectiveSessionId = firstNonEmptyString(turn?.sessionId, currentRequest?.sessionId, active?.sessionId, sessionId);
|
||||||
const threadId = firstNonEmptyString(turn?.threadId, currentRequest?.threadId, active?.threadId);
|
const threadId = firstNonEmptyString(turn?.threadId, currentRequest?.threadId, active?.threadId);
|
||||||
@@ -98,6 +99,7 @@ export function resolveCancelableAgentMessage(input: { messages: ChatMessage[];
|
|||||||
if (message.role !== "agent") continue;
|
if (message.role !== "agent") continue;
|
||||||
if (firstNonEmptyString(message.traceId, message.runnerTrace?.traceId) !== targetTraceId) continue;
|
if (firstNonEmptyString(message.traceId, message.runnerTrace?.traceId) !== targetTraceId) continue;
|
||||||
if (!messageBelongsToCancelTarget(message, input)) continue;
|
if (!messageBelongsToCancelTarget(message, input)) continue;
|
||||||
|
if (isTerminalStatus(message.status)) continue;
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { computed, nextTick, ref } from "vue";
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { api } from "@/api";
|
import { api } from "@/api";
|
||||||
import { connectWorkbenchEvents, type WorkbenchEventStream, type WorkbenchRealtimeEvent } from "@/api/workbench-events";
|
import { connectWorkbenchEvents, type WorkbenchEventStream, type WorkbenchRealtimeEvent } from "@/api/workbench-events";
|
||||||
import { assistantTextFromTraceEvents, mergeRunnerTrace, snapshotToRunnerTrace, type TraceSnapshot } from "@/composables/useTraceSubscription";
|
import { mergeRunnerTrace, snapshotToRunnerTrace, type TraceSnapshot } from "@/composables/useTraceSubscription";
|
||||||
import type { AgentChatResponse, AgentChatResultResponse, AgentRunProvenance, ApiResult, ChatMessage, LiveSurface, ProviderProfile, TraceEvent, WorkbenchSessionRecord } from "@/types";
|
import type { AgentChatResponse, AgentChatResultResponse, AgentRunProvenance, ApiResult, ChatMessage, LiveSurface, ProviderProfile, TraceEvent, WorkbenchSessionRecord } from "@/types";
|
||||||
import { firstNonEmptyString, nextProtocolId, normalizeWorkbenchSessionId, normalizeWorkbenchSessionRouteId } from "@/utils";
|
import { firstNonEmptyString, nextProtocolId, normalizeWorkbenchSessionId, normalizeWorkbenchSessionRouteId } from "@/utils";
|
||||||
import { failWorkbenchSessionSwitch, failWorkbenchSubmitJourney, finishWorkbenchSessionSwitchFullLoad, markWorkbenchSubmitApiAccepted, markWorkbenchTraceEventsReceived, markWorkbenchTraceProjected, startWorkbenchSessionSwitch, startWorkbenchSubmitJourney } from "@/utils/workbench-performance";
|
import { failWorkbenchSessionSwitch, failWorkbenchSubmitJourney, finishWorkbenchSessionSwitchFullLoad, markWorkbenchSubmitApiAccepted, markWorkbenchTraceEventsReceived, markWorkbenchTraceProjected, startWorkbenchSessionSwitch, startWorkbenchSubmitJourney } from "@/utils/workbench-performance";
|
||||||
@@ -356,28 +356,23 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function syncTurnStatusToMessage(traceId: string, result: AgentChatResultResponse | TraceSnapshot): void {
|
function syncTurnStatusToMessage(traceId: string, result: AgentChatResultResponse | TraceSnapshot): void {
|
||||||
const status = statusFromResult(result.status);
|
const resultStatus = statusFromResult(result.status);
|
||||||
const terminal = (result as AgentChatResultResponse).terminal === true || isTerminalMessageStatus(status);
|
|
||||||
const authoritySessionId = traceResultSessionId(result);
|
const authoritySessionId = traceResultSessionId(result);
|
||||||
updateActiveMessages((source) => source.map((message) => {
|
updateActiveMessages((source) => source.map((message) => {
|
||||||
if (!shouldApplyTraceToMessage(message, traceId, authoritySessionId)) return message;
|
if (!shouldApplyTraceToMessage(message, traceId, authoritySessionId)) return message;
|
||||||
|
const status = isTerminalMessageStatus(message.status) ? message.status : resultStatus;
|
||||||
|
const terminal = isTerminalMessageStatus(status) || (result as AgentChatResultResponse).terminal === true;
|
||||||
const runnerTrace = mergeTerminalResultTrace(message.runnerTrace, result as AgentChatResultResponse);
|
const runnerTrace = mergeTerminalResultTrace(message.runnerTrace, result as AgentChatResultResponse);
|
||||||
rememberTraceAuthority(runnerTrace);
|
rememberTraceAuthority(runnerTrace);
|
||||||
const error = normalizeAgentError((result as AgentChatResultResponse).error ?? runnerTrace?.error ?? message.error);
|
const error = normalizeAgentError((result as AgentChatResultResponse).error ?? runnerTrace?.error ?? message.error);
|
||||||
const errorText = agentErrorDisplayText(error);
|
const errorText = agentErrorDisplayText(error);
|
||||||
const agentRun = agentRunFromResult(result as AgentChatResultResponse, runnerTrace) ?? agentRunFromMessage(message);
|
const agentRun = agentRunFromResult(result as AgentChatResultResponse, runnerTrace) ?? agentRunFromMessage(message);
|
||||||
const replyText = agentReplyText((result as AgentChatResultResponse).reply);
|
|
||||||
const traceAssistantText = assistantTextFromTraceEvents(firstArray((result as AgentChatResultResponse).events, (result as AgentChatResultResponse).traceEvents, runnerTrace?.events));
|
|
||||||
const text = terminal
|
const text = terminal
|
||||||
? terminalAgentMessageText({
|
? projectedAgentMessageText({
|
||||||
status,
|
status,
|
||||||
assistantText: (result as AgentChatResultResponse).assistantText,
|
|
||||||
finalText: finalResponseText((result as AgentChatResultResponse).finalResponse),
|
finalText: finalResponseText((result as AgentChatResultResponse).finalResponse),
|
||||||
replyText,
|
|
||||||
errorText,
|
errorText,
|
||||||
resultText: (result as AgentChatResultResponse).text,
|
baseText: message.text
|
||||||
summaryText: (result as AgentChatResultResponse).summary,
|
|
||||||
traceAssistantText
|
|
||||||
})
|
})
|
||||||
: message.text;
|
: message.text;
|
||||||
return { ...message, status, text, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
return { ...message, status, text, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||||
@@ -563,12 +558,12 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
updatedAt: new Date().toISOString()
|
updatedAt: new Date().toISOString()
|
||||||
});
|
});
|
||||||
const explicitStatus = normalizedStatusText(result.status);
|
const explicitStatus = normalizedStatusText(result.status);
|
||||||
const status = explicitStatus ? statusFromResult(result.status) : message.status;
|
const resultStatus = explicitStatus ? statusFromResult(result.status) : message.status;
|
||||||
|
const status = isTerminalMessageStatus(message.status) ? message.status : resultStatus;
|
||||||
const terminal = result.terminal === true || isTerminalMessageStatus(status);
|
const terminal = result.terminal === true || isTerminalMessageStatus(status);
|
||||||
const error = normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
const error = normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
||||||
const errorText = agentErrorDisplayText(error);
|
const errorText = agentErrorDisplayText(error);
|
||||||
const traceAssistantText = assistantTextFromTraceEvents(events);
|
const text = terminal ? projectedAgentMessageText({ status, finalText: finalResponseText(result.finalResponse), errorText, baseText: message.text }) : message.text;
|
||||||
const text = terminal ? firstNonEmptyString(result.assistantText, finalResponseText(result.finalResponse), agentReplyText(result.reply), errorText, result.text, result.summary, traceAssistantText, message.text) ?? message.text : message.text;
|
|
||||||
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
||||||
rememberTraceAuthority(runnerTrace);
|
rememberTraceAuthority(runnerTrace);
|
||||||
return { ...message, status, text, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
return { ...message, status, text, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||||
@@ -848,15 +843,14 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
if (!shouldApplyTraceToMessage(message, traceId, authoritySessionId)) return message;
|
if (!shouldApplyTraceToMessage(message, traceId, authoritySessionId)) return message;
|
||||||
const runnerTrace = mergeRunnerTrace(message.runnerTrace, trace);
|
const runnerTrace = mergeRunnerTrace(message.runnerTrace, trace);
|
||||||
rememberTraceAuthority(runnerTrace);
|
rememberTraceAuthority(runnerTrace);
|
||||||
const status = statusFromResult(firstNonEmptyString(turn?.status, message.status) ?? message.status);
|
const status = message.status;
|
||||||
const terminal = turn?.terminal === true || isTerminalMessageStatus(turn?.status);
|
const terminal = isTerminalMessageStatus(status);
|
||||||
const traceAssistantText = message.role === "agent" ? assistantTextFromTraceEvents(Array.isArray(runnerTrace.events) ? runnerTrace.events : []) : null;
|
|
||||||
const error = message.role === "agent" ? normalizeAgentError(runnerTrace.error ?? message.error) : normalizeAgentError(message.error);
|
const error = message.role === "agent" ? normalizeAgentError(runnerTrace.error ?? message.error) : normalizeAgentError(message.error);
|
||||||
const errorText = message.role === "agent" ? agentErrorDisplayText(error) : null;
|
const errorText = message.role === "agent" ? agentErrorDisplayText(error) : null;
|
||||||
const nextText = message.role === "agent" && terminal
|
const nextText = message.role === "agent" && terminal
|
||||||
? firstNonEmptyString(finalResponseText(runnerTrace.finalResponse), errorText, traceAssistantText, message.text) ?? message.text
|
? projectedAgentMessageText({ status, finalText: finalResponseText(runnerTrace.finalResponse), errorText, baseText: message.text })
|
||||||
: message.text;
|
: message.text;
|
||||||
return { ...message, status, text: nextText, traceAutoLifecycle: terminal ? "terminal" : "running", runnerTrace, error: error ?? message.error ?? null, updatedAt: new Date().toISOString() };
|
return { ...message, status, text: nextText, traceAutoLifecycle: terminal ? "terminal" : message.traceAutoLifecycle, runnerTrace, error: error ?? message.error ?? null, updatedAt: new Date().toISOString() };
|
||||||
}));
|
}));
|
||||||
markWorkbenchTraceProjected(traceId);
|
markWorkbenchTraceProjected(traceId);
|
||||||
void refreshSessions(trace.sessionId ?? selectedSessionId.value);
|
void refreshSessions(trace.sessionId ?? selectedSessionId.value);
|
||||||
@@ -865,25 +859,16 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
function completeTrace(traceId: string, result: AgentChatResultResponse): void {
|
function completeTrace(traceId: string, result: AgentChatResultResponse): void {
|
||||||
const authoritySessionId = traceResultSessionId(result);
|
const authoritySessionId = traceResultSessionId(result);
|
||||||
if (!shouldApplyActiveTraceAuthority(traceId, authoritySessionId)) return;
|
if (!shouldApplyActiveTraceAuthority(traceId, authoritySessionId)) return;
|
||||||
const resultTrace = recordValue(result.runnerTrace);
|
|
||||||
const traceAssistantText = assistantTextFromTraceEvents(firstArray(result.events, result.traceEvents, resultTrace?.events));
|
|
||||||
const terminalStatus = result.status === "completed" ? "completed" : statusFromResult(result.status);
|
const terminalStatus = result.status === "completed" ? "completed" : statusFromResult(result.status);
|
||||||
const text = terminalAgentMessageText({
|
const finalText = finalResponseText(result.finalResponse);
|
||||||
status: terminalStatus,
|
const errorText = agentErrorDisplayText(result.error);
|
||||||
assistantText: result.assistantText,
|
|
||||||
finalText: finalResponseText(result.finalResponse),
|
|
||||||
replyText: typeof result.reply === "string" ? result.reply : result.reply?.content,
|
|
||||||
errorText: agentErrorDisplayText(result.error),
|
|
||||||
resultText: result.text,
|
|
||||||
summaryText: result.summary,
|
|
||||||
traceAssistantText
|
|
||||||
});
|
|
||||||
updateActiveMessages((source) => source.map((message) => {
|
updateActiveMessages((source) => source.map((message) => {
|
||||||
if (!shouldApplyTraceToMessage(message, traceId, authoritySessionId)) return message;
|
if (!shouldApplyTraceToMessage(message, traceId, authoritySessionId)) return message;
|
||||||
const runnerTrace = mergeTerminalResultTrace(message.runnerTrace, result);
|
const runnerTrace = mergeTerminalResultTrace(message.runnerTrace, result);
|
||||||
rememberTraceAuthority(runnerTrace);
|
rememberTraceAuthority(runnerTrace);
|
||||||
const error = normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
const error = normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
||||||
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
||||||
|
const text = projectedAgentMessageText({ status: terminalStatus, finalText, errorText, baseText: message.text });
|
||||||
return { ...message, status: terminalStatus, text, traceAutoLifecycle: "terminal", runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
return { ...message, status: terminalStatus, text, traceAutoLifecycle: "terminal", runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||||
}));
|
}));
|
||||||
rememberTurnStatus(traceId, result);
|
rememberTurnStatus(traceId, result);
|
||||||
@@ -1101,13 +1086,12 @@ function normalizeChatMessage(message: ChatMessage): ChatMessage {
|
|||||||
const status = normalizeChatMessageStatus(message.status);
|
const status = normalizeChatMessageStatus(message.status);
|
||||||
const baseText = firstNonEmptyString(message.text, messageText((message as Record<string, unknown>).content), messageText((message as Record<string, unknown>).message));
|
const baseText = firstNonEmptyString(message.text, messageText((message as Record<string, unknown>).content), messageText((message as Record<string, unknown>).message));
|
||||||
const finalText = firstNonEmptyString(finalResponseText((message as Record<string, unknown>).finalResponse), finalResponseText(runnerTrace?.finalResponse));
|
const finalText = firstNonEmptyString(finalResponseText((message as Record<string, unknown>).finalResponse), finalResponseText(runnerTrace?.finalResponse));
|
||||||
const traceAssistantText = assistantTextFromTraceEvents(Array.isArray(runnerTrace?.events) ? runnerTrace.events : []);
|
|
||||||
const errorText = agentErrorDisplayText(error);
|
const errorText = agentErrorDisplayText(error);
|
||||||
const text = role === "agent"
|
const text = role === "agent"
|
||||||
? isTerminalMessageStatus(status)
|
? isTerminalMessageStatus(status)
|
||||||
? terminalAgentMessageText({ status, finalText, errorText, traceAssistantText, baseText })
|
? projectedAgentMessageText({ status, finalText, errorText, baseText })
|
||||||
: ""
|
: ""
|
||||||
: firstNonEmptyString(baseText, finalText, traceAssistantText, errorText) ?? "";
|
: firstNonEmptyString(baseText, finalText, errorText) ?? "";
|
||||||
const messageId = firstNonEmptyString((message as Record<string, unknown>).messageId, message.id) ?? nextProtocolId("msg");
|
const messageId = firstNonEmptyString((message as Record<string, unknown>).messageId, message.id) ?? nextProtocolId("msg");
|
||||||
return { ...message, role, text, id: messageId, messageId, title: normalizeWorkbenchMessageTitle(role, message.title), createdAt: message.createdAt ?? new Date().toISOString(), status, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined };
|
return { ...message, role, text, id: messageId, messageId, title: normalizeWorkbenchMessageTitle(role, message.title), createdAt: message.createdAt ?? new Date().toISOString(), status, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined };
|
||||||
}
|
}
|
||||||
@@ -1306,33 +1290,10 @@ function finalResponseText(value: unknown): string | null {
|
|||||||
return firstNonEmptyString(messageText(record.text), messageText(record.content), messageText(record.message));
|
return firstNonEmptyString(messageText(record.text), messageText(record.content), messageText(record.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
function agentReplyText(value: AgentChatResultResponse["reply"]): string | null {
|
function projectedAgentMessageText(input: { status: unknown; finalText?: string | null; errorText?: string | null; baseText?: string | null }): string {
|
||||||
if (typeof value === "string") return value;
|
const text = firstNonEmptyString(input.finalText, input.baseText);
|
||||||
return value && typeof value === "object" ? firstNonEmptyString(value.content) : null;
|
if (!isFailureTerminalStatus(input.status)) return text ?? "";
|
||||||
}
|
return firstNonEmptyString(input.errorText, text) ?? "";
|
||||||
|
|
||||||
function terminalAgentMessageText(input: {
|
|
||||||
status: unknown;
|
|
||||||
assistantText?: unknown;
|
|
||||||
finalText?: string | null;
|
|
||||||
replyText?: string | null;
|
|
||||||
errorText?: string | null;
|
|
||||||
resultText?: unknown;
|
|
||||||
summaryText?: unknown;
|
|
||||||
traceAssistantText?: string | null;
|
|
||||||
baseText?: string | null;
|
|
||||||
}): string {
|
|
||||||
const finalText = firstNonEmptyString(
|
|
||||||
input.assistantText,
|
|
||||||
input.finalText,
|
|
||||||
input.replyText,
|
|
||||||
input.resultText,
|
|
||||||
input.summaryText,
|
|
||||||
input.traceAssistantText,
|
|
||||||
input.baseText
|
|
||||||
);
|
|
||||||
if (!isFailureTerminalStatus(input.status)) return finalText ?? "";
|
|
||||||
return firstNonEmptyString(input.errorText, finalText) ?? "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isFailureTerminalStatus(value: unknown): boolean {
|
function isFailureTerminalStatus(value: unknown): boolean {
|
||||||
|
|||||||
Reference in New Issue
Block a user