{"repo": "belingud/claudewarp", "files": {"claudewarp/core/models.py": {"ed0c32a1549659811f5ad0ccf0ddb87176ece4a8": {"t": 1785674425.6086905, "n": 11970, "h": "28759ab6c65b1f0d", "c": "\"\"\"\n核心数据模型\n\n定义代理服务器和配置相关的数据模型,使用Pydantic进行数据验证。\n\"\"\"\n\nimport re\nfrom datetime import datetime\nfrom typing import Any, Dict, List, Optional, Self\n\nfrom pydantic import (\n BaseModel,\n ConfigDict,\n Field,\n HttpUrl,\n field_validator,\n model_validator,\n)\n\n\nclass ProxyServer(BaseModel):\n \"\"\"代理服务器配置模型\n\n 表示单个Claude API中转站的配置信息,包含名称、URL、API密钥等。\n \"\"\"\n\n name: str = Field(\n ..., min_length=1, max_length=50, description=\"代理服务器名称,用于标识和选择\"\n )\n base_url: HttpUrl = Field(..., description=\"代理服务器的基础URL,必须是有效的HTTP/HTTPS地址\")\n api_key: Optional[str] = Field(default=None, description=\"API密钥,用于身份验证\")\n description: str = Field(default=\"\", max_length=200, description=\"代理服务器的描述信息\")\n tags: List[str] = Field(default_factory=list, description=\"标签列表,用于分类和筛选\")\n created_at: str = Field(\n default_factory=lambda: datetime.now().isoformat(), description=\"创建时间,ISO格式\"\n )\n updated_at: str = Field(\n default_factory=lambda: datetime.now().isoformat(), description=\"最后更新时间,ISO格式\"\n )\n is_active: bool = Field(default=True, description=\"是否启用该代理服务器\")\n bigmodel: Optional[str] = Field(default=None, description=\"大模型名称\")\n smallmodel: Optional[str] = Field(default=None, description=\"小模型名称\")\n auth_token: Optional[str] = Field(\n default=None, description=\"Auth令牌,用于身份验证(与api_key互斥)\"\n )\n api_key_helper: Optional[str] = Field(\n default=None,\n min_length=1,\n description=\"API密钥助手命令,用于获取API密钥(与api_key、auth_token互斥)\",\n )\n\n @field_validator(\"name\")\n def validate_name(cls, v: str) -> str:\n \"\"\"验证代理名称格式\"\"\"\n if not re.match(r\"^[a-zA-Z0-9_-]+$\", v):\n raise ValueError(\"代理名称只能包含字母、数字、下划线和横线\")\n return v\n\n @field_validator(\"tags\")\n def validate_tags(cls, v: List[str]) -> List[str]:\n \"\"\"验证标签列表\"\"\"\n return list(set(i.strip() for i in v))\n\n @field_validator(\"api_key\")\n def validate_api_key(cls, v: Optional[str]) -> Optional[str]:\n \"\"\"验证API Key长度\"\"\"\n if v and len(v) < 3:\n raise ValueError(\"API Key至少需要3个字符\")\n return v\n\n @field_validator(\"auth_token\")\n def validate_auth_token(cls, v: Optional[str]) -> Optional[str]:\n \"\"\"验证Auth令牌长度\"\"\"\n if v and len(v) < 3:\n raise ValueError(\"Auth令牌至少需要3个字符\")\n return v\n\n def update_timestamp(self) -> None:\n \"\"\"手动更新时间戳\"\"\"\n self.updated_at = datetime.now().isoformat()\n\n def copy_with_updates(self, **kwargs) -> \"ProxyServer\":\n \"\"\"创建副本并更新指定字段,同时更新时间戳\n\n Args:\n **kwargs: 要更新的字段\n\n Returns:\n ProxyServer: 更新后的新实例\n \"\"\"\n # 获取当前所有字段的值\n current_data = self.model_dump()\n\n # 更新指定字段\n current_data.update(kwargs)\n\n # 更新时间戳\n current_data[\"updated_at\"] = datetime.now().isoformat()\n\n # 创建新实例\n return ProxyServer(**current_data)\n\n @model_validator(mode=\"after\")\n def api_key_or_auth_token_or_helper(cls, values: Self) -> Self:\n \"\"\"确保api_key、auth_token或api_key_helper至少有一个且互斥\"\"\"\n # 检查三者是否同时存在多个\n auth_methods = []\n if values.api_key:\n auth_methods.append(\"api_key\")\n if values.auth_token:\n auth_methods.append(\"auth_token\")\n if values.api_key_helper:\n auth_methods.append(\"api_key_helper\")\n\n if len(auth_methods) > 1:\n raise ValueError(f\"{'、'.join(auth_methods)}只能存在一个\")\n if not auth_methods:\n raise ValueError(\"api_key、auth_token或api_key_helper至少有一个必须存在\")\n return values\n\n def get_auth_method(self) -> str:\n \"\"\"获取当前使用的认证方法\"\"\"\n if self.auth_token:\n return \"auth_token\"\n elif self.api_key:\n return \"api_key\"\n elif self.api_key_helper:\n return \"api_key_helper\"\n else:\n return \"none\"\n\n def get_active_credential(self) -> Optional[str]:\n \"\"\"获取当前活跃的认证凭据\"\"\"\n if self.auth_token:\n return self.auth_token\n elif self.api_key:\n return self.api_key\n elif self.api_key_helper:\n return self.api_key_helper\n return None\n\n model_config = ConfigDict(\n # 允许字段别名\n validate_by_name=True,\n # 验证赋值\n validate_assignment=True,\n # JSON编码器配置\n json_encoders={\n datetime: lambda v: v.isoformat(),\n # 确保HttpUrl对象正确序列化为字符串\n \"HttpUrl\": lambda v: str(v),\n },\n # 模型示例\n json_schema_extra={\n \"example\": {\n \"name\": \"proxy-cn\",\n \"base_url\": \"https://api.claude-proxy.com/\",\n \"api_key\": \"sk-1234567890abcdef\",\n \"description\": \"国内主力节点\",\n \"tags\": [\"china\", \"primary\"],\n \"is_active\": True,\n },\n \"examples\": [\n {\n \"name\": \"proxy-auth\",\n \"base_url\": \"https://api.claude-proxy.com/\",\n \"auth_token\": \"sk-ant-api03-abcdef123456\",\n \"description\": \"使用Auth令牌的代理\",\n \"tags\": [\"auth\", \"primary\"],\n \"is_active\": True,\n },\n {\n \"name\": \"proxy-helper\",\n \"base_url\": \"https://api.claude-proxy.com/\",\n \"api_key_helper\": \"echo 'sk-xxx'\",\n \"description\": \"使用API密钥助手命令的代理\",\n \"tags\": [\"helper\", \"dynamic\"],\n \"is_active\": True,\n },\n ],\n },\n )\n\n\nclass ProxyConfig(BaseModel):\n \"\"\"代理配置文件模型\n\n 表示整个应用程序的配置,包含所有代理服务器和全局设置。\n \"\"\"\n\n version: str = Field(default=\"1.0\", description=\"配置文件版本\")\n current_proxy: Optional[str] = Field(default=None, description=\"当前活跃的代理服务器名称\")\n proxies: Dict[str, ProxyServer] = Field(\n default_factory=dict, description=\"代理服务器字典,key为代理名称\"\n )\n settings: Dict[str, Any] = Field(default_factory=dict, description=\"应用程序设置\")\n created_at: str = Field(\n default_factory=lambda: datetime.now().isoformat(), description=\"配置创建时间\"\n )\n updated_at: str = Field(\n default_factory=lambda: datetime.now().isoformat(), description=\"配置最后更新时间\"\n )\n\n @model_validator(mode=\"after\")\n def validate_current_proxy(cls, values: Self) -> Self:\n \"\"\"验证当前代理是否存在于代理列表中\"\"\"\n if values.current_proxy is not None and values.current_proxy not in values.proxies:\n raise ValueError(f'当前代理 \"{values.current_proxy}\" 不存在于代理列表中')\n return values\n\n @field_validator(\"proxies\")\n def validate_proxies(cls, v: Dict[str, ProxyServer]) -> Dict[str, ProxyServer]:\n \"\"\"验证代理字典的一致性\"\"\"\n for name, proxy in v.items():\n if proxy.name != name:\n raise ValueError(\n f'代理名称不一致: 字典key为\"{name}\", 但代理对象name为\"{proxy.name}\"'\n )\n return v\n\n def update_config_timestamp(self) -> None:\n \"\"\"手动更新配置文件时间戳\"\"\"\n self.updated_at = datetime.now().isoformat()\n\n def copy_with_settings_update(self, **settings_kwargs) -> \"ProxyConfig\":\n \"\"\"更新settings并创建新的配置实例(不需要备份)\n\n Args:\n **settings_kwargs: 要更新的settings字段\n\n Returns:\n ProxyConfig: 更新后的新实例\n \"\"\"\n new_settings = self.settings.copy()\n new_settings.update(settings_kwargs)\n\n # 创建新实例,保持其他字段不变,只更新settings和时间戳\n return ProxyConfig(\n version=self.version,\n current_proxy=self.current_proxy,\n proxies=self.proxies.copy(),\n settings=new_settings,\n created_at=self.created_at,\n updated_at=datetime.now().isoformat(),\n )\n\n def copy_with_proxy_update(self, proxy_name: str, updated_proxy: ProxyServer) -> \"ProxyConfig\":\n \"\"\"更新proxy并创建新的配置实例(需要备份)\n\n Args:\n proxy_name: 代理名称\n updated_proxy: 更新后的代理对象\n\n Returns:\n ProxyConfig: 更新后的新实例\n \"\"\"\n new_proxies = self.proxies.copy()\n new_proxies[proxy_name] = updated_proxy\n\n return ProxyConfig(\n version=self.version,\n current_proxy=self.current_proxy,\n proxies=new_proxies,\n settings=self.settings.copy(),\n created_at=self.created_at,\n updated_at=datetime.now().isoformat(),\n )\n\n def get_current_proxy(self) -> Optional[ProxyServer]:\n \"\"\"获取当前活跃的代理服务器\"\"\"\n if self.current_proxy and self.current_proxy in self.proxies:\n return self.proxies[self.current_proxy]\n return None\n\n def add_proxy(self, proxy: ProxyServer) -> None:\n \"\"\"添加代理服务器\"\"\"\n self.proxies[proxy.name] = proxy\n # 如果是第一个代理,设置为当前代理\n if len(self.proxies) == 1:\n self.current_proxy = proxy.name\n\n def remove_proxy(self, name: str) -> bool:\n \"\"\"删除代理服务器\"\"\"\n if name not in self.proxies:\n return False\n\n del self.proxies[name]\n\n # 如果删除的是当前代理,切换到其他代理或清空\n if self.current_proxy == name:\n if self.proxies:\n self.current_proxy = next(iter(self.proxies))\n else:\n self.current_proxy = None\n\n return True\n\n def set_current_proxy(self, name: str) -> bool:\n \"\"\"设置当前代理\"\"\"\n if name not in self.proxies:\n return False\n self.current_proxy = name\n return True\n\n def get_proxy_names(self) -> List[str]:\n \"\"\"获取所有代理名称列表\"\"\"\n return list(self.proxies.keys())\n\n def get_active_proxies(self) -> Dict[str, ProxyServer]:\n \"\"\"获取所有启用的代理服务器\"\"\"\n return {name: proxy for name, proxy in self.proxies.items() if proxy.is_active}\n\n model_config = ConfigDict(\n # 允许字段别名\n validate_by_name=True,\n # 验证赋值\n validate_assignment=True,\n # JSON编码器配置\n json_encoders={\n datetime: lambda v: v.isoformat(),\n # 确保HttpUrl对象正确序列化为字符串\n \"HttpUrl\": lambda v: str(v),\n },\n # 模型示例\n json_schema_extra={\n \"example\": {\n \"version\": \"1.0\",\n \"current_proxy\": \"proxy-cn\",\n \"proxies\": {\n \"proxy-cn\": {\n \"name\": \"proxy-cn\",\n \"base_url\": \"https://proxy-cn.example.com/\",\n \"api_key\": \"sk-1234567890abcdef\",\n \"description\": \"国内主力节点\",\n \"tags\": [\"china\", \"primary\"],\n }\n },\n \"settings\": {\"auto_backup\": True, \"backup_count\": 5},\n }\n },\n )\n\n\nclass ExportFormat(BaseModel):\n \"\"\"环境变量导出格式配置\n\n 定义如何导出环境变量的格式和选项。\n \"\"\"\n\n shell_type: str = Field(default=\"bash\", description=\"Shell类型: bash, fish, powershell\")\n include_comments: bool = Field(default=True, description=\"是否包含注释\")\n prefix: str = Field(default=\"ANTHROPIC_\", description=\"环境变量前缀\")\n export_all: bool = Field(default=False, description=\"是否导出所有代理(默认只导出当前代理)\")\n\n @field_validator(\"shell_type\")\n def validate_shell_type(cls, v: str) -> str:\n \"\"\"验证Shell类型\"\"\"\n valid_shells = {\"bash\", \"fish\", \"powershell\", \"zsh\"}\n v = v.lower()\n if v not in valid_shells:\n raise ValueError(f\"不支持的Shell类型: {v}. 支持的类型: {', '.join(valid_shells)}\")\n return v\n\n @field_validator(\"prefix\")\n def validate_prefix(cls, v: str) -> str:\n \"\"\"验证环境变量前缀\"\"\"\n if not re.match(r\"^[A-Z_][A-Z0-9_]*$\", v):\n raise ValueError(\n \"环境变量前缀必须以大写字母或下划线开头,只能包含大写字母、数字和下划线\"\n )\n if not v.endswith(\"_\"):\n v += \"_\"\n return v\n\n model_config = ConfigDict(\n json_schema_extra={\n \"example\": {\n \"shell_type\": \"bash\",\n \"include_comments\": True,\n \"prefix\": \"ANTHROPIC_\",\n \"export_all\": False,\n }\n }\n )\n\n\n# 用于类型提示的导出\n__all__ = [\"ProxyServer\", \"ProxyConfig\", \"ExportFormat\"]\n"}}}}