from datetime import datetime from typing import Any from pydantic import BaseModel, Field, model_validator class ChildConversationItem(BaseModel): conversation_id: int conversation_type: int conversation_type_name: str peer_type: str peer_id: str peer_name: str | None = None last_message_preview: str | None = None last_message_at: datetime | None = None message_count: int class ChildConversationListResponse(BaseModel): items: list[ChildConversationItem] total: int next_cursor: int | None = None class ChildConversationMessageItem(BaseModel): id: int conversation_id: int seq: int sender_type: str sender_id: str receiver_type: str receiver_id: str content_type: int content_text: str | None = None content_json: dict[str, Any] | None = None media_file_key: str | None = None media_duration_ms: int | None = None media_mime_type: str | None = None media_size_bytes: int | None = None media_transcript_text: str | None = None client_msg_id: str | None = None sender_name_snapshot: str | None = None sender_avatar_snapshot: str | None = None receiver_name_snapshot: str | None = None receiver_avatar_snapshot: str | None = None ext_json: dict[str, Any] | None = None created_at: datetime class ChildConversationMessageListResponse(BaseModel): conversation_id: int has_more: bool next_cursor_seq: int | None = None items: list[ChildConversationMessageItem] class BaseConversationMessageCreateRequest(BaseModel): content_type: int = Field(ge=1, le=4) content_text: str | None = None content_json: dict[str, Any] | None = None media_file_key: str | None = None media_duration_ms: int | None = Field(default=None, ge=0) media_mime_type: str | None = None media_size_bytes: int | None = Field(default=None, ge=0) media_transcript_text: str | None = None client_msg_id: str = Field(min_length=1, max_length=64) ext_json: dict[str, Any] | None = None @model_validator(mode="after") def validate_message_payload(self) -> "BaseConversationMessageCreateRequest": if self.content_type == 1 and not self.content_text: raise ValueError("content_text is required when content_type=1") if self.content_type == 2 and not self.media_file_key: raise ValueError("media_file_key is required when content_type=2") if self.content_type == 3 and not self.media_file_key: raise ValueError("media_file_key is required when content_type=3") if self.content_type == 4 and self.content_json is None: raise ValueError("content_json is required when content_type=4") return self class ParentChildMessageCreateRequest(BaseConversationMessageCreateRequest): pass class DeviceMessageCreateRequest(BaseConversationMessageCreateRequest): conversation_type: int = Field(ge=1, le=2) peer_child_id: int | None = Field(default=None, ge=1) parent_user_id: int | None = Field(default=None, ge=1) @model_validator(mode="after") def validate_target(self) -> "DeviceMessageCreateRequest": if self.conversation_type == 1: if self.peer_child_id is None: raise ValueError("peer_child_id is required when conversation_type=1") if self.parent_user_id is not None: raise ValueError("parent_user_id must be empty when conversation_type=1") if self.conversation_type == 2: if self.parent_user_id is None: raise ValueError("parent_user_id is required when conversation_type=2") if self.peer_child_id is not None: raise ValueError("peer_child_id must be empty when conversation_type=2") return self class ConversationMessageCreateResponse(BaseModel): idempotent: bool conversation_id: int conversation_type: int conversation_type_name: str message: ChildConversationMessageItem