feat(ai): V2 深度增强 — SSE 流式/全局助手/内容安全/多角色覆盖

对标 Khanmigo/Duolingo Max/Squirrel AI/Century Tech 实现:

- SSE 流式响应:createAiChatCompletionStream AsyncGenerator + /api/ai/chat/stream SSE 端点 + useAiChatStream hook(AbortController 停止生成 + localStorage 持久化)

- Markdown 渲染:AiMarkdownRenderer(react-markdown + remark-gfm + 代码块/表格/列表 + hover 复制按钮)

- 全局 AI 助手:AiAssistantWidget 浮动按钮 + Sheet 侧抽屉 + usePathname 路由推断上下文(7 类场景系统提示)+ dashboard layout 全局注入 AiClientProvider

- 内容安全:content-safety.ts 多层过滤(输入/输出安全过滤 + 每日限制 student 50/teacher 200/parent 30/admin 500 + 学生苏格拉底模式),COPPA/FERPA K12 合规

- 多角色 AI 覆盖:家长端 AiChildSummary(学情摘要)+ 管理员端 AiUsageDashboard(使用监控)+ 学生端 AiStudyPath(个性化学习路径)

- i18n 修复:8 处错误键引用 + zh-CN/en ai.json 全面扩展

- 架构文档 004/005 同步更新
This commit is contained in:
SpecialX
2026-06-23 01:34:37 +08:00
parent a60105455e
commit 4da9194a5e
27 changed files with 3522 additions and 172 deletions

View File

@@ -65,3 +65,31 @@ export const createAiChatCompletion = async (input: AiChatRequest) => {
const usage = "usage" in result ? result.usage ?? null : null
return { content, usage }
}
/**
* 流式 AI 聊天补全
*
* 返回 AsyncGenerator逐 token 产出内容。
* 用于 SSE 流式响应,降低用户感知延迟。
*/
export async function* createAiChatCompletionStream(
input: AiChatRequest
): AsyncGenerator<string, void, unknown> {
const config = await getAiProviderConfig(input.providerId)
const client = await getAiClient(config)
const stream = await client.chat.completions.create({
model: config.model || input.model,
messages: input.messages,
temperature: input.temperature,
...(typeof input.maxTokens === "number" ? { max_tokens: input.maxTokens } : {}),
stream: true,
})
for await (const chunk of stream) {
const delta = chunk.choices?.[0]?.delta
const content = extractMessageContent(delta)
if (content) {
yield content
}
}
}

View File

@@ -1,5 +1,5 @@
export { encryptAiApiKey, decryptAiApiKey } from "./api-key-crypto"
export { createAiChatCompletion, testAiProviderById, testAiProviderConfig } from "./client"
export { createAiChatCompletion, createAiChatCompletionStream, testAiProviderById, testAiProviderConfig } from "./client"
export { getAiErrorMessage } from "./errors"
export { parseAiChatPayload, isRecord } from "./payload-parser"
export type { AiChatRequest, ChatMessage, ChatRole } from "./payload-parser"

View File

@@ -60,6 +60,7 @@ export type EventName =
| "homework.auto_save_failed"
// AI 模块监控事件
| "ai.chat"
| "ai.chat_stream"
| "ai.similar_question"
| "ai.grading_assist"
| "ai.lesson_content"