feat: RAG inline citations, source highlighting, and admin panel
- Increase RAG retrieval from 5 to 50 docs with relevance threshold (0.15) - LLM inline citations with [来源N] format and reference list - Clickable citation links scroll to source cards with highlight animation - Source previews with full chunk text and answer-matched highlighting - Message-scoped source IDs to fix cross-round citation targeting - Admin panel pages (knowledge, users, forum, course, settings) - Add rehype-raw dependency for HTML-in-markdown rendering Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -583,6 +583,9 @@ export const chatAPI = {
|
||||
} else if (data.type === "chunk") {
|
||||
console.log("[DEBUG-STREAM] 接收chunk:", data.content);
|
||||
onChunk?.(data.content);
|
||||
} else if (data.type === "sources") {
|
||||
console.log("[DEBUG-STREAM] 收到sources:", data.sources?.length, "个来源");
|
||||
onChunk?.(JSON.stringify(data));
|
||||
} else if (data.type === "done") {
|
||||
console.log("[DEBUG-STREAM] 流式完成, session_id:", data.session_id, "message_id:", data.message_id, "user_message_id:", data.user_message_id);
|
||||
onComplete?.(data.session_id, data.message_id, data.user_message_id);
|
||||
@@ -1067,3 +1070,129 @@ export const bookAPI = {
|
||||
return `${API_BASE_URL}/books/${bookId}/file`;
|
||||
},
|
||||
};
|
||||
|
||||
// ===== 后台管理 API =====
|
||||
export const adminAPI = {
|
||||
// 仪表盘
|
||||
async getDashboard() {
|
||||
return apiRequest<{
|
||||
total_users: number;
|
||||
active_users_7d: number;
|
||||
total_sessions: number;
|
||||
total_messages: number;
|
||||
total_documents: number;
|
||||
total_knowledge_bases: number;
|
||||
total_forum_posts: number;
|
||||
total_forum_replies: number;
|
||||
total_generated_images: number;
|
||||
}>("/admin/dashboard");
|
||||
},
|
||||
|
||||
async getUserTrends(days = 30) {
|
||||
return apiRequest<{ date: string; count: number }[]>(`/admin/trends/users?days=${days}`);
|
||||
},
|
||||
|
||||
async getMessageTrends(days = 30) {
|
||||
return apiRequest<{ date: string; count: number }[]>(`/admin/trends/messages?days=${days}`);
|
||||
},
|
||||
|
||||
// 用户管理
|
||||
async listUsers(skip = 0, limit = 50) {
|
||||
return apiRequest<{
|
||||
id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
full_name: string | null;
|
||||
is_active: boolean;
|
||||
is_superuser: boolean;
|
||||
created_at: string | null;
|
||||
last_login: string | null;
|
||||
session_count: number;
|
||||
message_count: number;
|
||||
}[]>(`/admin/users?skip=${skip}&limit=${limit}`);
|
||||
},
|
||||
|
||||
async toggleUserActive(userId: number) {
|
||||
return apiRequest<{ success: boolean; is_active: boolean }>(`/admin/users/${userId}/toggle-active`, { method: "PUT" });
|
||||
},
|
||||
|
||||
async toggleUserAdmin(userId: number) {
|
||||
return apiRequest<{ success: boolean; is_superuser: boolean }>(`/admin/users/${userId}/toggle-admin`, { method: "PUT" });
|
||||
},
|
||||
|
||||
async deleteUser(userId: number) {
|
||||
return apiRequest<{ success: boolean }>(`/admin/users/${userId}`, { method: "DELETE" });
|
||||
},
|
||||
|
||||
// 论坛管理
|
||||
async listForumCategories() {
|
||||
return apiRequest<{
|
||||
id: number;
|
||||
slug: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
post_count: number;
|
||||
}[]>("/admin/forum/categories");
|
||||
},
|
||||
|
||||
async createForumCategory(data: { name: string; slug: string; description?: string }) {
|
||||
return apiRequest("/admin/forum/categories", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
},
|
||||
|
||||
async updateForumCategory(categoryId: number, data: { name: string; slug: string; description?: string }) {
|
||||
return apiRequest(`/admin/forum/categories/${categoryId}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
},
|
||||
|
||||
async deleteForumCategory(categoryId: number) {
|
||||
return apiRequest<{ success: boolean }>(`/admin/forum/categories/${categoryId}`, { method: "DELETE" });
|
||||
},
|
||||
|
||||
async listForumPosts(skip = 0, limit = 50) {
|
||||
return apiRequest<{
|
||||
id: number;
|
||||
title: string;
|
||||
author_name: string;
|
||||
category_name: string;
|
||||
reply_count: number;
|
||||
created_at: string;
|
||||
}[]>(`/admin/forum/posts?skip=${skip}&limit=${limit}`);
|
||||
},
|
||||
|
||||
async deleteForumPost(postId: number) {
|
||||
return apiRequest<{ success: boolean }>(`/admin/forum/posts/${postId}`, { method: "DELETE" });
|
||||
},
|
||||
|
||||
// 知识库管理
|
||||
async listKnowledgeBases() {
|
||||
return apiRequest<{
|
||||
id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
owner_name: string;
|
||||
is_system: boolean;
|
||||
document_count: number;
|
||||
chunk_count: number;
|
||||
created_at: string;
|
||||
}[]>("/admin/knowledge-bases");
|
||||
},
|
||||
|
||||
async deleteKnowledgeBase(kbId: number) {
|
||||
return apiRequest<{ success: boolean }>(`/admin/knowledge-bases/${kbId}`, { method: "DELETE" });
|
||||
},
|
||||
|
||||
// 系统状态
|
||||
async getSystemStatus() {
|
||||
return apiRequest<{
|
||||
database: { status: string };
|
||||
vector_store: { status: string; vector_count: number };
|
||||
llm_model: string;
|
||||
embedding_model: string;
|
||||
}>("/admin/system/status");
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user