Files
famapp/src/modules/agent/assistant-chat-storage.ts
T
ginnoir 72d123b9a0
CI / checks (push) Has been cancelled
fix(agent): stop tool thrash after writes and log rounds
Cap get_api_docs, break duplicate tool rounds, force a reply after
successful writes, and log each tool call so limit hits are diagnosable.
2026-07-08 22:51:31 -05:00

78 lines
2.3 KiB
TypeScript

import type { ClientChatMessage } from "./messages";
export type AssistantChatMessage = {
role: "user" | "assistant";
content: string;
imageUrl?: string;
toolCalls?: Array<{ name: string; status: number }>;
};
const STORAGE_VERSION = "v2";
const MAX_MESSAGES = 40;
function storageKey(userId: string) {
return `assistant-chat:${STORAGE_VERSION}:${userId}`;
}
function isValidMessage(value: unknown): value is AssistantChatMessage {
if (!value || typeof value !== "object") return false;
const row = value as Record<string, unknown>;
if (row.role !== "user" && row.role !== "assistant") return false;
if (typeof row.content !== "string" || row.content.trim().length === 0) return false;
if (row.imageUrl !== undefined && typeof row.imageUrl !== "string") return false;
if (row.toolCalls !== undefined) {
if (!Array.isArray(row.toolCalls)) return false;
for (const call of row.toolCalls) {
if (!call || typeof call !== "object") return false;
const entry = call as Record<string, unknown>;
if (typeof entry.name !== "string" || typeof entry.status !== "number") return false;
}
}
return true;
}
export function toClientChatMessage(message: AssistantChatMessage): ClientChatMessage {
if (!message.imageUrl) {
return { role: message.role, content: message.content };
}
return {
role: message.role,
content: message.content,
attachments: [{ type: "image", url: message.imageUrl }],
};
}
export function loadAssistantChat(userId: string): AssistantChatMessage[] {
try {
const raw = localStorage.getItem(storageKey(userId));
if (!raw) return [];
const parsed: unknown = JSON.parse(raw);
if (!Array.isArray(parsed)) return [];
return parsed.filter(isValidMessage).slice(-MAX_MESSAGES);
} catch {
return [];
}
}
export function saveAssistantChat(userId: string, messages: AssistantChatMessage[]): void {
try {
const trimmed = messages.slice(-MAX_MESSAGES);
if (trimmed.length === 0) {
localStorage.removeItem(storageKey(userId));
return;
}
localStorage.setItem(storageKey(userId), JSON.stringify(trimmed));
} catch {
// Private browsing, quota exceeded, or disabled storage.
}
}
export function clearAssistantChat(userId: string): void {
try {
localStorage.removeItem(storageKey(userId));
} catch {
// Ignore storage errors.
}
}