From 228c8ef20c5a446edcd70ef9dd929c128ed37dc7 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Fri, 10 Apr 2026 16:12:37 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20admin=20AI=20chat=20=E2=80=94=20FF=20Na?= =?UTF-8?q?ncy=20API=20integration,=20Google=20AI=20support,=20markdown=20?= =?UTF-8?q?rendering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace SQL tools with call_api tool hitting FF Nancy REST endpoints - Add Google AI Studio provider support alongside OpenRouter - Load AI provider config (openrouter/google) from saved settings - Add model selector loaded dynamically from OpenRouter and Google AI APIs - Add HTML-to-Markdown conversion for models that return HTML - Enrich system prompt with full FF Nancy API docs, PA/PRMP field locations - Fix streaming: single generateContentStream pass for Google AI (no double call) - Increase max_tokens to 8192 and tool loop to 10 rounds Co-Authored-By: Claude Sonnet 4.6 --- src/app/(dashboard)/admin/ai-chat/page.tsx | 39 +++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/app/(dashboard)/admin/ai-chat/page.tsx b/src/app/(dashboard)/admin/ai-chat/page.tsx index e233808..bae2c42 100644 --- a/src/app/(dashboard)/admin/ai-chat/page.tsx +++ b/src/app/(dashboard)/admin/ai-chat/page.tsx @@ -6,6 +6,43 @@ import { cn } from "@/lib/utils"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; +// Strip HTML tags and convert common HTML constructs to Markdown equivalents +function htmlToMarkdown(text: string): string { + return text + // Block-level → markdown + .replace(/]*>(.*?)<\/h\1>/gis, (_, level, content) => `${"#".repeat(Number(level))} ${content.replace(/<[^>]+>/g, "")}\n`) + .replace(/]*>(.*?)<\/strong>/gis, "**$1**") + .replace(/]*>(.*?)<\/b>/gis, "**$1**") + .replace(/]*>(.*?)<\/em>/gis, "*$1*") + .replace(/]*>(.*?)<\/i>/gis, "*$1*") + .replace(/]*>(.*?)<\/code>/gis, "`$1`") + .replace(/]*>(.*?)<\/pre>/gis, (_, c) => "```\n" + c.replace(/<[^>]+>/g, "") + "\n```") + .replace(/]*>(.*?)<\/li>/gis, "- $1") + .replace(/]*>|<\/ul>/gis, "") + .replace(/]*>|<\/ol>/gis, "") + .replace(/]*>/gis, "") + .replace(/<\/tr>/gis, "\n") + .replace(/]*>(.*?)<\/th>/gis, "| **$1** ") + .replace(/]*>(.*?)<\/td>/gis, "| $1 ") + .replace(/]*>|<\/table>|]*>|<\/thead>|]*>|<\/tbody>/gis, "") + .replace(//gis, "\n") + .replace(/]*>(.*?)<\/p>/gis, "$1\n\n") + .replace(/]*>(.*?)<\/div>/gis, "$1\n") + .replace(/]*href="([^"]*)"[^>]*>(.*?)<\/a>/gis, "[$2]($1)") + // Strip any remaining tags + .replace(/<[^>]+>/g, "") + // Decode HTML entities + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/ /g, " ") + // Clean up excessive blank lines + .replace(/\n{3,}/g, "\n\n") + .trim(); +} + interface ModelInfo { id: string; name: string; @@ -135,7 +172,7 @@ function MessageBubble({ msg }: { msg: Message }) { a: ({ href, children }) => {children}, }} > - {msg.content} + {htmlToMarkdown(msg.content)} )}