mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-04 22:02:16 +02:00
feat: aggregate artifacts from messages
This commit is contained in:
parent
e9deb1dcb1
commit
cba2a726a2
1 changed files with 74 additions and 1 deletions
|
|
@ -1,4 +1,32 @@
|
||||||
import type { ArtifactKind, ArtifactStatus } from "../model/artifact";
|
import type { ThreadMessageLike } from "@assistant-ui/react";
|
||||||
|
import {
|
||||||
|
ARTIFACT_TOOL_KINDS,
|
||||||
|
type ArtifactKind,
|
||||||
|
type ArtifactStatus,
|
||||||
|
type ChatArtifact,
|
||||||
|
} from "../model/artifact";
|
||||||
|
|
||||||
|
interface ToolCallPart {
|
||||||
|
type: "tool-call";
|
||||||
|
toolCallId: string;
|
||||||
|
toolName: string;
|
||||||
|
args?: Record<string, unknown>;
|
||||||
|
result?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isToolCallPart(part: unknown): part is ToolCallPart {
|
||||||
|
return (
|
||||||
|
typeof part === "object" &&
|
||||||
|
part !== null &&
|
||||||
|
(part as { type?: unknown }).type === "tool-call" &&
|
||||||
|
typeof (part as { toolCallId?: unknown }).toolCallId === "string" &&
|
||||||
|
typeof (part as { toolName?: unknown }).toolName === "string"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function asRecord(value: unknown): Record<string, unknown> {
|
||||||
|
return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : {};
|
||||||
|
}
|
||||||
|
|
||||||
function firstString(...values: unknown[]): string | null {
|
function firstString(...values: unknown[]): string | null {
|
||||||
for (const value of values) {
|
for (const value of values) {
|
||||||
|
|
@ -64,3 +92,48 @@ function describeArtifact(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aggregate the deliverable artifacts referenced across a thread's messages.
|
||||||
|
*
|
||||||
|
* Scans assistant tool-call parts, keeps recognized deliverable tools, and
|
||||||
|
* dedupes by backing entity (so a regenerated report collapses to one entry,
|
||||||
|
* refreshed in place to keep chronological order). Errored deliverables are
|
||||||
|
* dropped — they have nothing to open or jump to.
|
||||||
|
*/
|
||||||
|
export function collectArtifacts(messages: readonly ThreadMessageLike[]): ChatArtifact[] {
|
||||||
|
const byKey = new Map<string, ChatArtifact>();
|
||||||
|
|
||||||
|
for (const message of messages) {
|
||||||
|
if (message.role !== "assistant" || !Array.isArray(message.content)) continue;
|
||||||
|
|
||||||
|
for (const part of message.content) {
|
||||||
|
if (!isToolCallPart(part)) continue;
|
||||||
|
const kind = ARTIFACT_TOOL_KINDS[part.toolName];
|
||||||
|
if (!kind) continue;
|
||||||
|
|
||||||
|
const args = asRecord(part.args);
|
||||||
|
const result = asRecord(part.result);
|
||||||
|
const { title, entityId, status } = describeArtifact(
|
||||||
|
kind,
|
||||||
|
args,
|
||||||
|
result,
|
||||||
|
part.result !== undefined
|
||||||
|
);
|
||||||
|
if (status === "error") continue;
|
||||||
|
|
||||||
|
const key = entityId != null ? `${kind}:${entityId}` : part.toolCallId;
|
||||||
|
byKey.set(key, {
|
||||||
|
key,
|
||||||
|
kind,
|
||||||
|
title,
|
||||||
|
status,
|
||||||
|
toolCallId: part.toolCallId,
|
||||||
|
entityId,
|
||||||
|
contentType: kind === "resume" ? "typst" : "markdown",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(byKey.values());
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue