""" Data models for AI Service. [AC-AISVC-02] Request/Response models aligned with OpenAPI contract. [AC-AISVC-13] Entity models for database persistence. """ from enum import Enum from typing import Any from pydantic import BaseModel, Field class ChannelType(str, Enum): WECHAT = "wechat" DOUYIN = "douyin" JD = "jd" class Role(str, Enum): USER = "user" ASSISTANT = "assistant" class ChatMessage(BaseModel): role: Role = Field(..., description="Message role: user or assistant") content: str = Field(..., description="Message content") class ChatRequest(BaseModel): session_id: str = Field(..., alias="sessionId", description="Session ID for conversation tracking") current_message: str = Field(..., alias="currentMessage", description="Current user message") channel_type: ChannelType = Field(..., alias="channelType", description="Channel type: wechat, douyin, jd") history: list[ChatMessage] | None = Field(default=None, description="Optional conversation history") metadata: dict[str, Any] | None = Field(default=None, description="Optional metadata") model_config = {"populate_by_name": True} class ChatResponse(BaseModel): reply: str = Field(..., description="AI generated reply content") confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence score between 0.0 and 1.0") should_transfer: bool = Field(..., alias="shouldTransfer", description="Whether to suggest transfer to human agent") transfer_reason: str | None = Field(default=None, alias="transferReason", description="Reason for transfer suggestion") metadata: dict[str, Any] | None = Field(default=None, description="Response metadata") model_config = {"populate_by_name": True} class ErrorCode(str, Enum): INVALID_REQUEST = "INVALID_REQUEST" MISSING_TENANT_ID = "MISSING_TENANT_ID" INVALID_TENANT_ID = "INVALID_TENANT_ID" INTERNAL_ERROR = "INTERNAL_ERROR" SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE" TIMEOUT = "TIMEOUT" LLM_ERROR = "LLM_ERROR" RETRIEVAL_ERROR = "RETRIEVAL_ERROR" class ErrorResponse(BaseModel): code: str = Field(..., description="Error code") message: str = Field(..., description="Error message") details: list[dict[str, Any]] | None = Field(default=None, description="Detailed error information") class SSEEventType(str, Enum): MESSAGE = "message" FINAL = "final" ERROR = "error" class SSEMessageEvent(BaseModel): delta: str = Field(..., description="Incremental text content") class SSEFinalEvent(BaseModel): reply: str = Field(..., description="Complete AI reply") confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence score") should_transfer: bool = Field(..., alias="shouldTransfer", description="Transfer suggestion") transfer_reason: str | None = Field(default=None, alias="transferReason", description="Transfer reason") metadata: dict[str, Any] | None = Field(default=None, description="Response metadata") model_config = {"populate_by_name": True} class SSEErrorEvent(BaseModel): code: str = Field(..., description="Error code") message: str = Field(..., description="Error message") details: list[dict[str, Any]] | None = Field(default=None, description="Error details")