fix: improve sorted chat activity rendering
Show tools and reasoning live in sorted mode Keep ambiguous text deferred until final output Avoid duplicate reasoning outside Activity
This commit is contained in:
@@ -36,19 +36,6 @@ const getMessageFinish = (message: ChatMessageEntry): string | undefined => {
|
||||
return typeof finish === 'string' ? finish : undefined;
|
||||
};
|
||||
|
||||
const isAssistantMessageCompleted = (message: ChatMessageEntry): boolean => {
|
||||
const info = message.info as { time?: { completed?: unknown }; status?: unknown };
|
||||
const completed = info.time?.completed;
|
||||
const status = info.status;
|
||||
if (typeof completed !== 'number' || completed <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (typeof status === 'string') {
|
||||
return status === 'completed';
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const buildTurnPartRecord = (
|
||||
turnId: string,
|
||||
messageId: string,
|
||||
@@ -69,6 +56,7 @@ interface ProjectActivityInput {
|
||||
turnId: string;
|
||||
assistantMessages: ChatMessageEntry[];
|
||||
summarySourceMessageId?: string;
|
||||
summarySourcePartId?: string;
|
||||
showTextJustificationActivity: boolean;
|
||||
}
|
||||
|
||||
@@ -84,35 +72,42 @@ export const projectTurnActivity = (input: ProjectActivityInput): ProjectActivit
|
||||
let hasTools = false;
|
||||
let hasReasoning = false;
|
||||
|
||||
input.assistantMessages.forEach((message) => {
|
||||
message.parts.forEach((part) => {
|
||||
if (part.type === 'tool') {
|
||||
hasTools = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (part.type === 'reasoning' && getPartText(part)) {
|
||||
hasReasoning = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const taskMessageById = new Map<string, string>();
|
||||
const taskOrder: string[] = [];
|
||||
const partsByAfterTool = new Map<string | null, TurnActivityRecord[]>();
|
||||
let currentAfterToolPartId: string | null = null;
|
||||
|
||||
input.assistantMessages.forEach((message) => {
|
||||
const messageCompleted = isAssistantMessageCompleted(message);
|
||||
const finish = getMessageFinish(message);
|
||||
const messageHasTool = message.parts.some((part) => part.type === 'tool');
|
||||
|
||||
message.parts.forEach((part, partIndex) => {
|
||||
const isTool = part.type === 'tool';
|
||||
if (isTool) {
|
||||
hasTools = true;
|
||||
}
|
||||
|
||||
const text = part.type === 'reasoning' || part.type === 'text'
|
||||
? getPartText(part)
|
||||
: undefined;
|
||||
|
||||
if (part.type === 'reasoning' && text) {
|
||||
hasReasoning = true;
|
||||
}
|
||||
const partId = part.id ?? `${message.info.id}-part-${partIndex}-${part.type}`;
|
||||
|
||||
const toolName = isTool
|
||||
? (part as { tool?: unknown }).tool
|
||||
: undefined;
|
||||
const standaloneTool = isTool && isStandaloneTool(toolName);
|
||||
if (standaloneTool) {
|
||||
const toolPartId = part.id ?? `${message.info.id}-part-${partIndex}-${part.type}`;
|
||||
const toolPartId = partId;
|
||||
if (!taskMessageById.has(toolPartId)) {
|
||||
taskMessageById.set(toolPartId, message.info.id);
|
||||
taskOrder.push(toolPartId);
|
||||
@@ -120,6 +115,12 @@ export const projectTurnActivity = (input: ProjectActivityInput): ProjectActivit
|
||||
currentAfterToolPartId = toolPartId;
|
||||
}
|
||||
|
||||
const isConfirmedSummaryText = part.type === 'text'
|
||||
&& typeof text === 'string'
|
||||
&& finish === 'stop'
|
||||
&& input.summarySourceMessageId === message.info.id
|
||||
&& input.summarySourcePartId === partId;
|
||||
|
||||
let kind: TurnActivityRecord['kind'] | null = null;
|
||||
if (isTool) {
|
||||
kind = 'tool';
|
||||
@@ -130,10 +131,9 @@ export const projectTurnActivity = (input: ProjectActivityInput): ProjectActivit
|
||||
} else if (
|
||||
input.showTextJustificationActivity
|
||||
&& part.type === 'text'
|
||||
&& messageCompleted
|
||||
&& typeof finish === 'string'
|
||||
&& finish !== 'stop'
|
||||
&& text
|
||||
&& !isConfirmedSummaryText
|
||||
&& (messageHasTool || (typeof finish === 'string' && finish !== 'stop'))
|
||||
) {
|
||||
kind = 'justification';
|
||||
}
|
||||
|
||||
@@ -168,6 +168,7 @@ export const projectTurnRecords = (
|
||||
turnId: turn.turnId,
|
||||
assistantMessages: turn.assistantMessages,
|
||||
summarySourceMessageId: turn.summary.sourceMessageId,
|
||||
summarySourcePartId: turn.summary.sourcePartId,
|
||||
showTextJustificationActivity: effectiveOptions.showTextJustificationActivity,
|
||||
});
|
||||
turn.activityParts = activity.activityParts;
|
||||
|
||||
@@ -1106,6 +1106,8 @@ const AssistantMessageBody: React.FC<Omit<MessageBodyProps, 'isUser'>> = ({
|
||||
&& hasAnchoredActivitySegments
|
||||
&& Boolean(toggleActivityGroup);
|
||||
|
||||
const shouldDeferSortedInlineText = isSortedRenderMode && !hasStopFinish;
|
||||
|
||||
|
||||
const renderedParts = React.useMemo(() => {
|
||||
const rendered: React.ReactNode[] = [];
|
||||
@@ -1152,7 +1154,7 @@ const AssistantMessageBody: React.FC<Omit<MessageBodyProps, 'isUser'>> = ({
|
||||
|
||||
if (part.type === 'text') {
|
||||
const activity = activityByPart.get(part);
|
||||
if (isSortedRenderMode && !hasStopFinish) {
|
||||
if (shouldDeferSortedInlineText) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
@@ -1177,10 +1179,6 @@ const AssistantMessageBody: React.FC<Omit<MessageBodyProps, 'isUser'>> = ({
|
||||
|
||||
if (part.type === 'reasoning') {
|
||||
const activity = activityByPart.get(part);
|
||||
if (isSortedRenderMode && !hasStopFinish) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (activity?.kind === 'reasoning') {
|
||||
i += 1;
|
||||
continue;
|
||||
@@ -1293,7 +1291,6 @@ const AssistantMessageBody: React.FC<Omit<MessageBodyProps, 'isUser'>> = ({
|
||||
chatRenderMode,
|
||||
collapsedPreviewCount,
|
||||
expandedTools,
|
||||
hasStopFinish,
|
||||
isMobile,
|
||||
isActivityOwnerMessage,
|
||||
isSortedRenderMode,
|
||||
@@ -1306,6 +1303,7 @@ const AssistantMessageBody: React.FC<Omit<MessageBodyProps, 'isUser'>> = ({
|
||||
shouldShowTool,
|
||||
streamPhase,
|
||||
showReasoningTraces,
|
||||
shouldDeferSortedInlineText,
|
||||
syntaxTheme,
|
||||
toggleActivityGroup,
|
||||
turnGroupingContext,
|
||||
|
||||
Reference in New Issue
Block a user