Add chat sources and citations

This commit is contained in:
Matthias
2026-06-16 10:58:15 +02:00
parent 85af9d7078
commit 9f17d4d1e1
9 changed files with 356 additions and 13 deletions

View File

@@ -20,11 +20,22 @@ type ToolTrace = {
inputSummary: string;
resultSummary: string;
};
type ChatSource = {
id: string;
title: string;
description?: string;
};
type ChatCitation = {
marker: string;
sourceId: string;
};
type UserChatMessage = { role: "user"; content: string };
type AssistantChatMessage = {
role: "assistant";
content: string;
toolTrace?: ToolTrace[];
sources?: ChatSource[];
citations?: ChatCitation[];
};
type ChatMessage = UserChatMessage | AssistantChatMessage;
type LegacyChatSession = {
@@ -69,6 +80,46 @@ function normalizeToolTrace(value: unknown): ToolTrace[] | undefined {
return trace.length > 0 ? trace : undefined;
}
function normalizeSources(value: unknown): ChatSource[] | undefined {
if (!Array.isArray(value)) return undefined;
const sources = value.flatMap((item) => {
if (!item || typeof item !== "object") return [];
const candidate = item as Record<string, unknown>;
if (typeof candidate.id !== "string" || typeof candidate.title !== "string") return [];
if (
candidate.description !== undefined &&
typeof candidate.description !== "string"
) {
return [];
}
return [
{
id: candidate.id,
title: candidate.title,
...(candidate.description ? { description: candidate.description } : {}),
},
];
});
return sources.length > 0 ? sources : undefined;
}
function normalizeCitations(value: unknown): ChatCitation[] | undefined {
if (!Array.isArray(value)) return undefined;
const citations = value.flatMap((item) => {
if (!item || typeof item !== "object") return [];
const candidate = item as Record<string, unknown>;
if (typeof candidate.marker !== "string" || typeof candidate.sourceId !== "string") {
return [];
}
return [{ marker: candidate.marker, sourceId: candidate.sourceId }];
});
return citations.length > 0 ? citations : undefined;
}
function normalizeMessage(value: unknown): ChatMessage | null {
if (!value || typeof value !== "object") return null;
const candidate = value as Record<string, unknown>;
@@ -78,9 +129,15 @@ function normalizeMessage(value: unknown): ChatMessage | null {
}
if (candidate.role === "assistant") {
const toolTrace = normalizeToolTrace(candidate.toolTrace);
return toolTrace
? { role: "assistant", content: candidate.content, toolTrace }
: { role: "assistant", content: candidate.content };
const sources = normalizeSources(candidate.sources);
const citations = normalizeCitations(candidate.citations);
return {
role: "assistant",
content: candidate.content,
...(toolTrace ? { toolTrace } : {}),
...(sources ? { sources } : {}),
...(citations ? { citations } : {}),
};
}
return null;
@@ -175,6 +232,12 @@ export function SavingsChatPage() {
...(message.role === "assistant" && message.toolTrace
? { toolTrace: message.toolTrace }
: {}),
...(message.role === "assistant" && message.sources
? { sources: message.sources }
: {}),
...(message.role === "assistant" && message.citations
? { citations: message.citations }
: {}),
})),
[displayMessages],
);