// File: frontend/app/chat/page.tsx (更新以使用 API) // Description: 对接后端 API 实现助手和会话的加载与管理 "use client"; import React, { useState, useRef, useEffect, useCallback } from "react"; import { SendHorizontal, Loader2, PanelRightOpen, PanelRightClose, UserPlus, Settings2, Trash2, Edit, RefreshCw, } from "lucide-react"; // 添加刷新图标 import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; // Shadcn UI Components import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, DialogFooter, DialogClose, } from "@/components/ui/dialog"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Slider } from "@/components/ui/slider"; import { Toaster, toast } from "sonner"; import { Skeleton } from "@/components/ui/skeleton"; // 导入骨架屏 // API 函数和类型 import { sendChatMessage, getAssistants, createAssistant, updateAssistant, deleteAssistant, getSessionsByAssistant, deleteSession, getMessagesBySession, Session, ChatApiResponse, Message as ApiMessage, } from "@/lib/api"; // 确保路径正确 import { Assistant, AssistantCreateData, AssistantUpdateData, } from "@/types/assistant"; // --- Frontend specific Message type (includes optional isError) --- interface Message extends ApiMessage { // Extend the type from API isError?: boolean; // Optional flag for frontend error styling } interface ChatSession { id: string; title: string; createdAt: Date; assistantId: string; isTemporary?: boolean; } // --- Zod Schema for Assistant Form Validation --- const assistantFormSchema = z.object({ name: z .string() .min(1, { message: "助手名称不能为空" }) .max(50, { message: "名称过长" }), description: z.string().max(200, { message: "描述过长" }).optional(), avatar: z.string().max(5, { message: "头像/Emoji 过长" }).optional(), // 简单限制长度 system_prompt: z .string() .min(1, { message: "系统提示不能为空" }) .max(4000, { message: "系统提示过长" }), model: z.string({ required_error: "请选择一个模型" }), temperature: z.number().min(0).max(1), }); type AssistantFormData = z.infer; // 可选的模型列表 const availableModels = [ { value: "gpt-3.5-turbo", label: "GPT-3.5 Turbo" }, { value: "gpt-4", label: "GPT-4" }, { value: "gpt-4-turbo", label: "GPT-4 Turbo" }, { value: "gemini-2.0-flash", label: "Gemini 2.0 Flash" }, { value: "deepseek-coder", label: "DeepSeek Coder" }, // 示例 // 添加更多模型... ]; // --- Helper Function --- const findLastSession = ( sessions: ChatSession[], assistantId: string ): ChatSession | undefined => { return sessions .filter((s) => s.assistantId === assistantId && !s.isTemporary) .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())[0]; }; // --- Assistant Form Component --- interface AssistantFormProps { assistant?: Assistant | null; // 传入表示编辑,否则是创建 onSave: (data: AssistantFormData, id?: string) => void; // 保存回调 onClose: () => void; // 关闭 Dialog 的回调 } function AssistantForm({ assistant, onSave, onClose }: AssistantFormProps) { const form = useForm({ resolver: zodResolver(assistantFormSchema), defaultValues: { name: assistant?.name || "", description: assistant?.description || "", avatar: assistant?.avatar || "", system_prompt: assistant?.system_prompt || "", model: assistant?.model || availableModels[0].value, // 默认第一个模型 temperature: assistant?.temperature ?? 0.7, // 默认 0.7 }, }); const [isSaving, setIsSaving] = useState(false); // 添加保存状态 async function onSubmit(data: AssistantFormData) { setIsSaving(true); try { await onSave(data, assistant?.id); // 调用异步保存函数 onClose(); // 成功后关闭 } catch (error) { // 错误已在 onSave 中处理并 toast } finally { setIsSaving(false); } } return (
{/* Name */} ( 助手名称 )} /> {/* Description */} ( 描述 (可选) )} /> {/* Avatar */} ( 头像 (可选) 建议使用单个 Emoji。 )} /> {/* System Prompt */} ( 系统提示 (System Prompt)