feat: show response generation time on assistant messages
Track how long each assistant response took to generate and display it subtly next to the timestamp (e.g. '· 12.3s'). The timing is measured from the first streaming delta to the final state, and preserved across the history reload that follows stream completion. Only visible for messages generated during the current session.
This commit is contained in:
@@ -539,6 +539,13 @@ export const ChatMessageComponent = memo(function ChatMessageComponent({ message
|
||||
</span>
|
||||
)}
|
||||
{message.timestamp && <Timestamp ts={message.timestamp} />}
|
||||
{!isUser && message.generationTimeMs != null && (
|
||||
<span className="text-[10px] text-pc-text-faint" title="Generation time">
|
||||
· {message.generationTimeMs < 1000
|
||||
? `${message.generationTimeMs}ms`
|
||||
: `${(message.generationTimeMs / 1000).toFixed(1)}s`}
|
||||
</span>
|
||||
)}
|
||||
{isUser && message.sendStatus === 'sending' && (
|
||||
<span title="Sending..."><Clock size={10} className="animate-pulse text-pc-text-faint" /></span>
|
||||
)}
|
||||
|
||||
@@ -54,6 +54,8 @@ export function useGateway() {
|
||||
const [activeSessions, setActiveSessions] = useState<Set<string>>(new Set());
|
||||
const [unreadSessions, setUnreadSessions] = useState<Set<string>>(new Set());
|
||||
const [agentIdentity, setAgentIdentity] = useState<AgentIdentity | null>(null);
|
||||
/** Map of runId → generation duration (ms), preserved across loadHistory reloads */
|
||||
const generationTimesRef = useRef<Map<string, number>>(new Map());
|
||||
|
||||
const handleAgentEvent = useCallback((payload: JsonPayload) => {
|
||||
if (payload?.stream !== 'tool') return;
|
||||
@@ -228,6 +230,18 @@ export function useGateway() {
|
||||
merged.push(msg);
|
||||
}
|
||||
}
|
||||
// Apply stored generation time to the last assistant message if available
|
||||
const genKey = sessionKey + ':latest';
|
||||
const genTime = generationTimesRef.current.get(genKey);
|
||||
if (genTime) {
|
||||
generationTimesRef.current.delete(genKey);
|
||||
for (let i = merged.length - 1; i >= 0; i--) {
|
||||
if (merged[i].role === 'assistant') {
|
||||
merged[i] = { ...merged[i], generationTimeMs: genTime };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
setMessages(merged);
|
||||
}
|
||||
} catch {
|
||||
@@ -342,10 +356,16 @@ export function useGateway() {
|
||||
blocks,
|
||||
isStreaming: true,
|
||||
runId,
|
||||
streamStartedAt: Date.now(),
|
||||
};
|
||||
return [...prev, msg];
|
||||
});
|
||||
} else if (state === 'final') {
|
||||
// Compute generation time from the streaming message before history reload replaces it
|
||||
const lastMsg = messagesRef.current[messagesRef.current.length - 1];
|
||||
if (lastMsg?.role === 'assistant' && lastMsg.streamStartedAt) {
|
||||
generationTimesRef.current.set(activeSessionRef.current + ':latest', Date.now() - lastMsg.streamStartedAt);
|
||||
}
|
||||
currentRunIdRef.current = null;
|
||||
setIsGenerating(false);
|
||||
loadHistory(activeSessionRef.current);
|
||||
|
||||
@@ -10,6 +10,10 @@ export interface ChatMessage {
|
||||
metadata?: Record<string, unknown>;
|
||||
/** Optimistic send status for user messages */
|
||||
sendStatus?: 'sending' | 'sent' | 'error';
|
||||
/** Timestamp (ms) when streaming started for this message */
|
||||
streamStartedAt?: number;
|
||||
/** Total generation time in milliseconds (set when streaming ends) */
|
||||
generationTimeMs?: number;
|
||||
}
|
||||
|
||||
export type MessageBlock =
|
||||
|
||||
Reference in New Issue
Block a user