From 0d60d1a0f0b5d5ed168e1d8cf341567b1a388964 Mon Sep 17 00:00:00 2001 From: Siz Long Date: Wed, 18 Feb 2026 23:39:45 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E4=B8=8D=E5=B0=86=E5=85=A8=E9=87=8F?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=9C=A8=E4=B8=80=E5=BC=80=E5=A7=8B=E5=B0=B1?= =?UTF-8?q?=E7=BB=99Bot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/chat/route.ts | 48 ++++++++++++++++++++++++++++++++ app/components/DocsAssistant.tsx | 1 - app/docs/[...slug]/page.tsx | 42 ++-------------------------- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index ed2ba00..e44df2a 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,6 +1,9 @@ import { streamText, UIMessage, convertToModelMessages } from "ai"; import { getModel, requiresApiKey, type AIProvider } from "@/lib/ai/models"; import { buildSystemMessage } from "@/lib/ai/prompt"; +import { source } from "@/lib/source"; +import fs from "fs/promises"; +import path from "path"; // 流式响应最长30秒 export const maxDuration = 30; @@ -39,6 +42,26 @@ export async function POST(req: Request) { ); } + // 如果有 slug 但没有 content,尝试在服务端读取内容 + if (pageContext?.slug && !pageContext.content) { + try { + const slugArray = pageContext.slug.split("/"); + const page = source.getPage(slugArray); + + if (page) { + const fullFilePath = path.join(process.cwd(), "app/docs", page.path); + const rawContent = await fs.readFile(fullFilePath, "utf-8"); + pageContext.content = extractTextFromMDX(rawContent); + } + } catch (error) { + console.warn( + `Failed to fetch content for slug ${pageContext.slug}:`, + error, + ); + // 出错时不中断,只是缺少上下文 + } + } + // 构建系统消息,包含页面上下文 const systemMessage = buildSystemMessage(system, pageContext); @@ -67,3 +90,28 @@ export async function POST(req: Request) { ); } } + +// 提取纯文本内容,过滤掉 MDX 语法 +function extractTextFromMDX(content: string): string { + let text = content + .replace(/^---[\s\S]*?---/m, "") // 移除头部元数据 (frontmatter) + .replace(/```[\s\S]*?```/g, "") // 移除代码块 + .replace(/`([^`]+)`/g, "$1"); // 移除内联代码符号,保留内容 + + // 递归移除 HTML/MDX 标签,防止嵌套标签清理不干净 + let prevText; + do { + prevText = text; + text = text.replace(/<[^>]+>/g, ""); + } while (text !== prevText); + + return text + .replace(/\*\*([^*]+)\*\*/g, "$1") // 移除粗体符号,保留文字 + .replace(/\*([^*]+)\*/g, "$1") // 移除斜体符号,保留文字 + .replace(/#{1,6}\s+/g, "") // 移除标题符号 (#) + .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") // 移除链接语法,仅保留链接文本 + .replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1") // 移除图片语法,保留 alt 文本 + .replace(/[#*`()[!\]!]/g, "") // 移除剩余的常用 Markdown 符号 + .replace(/\n{2,}/g, "\n") // 规范化换行,将多余的空行合并 + .trim(); +} diff --git a/app/components/DocsAssistant.tsx b/app/components/DocsAssistant.tsx index 139f98a..91459cf 100644 --- a/app/components/DocsAssistant.tsx +++ b/app/components/DocsAssistant.tsx @@ -15,7 +15,6 @@ import { interface PageContext { title?: string; description?: string; - content?: string; slug?: string; } diff --git a/app/docs/[...slug]/page.tsx b/app/docs/[...slug]/page.tsx index 7964685..64c62e2 100644 --- a/app/docs/[...slug]/page.tsx +++ b/app/docs/[...slug]/page.tsx @@ -14,31 +14,8 @@ import { Contributors } from "@/app/components/Contributors"; import { DocsAssistant } from "@/app/components/DocsAssistant"; import { LicenseNotice } from "@/app/components/LicenseNotice"; import { PageFeedback } from "@/app/components/PageFeedback"; -import fs from "fs/promises"; -import path from "path"; - -// Extract clean text content from MDX -function extractTextFromMDX(content: string): string { - let text = content - .replace(/^---[\s\S]*?---/m, "") // Remove frontmatter - .replace(/```[\s\S]*?```/g, "") // Remove code blocks - .replace(/`([^`]+)`/g, "$1"); // Remove inline code - // Remove HTML/MDX tags recursively to prevent incomplete multi-character sanitization - let prevText; - do { - prevText = text; - text = text.replace(/<[^>]+>/g, ""); - } while (text !== prevText); - return text - .replace(/\*\*([^*]+)\*\*/g, "$1") // Remove bold - .replace(/\*([^*]+)\*/g, "$1") // Remove italic - .replace(/#{1,6}\s+/g, "") // Remove headers - .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") // Remove links, keep text - .replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1") // Remove images, keep alt text - .replace(/[#*`()[!\]!]/g, "") // Remove common markdown symbols - .replace(/\n{2,}/g, "\n") // Normalize line breaks - .trim(); -} +// Extract clean text content from MDX - no longer used on client/page side +// content fetching moved to API route for performance interface Param { params: Promise<{ @@ -67,20 +44,6 @@ export default async function DocPage({ params }: Param) { getDocContributorsByDocId(docIdFromPage); const Mdx = page.data.body; - // Prepare page content for AI assistant - let pageContentForAI = ""; - try { - const fullFilePath = path.join(process.cwd(), "app/docs", page.file.path); - const rawContent = await fs.readFile(fullFilePath, "utf-8"); - const extractedText = extractTextFromMDX(rawContent); - // Use full extracted content without truncation - pageContentForAI = extractedText; - } catch (error) { - console.warn("Failed to read file content for AI assistant:", error); - // Fallback to using page metadata - pageContentForAI = `${page.data.title}\n${page.data.description || ""}`; - } - return ( <> @@ -104,7 +67,6 @@ export default async function DocPage({ params }: Param) { pageContext={{ title: page.data.title, description: page.data.description, - content: pageContentForAI, slug: slug?.join("/"), }} /> From 81cdd9124b8735b20f170d9576b2476bc4f02ee2 Mon Sep 17 00:00:00 2001 From: Siz Long Date: Wed, 18 Feb 2026 23:50:15 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E7=94=A8=E6=88=B7=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=E7=8B=AC=E7=AB=8B=E5=8F=82=E6=95=B0=E4=BC=A0?= =?UTF-8?q?=E9=80=92=EF=BC=8C=E4=B8=8D=E4=BC=9A=E8=A2=AB=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E4=B8=BA=E6=A0=BC=E5=BC=8F=E5=8C=96=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/chat/route.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index e44df2a..e570f1f 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -55,7 +55,8 @@ export async function POST(req: Request) { } } catch (error) { console.warn( - `Failed to fetch content for slug ${pageContext.slug}:`, + "Failed to fetch content for slug:", + pageContext.slug, error, ); // 出错时不中断,只是缺少上下文