feat: custom tool renderers

This commit is contained in:
Zoe
2026-09-25 19:46:00 -05:00
parent 3f81f96bda
commit bb5795e00d
18 changed files with 1294 additions and 65 deletions
+269
View File
@@ -0,0 +1,269 @@
export type StoredToolValue = Readonly<{ type: string; value: string }> | null | undefined;
export const parseToolValue = (stored: StoredToolValue): unknown => {
if (!stored) {
return null;
}
if (stored.type !== "json") {
return stored.value;
}
try {
return JSON.parse(stored.value);
} catch {
// Tool arguments may still be streaming, or come from older records.
return stored.value;
}
};
export const formatToolValue = (stored: StoredToolValue): string => {
const value = parseToolValue(stored);
return typeof value === "string" ? value : JSON.stringify(value, null, 2) ?? "";
};
export interface SearchSource {
title: string;
url: string;
hostname: string;
snippet: string;
}
export const readSearchSources = (value: unknown): SearchSource[] | null => {
if (!Array.isArray(value)) {
return null;
}
const sources: SearchSource[] = [];
const seen = new Set<string>();
for (const item of value) {
if (typeof item !== "object" || item === null || !("link" in item) || typeof item.link !== "string") {
continue;
}
let url: URL;
try {
url = new URL(item.link);
} catch {
continue;
}
if (url.protocol !== "https:" && url.protocol !== "http:") {
continue;
}
url.hash = "";
if (seen.has(url.href)) {
continue;
}
seen.add(url.href);
sources.push({
title: "title" in item && typeof item.title === "string" ? item.title : url.hostname,
url: url.href,
hostname: url.hostname,
snippet: "snippet" in item && typeof item.snippet === "string" ? item.snippet : "",
});
}
return sources;
};
export interface ToolDisplayInfo {
icon: string;
verb: string;
target: string;
}
export const formatDuration = (start?: Date | string | number | null, end?: Date | string | number | null): string | null => {
if (!start || !end) {
return null;
}
const startTime = typeof start === "number" ? start : new Date(start).getTime();
const endTime = typeof end === "number" ? end : new Date(end).getTime();
if (Number.isNaN(startTime) || Number.isNaN(endTime) || endTime <= startTime) {
return null;
}
const seconds = (endTime - startTime) / 1000;
if (seconds < 0.05) {
return null;
}
return `${seconds.toFixed(1)}s`;
};
const extractField = (obj: unknown, fields: string[]): string => {
if (typeof obj !== "object" || obj === null) {
return typeof obj === "string" ? obj : "";
}
for (const field of fields) {
if (field in obj) {
const val = (obj as Record<string, unknown>)[field];
if (typeof val === "string" && val.length > 0) {
return val;
}
}
}
return "";
};
export const DOMAIN_COLORS = [
"#eab308", // amber / yellow
"#16a34a", // emerald / green
"#0284c7", // sky / blue
"#ea580c", // orange
"#dc2626", // red
"#7c3aed", // violet
"#0d9488", // teal
"#db2777", // pink
"#4f46e5", // indigo
];
export const getDomainColor = (domain: string): string => {
const clean = domain.replace(/^www\./, "").toLowerCase();
let hash = 0;
for (let i = 0; i < clean.length; i++) {
hash = (hash << 5) - hash + clean.charCodeAt(i);
hash |= 0;
}
const index = Math.abs(hash) % DOMAIN_COLORS.length;
return DOMAIN_COLORS[index] ?? "#eab308";
};
export const domainInitial = (domain: string): string => {
const clean = domain.replace(/^www\./, "").trim();
return clean ? clean.charAt(0).toUpperCase() : "?";
};
export const getFaviconUrl = (hostname: string): string => {
const clean = hostname.replace(/^www\./, "").trim();
return `https://icons.duckduckgo.com/ip3/${clean}.ico`;
};
export const getToolDisplayInfo = (toolCall: {
toolName: string;
status: string;
input?: StoredToolValue;
output?: StoredToolValue;
error?: StoredToolValue;
}): ToolDisplayInfo => {
const rawInput = parseToolValue(toolCall.input);
const rawOutput = parseToolValue(toolCall.output);
const rawError = parseToolValue(toolCall.error);
const isPending = toolCall.status === "pending";
const isFailed = toolCall.status === "failed";
const isCancelled = toolCall.status === "cancelled";
const name = toolCall.toolName.toLowerCase();
const errorSummary = typeof rawError === "string"
? (rawError.trim().split("\n")[0] ?? "")
: extractField(rawError, ["message", "error"]);
if (name === "search" || name.includes("search")) {
const query = extractField(rawInput, ["query", "q"]);
const target = query ? `"${query}"` : "";
const icon = "i-mynaui-search";
if (isPending) {
return { icon, verb: "Searching the web", target };
}
if (isFailed) {
return { icon: "i-mynaui-danger-triangle", verb: "Search failed", target: target || errorSummary };
}
if (isCancelled) {
return { icon, verb: "Search cancelled", target };
}
const sources = readSearchSources(rawOutput);
const count = sources?.length ?? 0;
const verb = count > 0 ? `Searched ${count} ${count === 1 ? "site" : "sites"}` : "Searched the web";
return { icon, verb, target };
}
if (name === "fetchurl" || name === "fetch_url" || name.includes("fetch") || name.includes("scrape")) {
const url = extractField(rawInput, ["url", "link", "uri", "href"]);
const icon = "i-mynaui-link";
if (isPending) {
return { icon, verb: "Fetching URL", target: url };
}
if (isFailed) {
return { icon: "i-mynaui-danger-triangle", verb: "Fetch failed", target: url || errorSummary };
}
if (isCancelled) {
return { icon, verb: "Fetch cancelled", target: url };
}
return { icon, verb: "Fetched URL", target: url };
}
if (name === "python" || name.includes("python") || name.includes("pyeval")) {
const code = extractField(rawInput, ["code", "script", "command"]);
const firstLine = code.trim().split("\n")[0] ?? "";
const icon = "i-mynaui-terminal";
if (isPending) {
return { icon, verb: "Running Python", target: firstLine };
}
if (isFailed) {
return { icon: "i-mynaui-danger-triangle", verb: "Python failed", target: firstLine || errorSummary };
}
if (isCancelled) {
return { icon, verb: "Python cancelled", target: firstLine };
}
return { icon, verb: "Ran Python", target: firstLine };
}
if (name.includes("read") || name.includes("cat") || name.includes("view")) {
const path = extractField(rawInput, ["path", "file", "filePath", "filename"]);
const icon = "i-mynaui-file";
if (isPending) {
return { icon, verb: "Reading", target: path };
}
if (isFailed) {
return { icon: "i-mynaui-danger-triangle", verb: "Read failed", target: path || errorSummary };
}
if (isCancelled) {
return { icon, verb: "Read cancelled", target: path };
}
return { icon, verb: "Read", target: path };
}
if (name.includes("edit") || name.includes("write") || name.includes("modify") || name.includes("patch") || name.includes("save")) {
const path = extractField(rawInput, ["path", "file", "filePath", "filename"]);
const icon = "i-mynaui-edit";
if (isPending) {
return { icon, verb: "Editing", target: path };
}
if (isFailed) {
return { icon: "i-mynaui-danger-triangle", verb: "Edit failed", target: path || errorSummary };
}
if (isCancelled) {
return { icon, verb: "Edit cancelled", target: path };
}
return { icon, verb: "Edited", target: path };
}
if (name.includes("query") || name.includes("sql") || name.includes("database") || name.includes("db")) {
const query = extractField(rawInput, ["query", "sql", "statement", "command"]);
const icon = "i-mynaui-database";
if (isPending) {
return { icon, verb: "Querying", target: query };
}
if (isFailed) {
const target = query || errorSummary;
return { icon: "i-mynaui-database", verb: "Query failed", target };
}
if (isCancelled) {
return { icon, verb: "Query cancelled", target: query };
}
return { icon, verb: "Queried", target: query };
}
const formattedName = toolCall.toolName
.replace(/([A-Z])/g, " $1")
.replace(/[-_]/g, " ")
.trim();
const capitalized = formattedName.charAt(0).toUpperCase() + formattedName.slice(1);
const genericTarget = extractField(rawInput, ["query", "path", "command", "prompt", "url"])
|| (isFailed ? errorSummary : "");
if (isPending) {
return { icon: "i-mynaui-tool", verb: `Running ${capitalized}`, target: genericTarget };
}
if (isFailed) {
return { icon: "i-mynaui-danger-triangle", verb: `${capitalized} failed`, target: genericTarget || errorSummary };
}
if (isCancelled) {
return { icon: "i-mynaui-tool", verb: `${capitalized} cancelled`, target: genericTarget };
}
return { icon: "i-mynaui-tool", verb: capitalized, target: genericTarget };
};