File size: 2,713 Bytes
f3ad26c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import type { ChatMessage, MessageSegment } from './contracts';

function upsertContentSegment(
  segments: MessageSegment[],
  id: string,
  kind: 'text' | 'reasoning',
  content: string,
): MessageSegment[] {
  const index = segments.findIndex((segment) => segment.id === id);
  if (!content.trim()) {
    return index < 0 ? segments : segments.filter((_, candidate) => candidate !== index);
  }
  const next: MessageSegment = { id, kind, content };
  if (index < 0) return [...segments, next];
  return segments.map((segment, candidate) => candidate === index ? next : segment);
}

export function updateRoundSegments(
  segments: MessageSegment[],
  roundId: string,
  reasoning: string,
  text: string,
): MessageSegment[] {
  let next = upsertContentSegment(segments, `${roundId}:reasoning`, 'reasoning', reasoning);
  next = upsertContentSegment(next, `${roundId}:text`, 'text', text);
  return next;
}

export function appendToolSegment(
  segments: MessageSegment[],
  toolCallId: string,
  roundId?: string,
): MessageSegment[] {
  if (segments.some((segment) => segment.kind === 'tool' && segment.toolCallId === toolCallId)) {
    return segments;
  }
  return [...segments, {
    id: `tool:${toolCallId}`,
    kind: 'tool',
    toolCallId,
    ...(roundId ? { roundId } : {}),
  }];
}

export function segmentsForMessage(message: ChatMessage): MessageSegment[] {
  if (message.segments) return message.segments;
  const segments: MessageSegment[] = [];
  if (message.reasoning?.trim()) {
    segments.push({ id: `${message.id}:legacy-reasoning`, kind: 'reasoning', content: message.reasoning });
  }
  if (message.content.trim()) {
    segments.push({ id: `${message.id}:legacy-text`, kind: 'text', content: message.content });
  }
  const referencedArtifacts = new Set<string>();
  for (const tool of message.tools ?? []) {
    segments.push({ id: `tool:${tool.id}`, kind: 'tool', toolCallId: tool.id });
    if (tool.artifactId) referencedArtifacts.add(tool.artifactId);
  }
  for (const artifact of message.artifacts ?? []) {
    if (!referencedArtifacts.has(artifact.id)) {
      segments.push({ id: `artifact:${artifact.id}`, kind: 'artifact', artifactId: artifact.id });
    }
  }
  return segments;
}

export function normalizeMessageSegments(message: ChatMessage): ChatMessage {
  if (message.role !== 'assistant') return message;
  const artifactIds = new Set((message.artifacts ?? []).map((artifact) => artifact.id));
  const tools = message.tools?.map((tool) => !tool.artifactId && artifactIds.has(tool.id)
    ? { ...tool, artifactId: tool.id }
    : tool);
  const withTools = tools ? { ...message, tools } : message;
  return { ...withTools, segments: segmentsForMessage(withTools) };
}