89 lines
3.2 KiB
Python
89 lines
3.2 KiB
Python
|
|
"""
|
||
|
|
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"
|
||
|
|
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")
|