Spaces:
Running
Running
Download models.py from ZHIWEI666/ComfyUI-Ranking-API: direct link, hf CLI and curl.
- Browser
- Download file 12.5 kB
-
https://huggingface.co/spaces/ZHIWEI666/ComfyUI-Ranking-API/resolve/main/models.py
- Command line
-
hf download hf://spaces/ZHIWEI666/ComfyUI-Ranking-API/models.py
-
curl -L -o models.py https://huggingface.co/spaces/ZHIWEI666/ComfyUI-Ranking-API/resolve/main/models.py
12.5 kB
| # models.py | |
| import re | |
| from pydantic import BaseModel, Field, field_validator # 🧹 P2修复:pydantic v2 已废弃 v1 风格 validator | |
| from typing import Optional, List, Literal | |
| from profile_media import normalize_profile_media | |
| def _validate_email_format(email: str) -> str: | |
| """邮箱格式校验""" | |
| if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email): | |
| raise ValueError('邮箱格式不正确') | |
| return email | |
| def _validate_password_length(password: str) -> str: | |
| """密码长度校验:6-64字符(兼容旧用户短密码)""" | |
| if len(password) < 6: | |
| raise ValueError('密码长度不能少于6个字符') | |
| if len(password) > 64: | |
| raise ValueError('密码长度不能超过64个字符') | |
| return password | |
| class SendCodeRequest(BaseModel): | |
| contact: str = Field(..., min_length=1, max_length=254) | |
| contact_type: Literal["email", "phone"] | |
| action_type: Literal["register", "reset", "recover", "withdraw"] | |
| account: Optional[str] = None | |
| class UserRegister(BaseModel): | |
| account: str | |
| password: str | |
| email: str | |
| phone: Optional[str] = "" | |
| name: str | |
| gender: str | |
| code: str | |
| avatarDataUrl: Optional[str] = None | |
| age: Optional[int] = None | |
| country: Optional[str] = None | |
| region: Optional[str] = None | |
| intro: Optional[str] = None | |
| # 🧹 P2修复:v2 中下划线前缀属性会被当作私有属性而非校验器,改用 field_validator 显式挂载(校验逻辑不变) | |
| def _check_password(cls, v): | |
| return _validate_password_length(v) | |
| def _check_email(cls, v): | |
| return _validate_email_format(v) | |
| def _check_avatar_url(cls, value): | |
| return normalize_profile_media(value) | |
| class UserLogin(BaseModel): | |
| account: str | |
| password: str | |
| remember: Optional[bool] = True # 保持登录选项:True=30天, False=24小时 | |
| def _check_password(cls, v): | |
| return _validate_password_length(v) | |
| class UserUpdate(BaseModel): | |
| name: Optional[str] = None | |
| intro: Optional[str] = None | |
| age: Optional[int] = None | |
| birthday: Optional[str] = None # 🚀 出生日期 | |
| country: Optional[str] = None | |
| region: Optional[str] = None | |
| gender: Optional[str] = None | |
| avatarDataUrl: Optional[str] = None | |
| bannerUrl: Optional[str] = None # 🚀 个人资料卡背景图云端URL | |
| def _check_media_url(cls, value): | |
| return normalize_profile_media(value) | |
| class SettingsPrivacy(BaseModel): | |
| model_config = {"extra": "forbid"} | |
| follows: bool = Field(strict=True) | |
| likes: bool = Field(strict=True) | |
| favorites: bool = Field(strict=True) | |
| downloads: bool = Field(strict=True) | |
| class UserSettings(UserUpdate): | |
| model_config = {"extra": "forbid"} | |
| privacy: SettingsPrivacy | |
| # 🧹 P2清理:移除零引用的 PasswordReset 模型(重置密码实际走 UserUpdate/专用端点) | |
| class CommentCreate(BaseModel): | |
| item_id: str | |
| author: str | |
| content: str | |
| reply_to_user: Optional[str] = None | |
| parent_id: Optional[str] = None | |
| class ItemCreate(BaseModel): | |
| request_id: Optional[str] = Field(default=None, strict=True, pattern=r'^[0-9a-f]{32}$') | |
| type: str | |
| title: str | |
| shortDesc: str | |
| fullDesc: str | |
| link: str | |
| coverUrl: Optional[str] = None | |
| imageUrls: Optional[List[str]] = [] | |
| author: str | |
| price: int = Field(default=0, ge=0, strict=True) | |
| github_token: Optional[str] = None | |
| netdisk_password: Optional[str] = None # ☁️ 网盘提取码(加密存储,购买后解密) | |
| is_netdisk: Optional[bool] = False # ☁️ 是否为网盘资源 | |
| is_original: Optional[bool] = False # 🎨 是否为原创作品 | |
| allow_refund: Optional[bool] = True # 💸 是否支持退款(默认支持) | |
| class ItemUpdate(BaseModel): | |
| expected_revision: Optional[int] = Field(default=None, ge=0, le=9007199254740991, strict=True) | |
| title: Optional[str] = None | |
| shortDesc: Optional[str] = None | |
| fullDesc: Optional[str] = None | |
| link: Optional[str] = None | |
| coverUrl: Optional[str] = None | |
| imageUrls: Optional[List[str]] = None | |
| price: Optional[int] = Field(default=None, ge=0, strict=True) | |
| github_token: Optional[str] = None | |
| netdisk_password: Optional[str] = None # ☁️ 网盘提取码 | |
| is_netdisk: Optional[bool] = None # ☁️ 是否为网盘资源 | |
| is_original: Optional[bool] = None # 🎨 是否为原创作品 | |
| allow_refund: Optional[bool] = None # 💸 是否支持退款 | |
| class FollowToggle(BaseModel): | |
| user_id: str | |
| target_account: str | |
| is_active: bool | |
| class PrivateMessage(BaseModel): | |
| sender: str | |
| receiver: str | |
| content: str | |
| class IdempotentPrivateMessage(PrivateMessage): | |
| client_request_id: str = Field(..., strict=True, min_length=32, max_length=32, pattern=r"^[0-9a-f]{32}$") | |
| class PrivacySettings(BaseModel): | |
| follows: bool | |
| likes: bool | |
| favorites: bool | |
| downloads: bool | |
| class InteractionToggle(BaseModel): | |
| item_id: str | |
| user_id: str | |
| action_type: str | |
| is_active: bool | |
| class RatingRequest(BaseModel): | |
| score: int # 1-5 | |
| def validate_score(cls, v): | |
| if not isinstance(v, int): | |
| raise ValueError("score must be an integer between 1 and 5") | |
| if v < 1 or v > 5: | |
| raise ValueError("score must be between 1 and 5") | |
| return v | |
| class RechargeRequest(BaseModel): | |
| account: str | |
| amount: int = Field(..., ge=1, le=10000, strict=True) # 单次充值1-10000 | |
| method: Optional[str] = "alipay" | |
| class PurchaseRequest(BaseModel): | |
| account: str | |
| item_id: str | |
| expected_price: Optional[int] = Field(default=None, ge=0, strict=True) | |
| class PromptPurchaseRequest(BaseModel): | |
| expected_price: Optional[int] = Field(default=None, ge=0, strict=True) | |
| class TipRequest(BaseModel): | |
| sender_account: str | |
| target_account: str | |
| amount: int | |
| is_anonymous: bool | |
| item_id: Optional[str] = None # 🚀 新增:记录是为哪个作品打赏的 | |
| class WithdrawRequest(BaseModel): | |
| account: str | |
| amount: int = Field(..., ge=1, le=5000, strict=True) # 单次提现1-5000 | |
| alipayAccount: str = Field(..., min_length=1, max_length=256) | |
| real_name: str = Field(..., min_length=1, max_length=128) | |
| code: str = Field(..., pattern=r"^[0-9]{6}$") | |
| # ========================================== | |
| # 📝 任务榜数据模型 | |
| # ========================================== | |
| class TaskCreate(BaseModel): | |
| """ 发布新任务 """ | |
| title: str # 任务标题 | |
| description: str # 任务详情 | |
| referenceImages: Optional[List[str]] = [] # 参考图(最多6张) | |
| referenceLink: Optional[str] = None # 参考链接 | |
| totalPrice: int = Field(..., ge=0, le=2147483647, strict=True) # SQL整数范围 | |
| depositRatio: int = Field(..., strict=True) # 订金比例(10/20/30/50) | |
| deadline: str # 截止时间 (ISO格式) | |
| publisher: str # 发布者账号 | |
| class TaskUpdate(BaseModel): | |
| """ 更新任务 """ | |
| title: Optional[str] = None | |
| description: Optional[str] = None | |
| referenceImages: Optional[List[str]] = None | |
| referenceLink: Optional[str] = None | |
| totalPrice: Optional[int] = Field(None, ge=0, le=2147483647, strict=True) | |
| depositRatio: Optional[int] = Field(None, strict=True) | |
| deadline: Optional[str] = None | |
| # 🔧 P2修复:以下三个 body 模型修复前后端参数通道不匹配 | |
| # (前端以 JSON body 提交,旧版端点用裸标量参数会被 FastAPI 解析为 query 导致 422) | |
| class TaskSubmit(BaseModel): | |
| """ 接单者提交成果 """ | |
| deliverables: List[str] # 交付成果列表 | |
| note: Optional[str] = None # 提交说明 | |
| class TaskDispute(BaseModel): | |
| """ 发起申诉 """ | |
| reason: str # 申诉理由 | |
| evidence: Optional[List[str]] = None # 证据图片URL列表(最多6张) | |
| class TaskDisputeResponse(BaseModel): | |
| """ 被申诉方回应申诉 """ | |
| response: str # 回应内容 | |
| evidence: Optional[List[str]] = None # 证据图片URL列表(最多6张) | |
| # 🧹 P2清理:移除零引用的 TaskApply/TaskAssign/TaskAccept 模型(对应端点使用 query 参数且前端匹配) | |
| # ========================================== | |
| # 💬 讨论区数据模型(小红书风格图文社区) | |
| # ========================================== | |
| class PostCreate(BaseModel): | |
| """ 创建帖子 """ | |
| title: str # 标题 | |
| content: str # 正文/文案 | |
| cover_image: str # 封面图(第一张) | |
| images: Optional[List[str]] = [] # 图片列表(最多9张) | |
| author: str # 作者账号 | |
| is_original: Optional[bool] = False # 🎨 是否为原创作品 | |
| post_type: str = "image" # "image" 或 "video" | |
| video_url: Optional[str] = None # 视频 URL(仅 video 类型) | |
| class PostUpdate(BaseModel): | |
| """ 更新帖子 """ | |
| title: Optional[str] = None | |
| content: Optional[str] = None | |
| cover_image: Optional[str] = None | |
| images: Optional[List[str]] = None | |
| is_original: Optional[bool] = None # 🎨 是否为原创作品 | |
| post_type: Optional[str] = None # "image" 或 "video" | |
| video_url: Optional[str] = None # 视频 URL(仅 video 类型) | |
| # ========================================== | |
| # ✨ 提示词市场数据模型(图像/视频提示词) | |
| # ========================================== | |
| class PromptCreate(BaseModel): | |
| """ 发布提示词 """ | |
| prompt_type: str # "image"、"video" 或 "music"(所属子界面) | |
| category: str # 所属分类(人物/动作等) | |
| title: str # 标题 | |
| content: str # 公开介绍文案 | |
| prompt_text: str # 提示词正文(付费内容,服务端脱敏下发) | |
| cover_image: str # 封面图 | |
| media_type: str = "image" # "image" 或 "video"(示例媒体类型) | |
| video_url: Optional[str] = None # 示例视频 URL(仅 media_type=video) | |
| audio_url: Optional[str] = None # 示例音频 URL(仅音乐提示词,图片+音频方案;视频可选) | |
| images: Optional[List[str]] = None # 图像提示词图片列表(最多9张,首张为封面) | |
| is_original: bool = False # 原创作品标记 | |
| price: int = Field(default=0, ge=0, le=10000, strict=True) # 提示词定价(0=免费) | |
| tags: Optional[List[str]] = None # 自定义标签(最多10个,每个≤12字) | |
| class PromptUpdate(BaseModel): | |
| """ 更新提示词 """ | |
| category: Optional[str] = None | |
| title: Optional[str] = None | |
| content: Optional[str] = None | |
| prompt_text: Optional[str] = None | |
| cover_image: Optional[str] = None | |
| media_type: Optional[str] = None | |
| video_url: Optional[str] = None | |
| audio_url: Optional[str] = None # 示例音频 URL(仅音乐提示词;传 null 表示清除) | |
| images: Optional[List[str]] = None # 图像提示词图片列表(最多9张,首张为封面) | |
| is_original: Optional[bool] = None # 原创作品标记 | |
| price: Optional[int] = Field(default=None, ge=0, le=10000, strict=True) | |
| tags: Optional[List[str]] = None | |
| class PromptCategoryAdd(BaseModel): | |
| """ 添加提示词分类 """ | |
| prompt_type: str # "image"、"video" 或 "music" | |
| name: str # 分类名称(≤8字,去重) | |