MCPcopy Create free account
hub / github.com/agegr/pi-web / AssistantMessageView

Function AssistantMessageView

components/MessageView.tsx:339–579  ·  view source on GitHub ↗
({
  message,
  isStreaming,
  toolResults,
  modelNames,
  cwd,
  onOpenFile,
  showTimestamp,
  prevTimestamp,
  sessionId,
  entryId,
}: {
  message: AssistantMessage;
  isStreaming?: boolean;
  toolResults?: Map<string, ToolResultMessage>;
  modelNames?: Record<string, string>;
  cwd?: string;
  onOpenFile?: (filePath: string) => void;
  showTimestamp?: boolean;
  prevTimestamp?: number;
  sessionId?: string;
  entryId?: string;
})

Source from the content-addressed store, hash-verified

337}
338
339function AssistantMessageView({
340 message,
341 isStreaming,
342 toolResults,
343 modelNames,
344 cwd,
345 onOpenFile,
346 showTimestamp,
347 prevTimestamp,
348 sessionId,
349 entryId,
350}: {
351 message: AssistantMessage;
352 isStreaming?: boolean;
353 toolResults?: Map<string, ToolResultMessage>;
354 modelNames?: Record<string, string>;
355 cwd?: string;
356 onOpenFile?: (filePath: string) => void;
357 showTimestamp?: boolean;
358 prevTimestamp?: number;
359 sessionId?: string;
360 entryId?: string;
361}) {
362 const time = showTimestamp ? formatTime(message.timestamp) : null;
363 const blockItems = (message.content ?? [])
364 .map((block, originalIndex) => ({ block, originalIndex }))
365 .filter(({ block }) => !isEmptyThinkingBlock(block, { isStreaming }));
366 const blocks = blockItems.map(({ block }) => block);
367 const [hovered, setHovered] = useState(false);
368 const [copied, setCopied] = useState(false);
369 const streamStartRef = useRef<number | null>(null);
370 const [tps, setTps] = useState<number | null>(null);
371 const blockItemsRef = useRef(blockItems);
372 blockItemsRef.current = blockItems;
373
374 // Streaming-based timing for thinking blocks
375 const blockStartTimesRef = useRef<Map<number, number>>(new Map());
376 const [streamingDurations, setStreamingDurations] = useState<Map<number, number>>(new Map());
377
378 // Thinking duration derived from file timestamps: time from prev message end to this message end
379 // This is the total generation time (thinking + any text before first tool call)
380 const thinkingDurationFromFile = useMemo<number | undefined>(() => {
381 if (!message.timestamp || !prevTimestamp) return undefined;
382 const secs = Math.round((message.timestamp - prevTimestamp) / 1000);
383 return secs > 0 ? secs : undefined;
384 }, [message.timestamp, prevTimestamp]);
385
386 // Tool call durations derived from session file timestamps (accurate for completed messages)
387 // assistant message timestamp = when generation ended = when tools started running
388 // toolResult timestamp = when tool execution finished
389 const toolCallDurations = useMemo<Map<string, number>>(() => {
390 const map = new Map<string, number>();
391 if (!toolResults || !message.timestamp) return map;
392 for (const [callId, result] of toolResults) {
393 if (result.timestamp && message.timestamp) {
394 const secs = Math.round((result.timestamp - message.timestamp) / 1000);
395 if (secs > 0) map.set(callId, secs);
396 }

Callers

nothing calls this directly

Calls 3

isEmptyThinkingBlockFunction · 0.90
formatTimeFunction · 0.85
formatUsageFunction · 0.85

Tested by

no test coverage detected