from enum import Enum from typing import List, Optional from pydantic import BaseModel class ModelType(str, Enum): """模型类型枚举""" HOWCUTEAMI = "howcuteami" DEEPFACE = "deepface" HYBRID = "hybrid" # 混合模式:颜值性别用howcuteami,年龄情绪用deepface class ImageScoreItem(BaseModel): file_path: str score: float is_cropped_face: bool = False size_bytes: int size_str: str last_modified: str nickname: Optional[str] = None hostname: Optional[str] = None region: Optional[str] = None region_country: Optional[str] = None class SearchRequest(BaseModel): keyword: Optional[str] = "" searchType: Optional[str] = "face" top_k: Optional[int] = 5 score_threshold: float = 0.0 nickname: Optional[str] = None region: Optional[str] = None platform: Optional[str] = None class ImageSearchRequest(BaseModel): image: Optional[str] = None # base64编码的图片 searchType: Optional[str] = "face" top_k: Optional[int] = 5 score_threshold: float = 0.0 nickname: Optional[str] = None class ImageFileList(BaseModel): results: List[ImageScoreItem] count: int class PagedImageFileList(BaseModel): results: List[ImageScoreItem] count: int page: int page_size: int total_pages: int class CelebrityMatchResponse(BaseModel): filename: str display_name: Optional[str] = None distance: float similarity: float confidence: float face_filename: Optional[str] = None class CategoryStatItem(BaseModel): category: str display_name: str count: int class CategoryStatsResponse(BaseModel): stats: List[CategoryStatItem] total: int class RegionStatItem(BaseModel): region: str region_country: str count: int class RegionStatsResponse(BaseModel): stats: List[RegionStatItem] total: int class UserDeviceItem(BaseModel): device_id: str device_type: Optional[str] = None device_model: Optional[str] = None os_version: Optional[str] = None app_version: Optional[str] = None timezone: Optional[str] = None region: Optional[str] = None region_country: Optional[str] = None language: Optional[str] = None detection_count: int = 0 created_at: str updated_at: str class UserDeviceListResponse(BaseModel): results: List[UserDeviceItem] count: int page: int page_size: int total_pages: int class RankResponse(BaseModel): success: bool = True score: float region_code: Optional[str] = None region_name: Optional[str] = None region_rank: int region_total: int region_percentile: float global_rank: int global_total: int global_percentile: float days_limit: int = 90 class LeaderboardItem(BaseModel): record_id: int file_path: str = "" rank: int score: float nickname: Optional[str] = None display_name: str = "" region_code: Optional[str] = None region_name: Optional[str] = None gender: Optional[str] = None age: Optional[str] = None avatar_id: int created_at: Optional[str] = None updated_at: Optional[str] = None is_current_user: bool = False class LeaderboardResponse(BaseModel): success: bool = True region_code: Optional[str] = None region_name: Optional[str] = None total_records: int items: List[LeaderboardItem] days_limit: int = 90