// Shared helper functions for chat route // Exported for testing // File upload limits (must match client-side) export const MAX_FILE_SIZE = 2 * 1024 * 1024 // 2MB export const MAX_FILES = 5 // Helper function to validate file parts in messages export function validateFileParts(messages: any[]): { valid: boolean error?: string } { const lastMessage = messages[messages.length - 1] const fileParts = lastMessage?.parts?.filter((p: any) => p.type === "file") || [] if (fileParts.length > MAX_FILES) { return { valid: false, error: `Too many files. Maximum ${MAX_FILES} allowed.`, } } for (const filePart of fileParts) { // Data URLs format: data:image/png;base64, // Base64 increases size by ~33%, so we check the decoded size if (filePart.url?.startsWith("data:")) { const base64Data = filePart.url.split(",")[1] if (base64Data) { const sizeInBytes = Math.ceil((base64Data.length * 3) / 4) if (sizeInBytes > MAX_FILE_SIZE) { return { valid: false, error: `File exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`, } } } } } return { valid: true } } // Helper function to check if diagram is minimal/empty export function isMinimalDiagram(xml: string): boolean { const stripped = xml.replace(/\s/g, "") return !stripped.includes('id="2"') } // Helper function to replace historical tool call XML with placeholders // This reduces token usage and forces LLM to rely on the current diagram XML (source of truth) // Also fixes invalid/undefined inputs from interrupted streaming export function replaceHistoricalToolInputs(messages: any[]): any[] { return messages.map((msg) => { if (msg.role !== "assistant" || !Array.isArray(msg.content)) { return msg } const replacedContent = msg.content .map((part: any) => { if (part.type === "tool-call") { const toolName = part.toolName // Fix invalid/undefined inputs from interrupted streaming if ( !part.input || typeof part.input !== "object" || Object.keys(part.input).length === 0 ) { // Skip tool calls with invalid inputs entirely return null } if ( toolName === "display_diagram" || toolName === "edit_diagram" ) { return { ...part, input: { placeholder: "[XML content replaced - see current diagram XML in system context]", }, } } } return part }) .filter(Boolean) // Remove null entries (invalid tool calls) return { ...msg, content: replacedContent } }) }