"""会话和消息相关的 Schema""" import uuid import datetime from typing import Optional, Dict, Any, List from pydantic import BaseModel, Field, ConfigDict, field_serializer, model_serializer # 导入 FileInput(用于体验运行) from app.schemas.app_schema import FileInput # ---------- Input Schemas ---------- class ConversationCreate(BaseModel): """创建会话请求""" title: Optional[str] = Field(None, max_length=255, description="会话标题") user_id: Optional[str] = Field(None, description="用户ID(外部系统)") class MessageCreate(BaseModel): """创建消息请求""" content: str = Field(..., description="消息内容") variables: Optional[Dict[str, Any]] = Field(None, description="变量参数") class ChatRequest(BaseModel): """聊天请求(基于 share_token)""" message: str = Field(..., description="用户消息") conversation_id: Optional[uuid.UUID] = Field(None, description="会话ID(多轮对话)") user_id: Optional[str] = Field(None, description="用户ID(外部系统)") variables: Optional[Dict[str, Any]] = Field(None, description="变量参数") stream: bool = Field(default=False, description="是否流式返回") web_search: bool = Field(default=False, description="是否启用网络搜索") memory: bool = Field(default=True, description="是否启用记忆功能") thinking: bool = Field(default=False, description="是否启用深度思考(需Agent配置支持)") files: List[FileInput] = Field(default_factory=list, description="附件列表(支持多文件)") # ---------- Output Schemas ---------- class Message(BaseModel): """消息输出""" model_config = ConfigDict(from_attributes=True) id: uuid.UUID conversation_id: uuid.UUID role: str content: str meta_data: Optional[Dict[str, Any]] = None created_at: datetime.datetime @field_serializer("created_at", when_used="json") def _serialize_created_at(self, dt: datetime.datetime): return int(dt.timestamp() * 1000) if dt else None @field_serializer("meta_data", when_used="json") def _serialize_meta_data(self, data: Optional[Dict[str, Any]]): return data or {} class Conversation(BaseModel): """会话输出""" model_config = ConfigDict(from_attributes=True) id: uuid.UUID app_id: uuid.UUID workspace_id: uuid.UUID user_id: Optional[str] = None title: Optional[str] = None summary: Optional[str] = None is_draft: bool message_count: int is_active: bool created_at: datetime.datetime updated_at: datetime.datetime @field_serializer("created_at", when_used="json") def _serialize_created_at(self, dt: datetime.datetime): return int(dt.timestamp() * 1000) if dt else None @field_serializer("updated_at", when_used="json") def _serialize_updated_at(self, dt: datetime.datetime): return int(dt.timestamp() * 1000) if dt else None class ConversationWithMessages(Conversation): """会话详情(包含消息列表)""" messages: List[Message] = [] class ChatResponse(BaseModel): """聊天响应(非流式)""" conversation_id: uuid.UUID message: str message_id: str usage: Optional[Dict[str, Any]] = None elapsed_time: Optional[float] = None reasoning_content: Optional[str] = None suggested_questions: Optional[List[str]] = None citations: Optional[List[Dict[str, Any]]] = None audio_url: Optional[str] = None audio_status: Optional[str] = None @model_serializer(mode="wrap") def _serialize(self, handler): data = handler(self) if not data.get("reasoning_content"): data.pop("reasoning_content", None) return data # ---------- Conversation Summary Schemas ---------- class ConversationOut(BaseModel): theme: str question: list[str] summary: str takeaways: list[str] info_score: int