diff --git a/.env.example b/.env.example index 2b1399f..5d3df3e 100755 --- a/.env.example +++ b/.env.example @@ -112,3 +112,34 @@ COS_PRESIGN_EXPIRE_SECONDS=600 UPLOAD_MAX_BYTES=20971520 UPLOAD_ALLOWED_TYPES=image/jpeg,image/png,image/webp UPLOAD_SCENES_JSON={"report_photo":{"bucket":"temp"},"avatar":{"bucket":"temp"}} + +# ── 知识库与检索(架构 §8.3,TD-08)──────────────────────────────────────── +# 当前 embedding 为确定性假实现(dev/test);真实 BGE-M3 ONNX 接入时只换这里。 +KB_VECTOR_DIMENSIONS=1024 +KB_EMBEDDING_MODEL=deterministic-v1 +KB_EMBEDDING_DEVICE=cpu +RETRIEVAL_TOP_K=50 +RETRIEVAL_RERANK_WEIGHTS_JSON={"vector":0.45,"keyword":0.2,"rule":0.25,"freshness":0.1} + +# ── 华为账号登录(FR-ACC-01;真实联调待 AGC 配置,见《前置开通清单》§3)──── +HUAWEI_JWKS_URL=https://oauth-login.cloud.huawei.com/oauth2/v3/certs +HUAWEI_AUDIENCE= +HUAWEI_ISSUER=https://accounts.huawei.com +HUAWEI_JWKS_CACHE_SECONDS=86400 + +# ── 手机号验证码登录(FR-ACC-04/14,TD-32)───────────────────────────────── +# fake = 验证码打日志(dev);tencent = 真实发送(签名审核通过后切换并填下方凭证) +SMS_PROVIDER=fake +TENCENT_SMS_SECRET_ID= +TENCENT_SMS_SECRET_KEY= +TENCENT_SMS_SDK_APP_ID= +TENCENT_SMS_SIGN_NAME= +TENCENT_SMS_TEMPLATE_ID= +SMS_CODE_TTL_SECONDS=300 +SMS_CODE_LENGTH=6 +SMS_VERIFY_MAX_ATTEMPTS=5 +SMS_RESEND_COOLDOWN_SECONDS=60 +SMS_HOURLY_LIMIT=5 +SMS_DAILY_LIMIT=10 +SMS_IP_DAILY_LIMIT=20 +SMS_DEVICE_DAILY_LIMIT=10 diff --git a/.gitignore b/.gitignore index fc491e5..a1969af 100755 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,8 @@ htmlcov/ logs/ tmp/ + +# 算法 SPIKE 的模型缓存与输出(模型文件绝不入库) +models/ +scripts/spikes/out/ +scripts/spikes/assets/ diff --git a/apps/api/main.py b/apps/api/main.py index eda2efa..a1e44ee 100644 --- a/apps/api/main.py +++ b/apps/api/main.py @@ -12,7 +12,7 @@ from contextlib import asynccontextmanager from fastapi import FastAPI from apps.api.deps import build_llm_gateway -from apps.api.routers import agent, auth, events, health, quota, tasks, uploads +from apps.api.routers import agent, auth, auth_sms, events, health, kb, quota, tasks, uploads from packages.shared.config import Settings, get_settings from packages.shared.db import dispose_engine from packages.shared.handlers import install_exception_handlers @@ -69,7 +69,16 @@ def create_app(settings: Settings | None = None) -> FastAPI: setup_tracing(app, cfg) app.include_router(health.router) - routers = (auth.router, agent.router, quota.router, tasks.router, events.router, uploads.router) + routers = ( + auth.router, + auth_sms.router, + agent.router, + quota.router, + tasks.router, + events.router, + uploads.router, + kb.router, + ) for router in routers: app.include_router(router, prefix=cfg.api_prefix) diff --git a/apps/api/routers/auth_sms.py b/apps/api/routers/auth_sms.py new file mode 100644 index 0000000..c17c2c7 --- /dev/null +++ b/apps/api/routers/auth_sms.py @@ -0,0 +1,12 @@ +"""手机号验证码登录路由(骨架,T11 填充实现)。 + +POST /auth/sms/send、POST /auth/sms/verify; +实现契约见 docs/开发任务包/T11-短信登录代码先行.md。 +与 auth.py(华为/dev-token/refresh)分文件以避免并行冲突。 +""" + +from __future__ import annotations + +from fastapi import APIRouter + +router = APIRouter(prefix="/auth", tags=["auth-sms"]) diff --git a/apps/api/routers/kb.py b/apps/api/routers/kb.py new file mode 100644 index 0000000..598d560 --- /dev/null +++ b/apps/api/routers/kb.py @@ -0,0 +1,10 @@ +"""知识库实体路由(骨架,T6 填充实现)。 + +GET /kb/items 等;实现契约见 docs/开发任务包/T6-知识库实体与图谱.md。 +""" + +from __future__ import annotations + +from fastapi import APIRouter + +router = APIRouter(prefix="/kb", tags=["kb"]) diff --git a/migrations/versions/0005_kb_tables.py b/migrations/versions/0005_kb_tables.py new file mode 100644 index 0000000..343031d --- /dev/null +++ b/migrations/versions/0005_kb_tables.py @@ -0,0 +1,179 @@ +"""Knowledge base tables for the Sprint-2 parallel batch (架构文档 §6.2 kb 域). + +七张表一次落齐,任务批(T6/T7/T8)不再写迁移: + kb_entity / kb_relation / kb_rule → T6 实体图谱、T7 规则引擎 + kb_chunk (vector 1024 + tsv) → T8 混合检索 + kb_asset / kb_color → T6/T8 + kb_gap → T8 无结果缺口埋点 + +索引决策: + - kb_chunk.embedding 用 HNSW(cosine)——万级数据下召回延迟远优于 IVFFlat, + 且不需要先灌数据再训练聚类(IVFFlat 的 num_lists 依赖数据量)。 + - tsv 用 GIN;tsvector 是生成列(PG12+),应用层不手工维护。 + +Reversible: 是。降级按依赖倒序 drop。 +""" + +from __future__ import annotations + +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op +from pgvector.sqlalchemy import Vector +from sqlalchemy.dialects import postgresql + +revision: str = "0005" +down_revision: str | None = "0004" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + op.create_table( + "kb_entity", + sa.Column("entity_id", sa.BigInteger(), nullable=False), + sa.Column("entity_type", sa.String(length=32), nullable=False), + sa.Column("name", sa.String(length=128), nullable=False), + sa.Column("attrs", postgresql.JSONB(), nullable=False), + sa.Column("status", sa.String(length=16), nullable=False, server_default=sa.text("'draft'")), + sa.Column("version", sa.Integer(), nullable=False, server_default=sa.text("1")), + sa.Column("source", sa.String(length=255), nullable=True), + sa.Column("license", sa.String(length=64), nullable=True), + sa.Column("updated_by", sa.String(length=64), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), + sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), + sa.PrimaryKeyConstraint("entity_id"), + sa.UniqueConstraint("entity_type", "name", name="uq_kb_entity_type_name"), + ) + op.create_index("ix_kb_entity_type_status", "kb_entity", ["entity_type", "status"]) + + op.create_table( + "kb_relation", + sa.Column("relation_id", sa.BigInteger(), nullable=False), + sa.Column("from_id", sa.BigInteger(), nullable=False), + sa.Column("rel_type", sa.String(length=64), nullable=False), + sa.Column("to_id", sa.BigInteger(), nullable=False), + sa.Column("weight", sa.Numeric(4, 3), nullable=False, server_default=sa.text("1")), + sa.Column("confidence", sa.Numeric(4, 3), nullable=False, server_default=sa.text("1")), + sa.Column("evidence", sa.String(length=255), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), + sa.PrimaryKeyConstraint("relation_id"), + sa.UniqueConstraint("from_id", "rel_type", "to_id", name="uq_kb_relation_triple"), + sa.ForeignKeyConstraint(["from_id"], ["kb_entity.entity_id"], ondelete="CASCADE"), + sa.ForeignKeyConstraint(["to_id"], ["kb_entity.entity_id"], ondelete="CASCADE"), + ) + op.create_index("ix_kb_relation_to", "kb_relation", ["to_id"]) + + op.create_table( + "kb_rule", + sa.Column("rule_id", sa.BigInteger(), nullable=False), + sa.Column("rule_key", sa.String(length=64), nullable=False), + sa.Column("category", sa.String(length=32), nullable=False), + sa.Column("version", sa.Integer(), nullable=False, server_default=sa.text("1")), + sa.Column("status", sa.String(length=16), nullable=False, server_default=sa.text("'draft'")), + sa.Column("priority", sa.Integer(), nullable=False, server_default=sa.text("60")), + sa.Column("conditions", postgresql.JSONB(), nullable=False), + sa.Column("effects", postgresql.JSONB(), nullable=False), + sa.Column("is_hard", sa.Boolean(), nullable=False, server_default=sa.text("false")), + sa.Column("confidence", sa.Numeric(4, 3), nullable=False, server_default=sa.text("0.9")), + sa.Column("evidence", sa.String(length=255), nullable=True), + sa.Column("author", sa.String(length=64), nullable=True), + sa.Column("reviewer", sa.String(length=64), nullable=True), + sa.Column("published_at", sa.DateTime(timezone=True), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), + sa.PrimaryKeyConstraint("rule_id"), + sa.UniqueConstraint("rule_key", name="uq_kb_rule_key"), + ) + op.create_index("ix_kb_rule_status_priority", "kb_rule", ["status", "priority"]) + + op.create_table( + "kb_chunk", + sa.Column("chunk_id", sa.BigInteger(), nullable=False), + sa.Column("entity_id", sa.BigInteger(), nullable=False), + sa.Column("chunk_type", sa.String(length=32), nullable=False, server_default=sa.text("'text'")), + sa.Column("text", sa.Text(), nullable=False), + sa.Column("embedding", Vector(1024), nullable=False), + sa.Column("embedding_model", sa.String(length=64), nullable=False), + sa.Column("embedding_version", sa.String(length=16), nullable=False), + sa.Column( + "tsv", + postgresql.TSVECTOR(), + sa.Computed("to_tsvector('simple', text)", persisted=True), + nullable=False, + ), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), + sa.PrimaryKeyConstraint("chunk_id"), + sa.ForeignKeyConstraint(["entity_id"], ["kb_entity.entity_id"], ondelete="CASCADE"), + ) + op.create_index("ix_kb_chunk_entity", "kb_chunk", ["entity_id"]) + op.execute( + "CREATE INDEX ix_kb_chunk_embedding_hnsw ON kb_chunk " + "USING hnsw (embedding vector_cosine_ops)" + ) + op.execute("CREATE INDEX ix_kb_chunk_tsv_gin ON kb_chunk USING gin (tsv)") + + op.create_table( + "kb_asset", + sa.Column("asset_id", sa.BigInteger(), nullable=False), + sa.Column("entity_id", sa.BigInteger(), nullable=False), + sa.Column("asset_type", sa.String(length=32), nullable=False), + sa.Column("cos_key", sa.String(length=255), nullable=False), + sa.Column("spec_check", postgresql.JSONB(), nullable=False), + sa.Column("tryon_ready", sa.Boolean(), nullable=False, server_default=sa.text("false")), + sa.Column("review_status", sa.String(length=16), nullable=False, server_default=sa.text("'pending'")), + sa.Column("license", sa.String(length=64), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), + sa.PrimaryKeyConstraint("asset_id"), + sa.ForeignKeyConstraint(["entity_id"], ["kb_entity.entity_id"], ondelete="CASCADE"), + ) + op.create_index("ix_kb_asset_entity", "kb_asset", ["entity_id"]) + op.execute( + "CREATE INDEX ix_kb_asset_tryon_ready ON kb_asset (tryon_ready) WHERE tryon_ready" + ) + + op.create_table( + "kb_color", + sa.Column("color_id", sa.BigInteger(), nullable=False), + sa.Column("season_code", sa.String(length=16), nullable=False), + sa.Column("role", sa.String(length=8), nullable=False), + sa.Column("lab_l", sa.Numeric(6, 2), nullable=False), + sa.Column("lab_a", sa.Numeric(6, 2), nullable=False), + sa.Column("lab_b", sa.Numeric(6, 2), nullable=False), + sa.Column("hex", sa.String(length=7), nullable=False), + sa.Column("name_cn", sa.String(length=64), nullable=False), + sa.PrimaryKeyConstraint("color_id"), + ) + op.create_index("ix_kb_color_season", "kb_color", ["season_code"]) + + op.create_table( + "kb_gap", + sa.Column("gap_id", sa.BigInteger(), nullable=False), + sa.Column("query_text", sa.Text(), nullable=False), + sa.Column("filter_ctx", postgresql.JSONB(), nullable=False), + sa.Column("hit_count", sa.Integer(), nullable=False, server_default=sa.text("1")), + sa.Column("first_seen_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), + sa.Column("last_seen_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), + sa.PrimaryKeyConstraint("gap_id"), + ) + op.create_index("ix_kb_gap_query", "kb_gap", ["query_text"]) + + +def downgrade() -> None: + op.drop_index("ix_kb_gap_query", table_name="kb_gap") + op.drop_table("kb_gap") + op.drop_index("ix_kb_color_season", table_name="kb_color") + op.drop_table("kb_color") + op.execute("DROP INDEX IF EXISTS ix_kb_asset_tryon_ready") + op.drop_index("ix_kb_asset_entity", table_name="kb_asset") + op.drop_table("kb_asset") + op.execute("DROP INDEX IF EXISTS ix_kb_chunk_tsv_gin") + op.execute("DROP INDEX IF EXISTS ix_kb_chunk_embedding_hnsw") + op.drop_index("ix_kb_chunk_entity", table_name="kb_chunk") + op.drop_table("kb_chunk") + op.drop_index("ix_kb_rule_status_priority", table_name="kb_rule") + op.drop_table("kb_rule") + op.drop_index("ix_kb_relation_to", table_name="kb_relation") + op.drop_table("kb_relation") + op.drop_index("ix_kb_entity_type_status", table_name="kb_entity") + op.drop_table("kb_entity") diff --git a/packages/shared/config.py b/packages/shared/config.py index dea6034..ad4b129 100644 --- a/packages/shared/config.py +++ b/packages/shared/config.py @@ -168,6 +168,41 @@ class Settings(BaseSettings): alias="UPLOAD_SCENES_JSON", ) + # --- 知识库与检索(架构 §8.3,TD-08)-------------------------------------- + kb_vector_dimensions: int = 1024 + # 当前默认 = 确定性假实现;真实 BGE-M3 ONNX 是后续任务,届时只换这里。 + kb_embedding_model: str = "deterministic-v1" + kb_embedding_device: str = "cpu" + retrieval_top_k: int = 50 + # 重排权重(架构 §8.3 唯一口径):0.45 向量 + 0.2 关键词 + 0.25 规则 + 0.1 新鲜度。 + retrieval_rerank_weights: dict[str, float] = Field( + default_factory=lambda: {"vector": 0.45, "keyword": 0.2, "rule": 0.25, "freshness": 0.1}, + alias="RETRIEVAL_RERANK_WEIGHTS_JSON", + ) + + # --- 华为账号登录(FR-ACC-01;联调待 AGC 配置,见《前置开通清单》§3)------ + huawei_jwks_url: str = "https://oauth-login.cloud.huawei.com/oauth2/v3/certs" + huawei_audience: str = "" # = AGC App ID;空配置时验签必须拒绝而不是跳过 + huawei_issuer: str = "https://accounts.huawei.com" + huawei_jwks_cache_seconds: int = 86400 + + # --- 手机号验证码登录(FR-ACC-04/14,TD-32)-------------------------------- + # fake = 验证码打日志(dev);tencent = 真实发送(待签名审核通过后切换) + sms_provider: str = "fake" + tencent_sms_secret_id: str = "" + tencent_sms_secret_key: str = "" + tencent_sms_sdk_app_id: str = "" + tencent_sms_sign_name: str = "" + tencent_sms_template_id: str = "" + sms_code_ttl_seconds: int = 300 + sms_code_length: int = 6 + sms_verify_max_attempts: int = 5 + sms_resend_cooldown_seconds: int = 60 + sms_hourly_limit: int = 5 + sms_daily_limit: int = 10 + sms_ip_daily_limit: int = 20 + sms_device_daily_limit: int = 10 + # --- Observability ----------------------------------------------------- otel_enabled: bool = False otel_service_name: str = "meida-api" @@ -182,6 +217,7 @@ class Settings(BaseSettings): "llm_budget_tiers", "task_bonus_rules", "upload_scenes", + "retrieval_rerank_weights", mode="before", ) @classmethod diff --git a/packages/shared/errors.py b/packages/shared/errors.py index 5ac076f..8ab5d01 100644 --- a/packages/shared/errors.py +++ b/packages/shared/errors.py @@ -32,6 +32,8 @@ class ErrorCode(IntEnum): REFRESH_TOKEN_INVALID = 40103 REFRESH_TOKEN_EXPIRED = 40104 SESSION_REVOKED = 40105 + SMS_CODE_INVALID = 40110 + SMS_CODE_EXPIRED = 40111 FORBIDDEN = 40300 QUOTA_EXHAUSTED = 40301 CONSENT_REQUIRED = 40302 @@ -42,6 +44,8 @@ class ErrorCode(IntEnum): STATE_CONFLICT = 40900 TASK_BONUS_ALREADY_CLAIMED = 40910 RATE_LIMITED = 42900 + SMS_SEND_TOO_FREQUENT = 42910 + SMS_DAILY_LIMIT = 42911 # --- 5xxxx server ----------------------------------------------------- INTERNAL_ERROR = 50000 @@ -58,6 +62,7 @@ class ErrorCode(IntEnum): LLM_PROMPT_CACHE_VIOLATION = 60004 MODERATION_UNAVAILABLE = 60005 LLM_SCENE_DEGRADED = 60006 + SMS_PROVIDER_FAILED = 61000 @dataclass(frozen=True, slots=True) @@ -84,6 +89,8 @@ ERROR_META: dict[ErrorCode, ErrorMeta] = { ErrorCode.REFRESH_TOKEN_INVALID: ErrorMeta(401, "登录状态无效", "请重新登录"), ErrorCode.REFRESH_TOKEN_EXPIRED: ErrorMeta(401, "登录状态已过期", "请重新登录"), ErrorCode.SESSION_REVOKED: ErrorMeta(401, "该会话已登出", "请重新登录"), + ErrorCode.SMS_CODE_INVALID: ErrorMeta(401, "验证码错误", "请检查后重试"), + ErrorCode.SMS_CODE_EXPIRED: ErrorMeta(401, "验证码已过期", "请重新获取"), ErrorCode.FORBIDDEN: ErrorMeta(403, "没有访问权限"), ErrorCode.QUOTA_EXHAUSTED: ErrorMeta(403, "今日额度已用完", "明天恢复,或完成任务多换几次"), ErrorCode.CONSENT_REQUIRED: ErrorMeta(403, "需要先完成授权", "请阅读并同意后继续"), @@ -98,6 +105,8 @@ ERROR_META: dict[ErrorCode, ErrorMeta] = { 409, "这个周期的任务奖励已领取", "下个周期再来" ), ErrorCode.RATE_LIMITED: ErrorMeta(429, "操作太频繁了", "请稍后再试"), + ErrorCode.SMS_SEND_TOO_FREQUENT: ErrorMeta(429, "发送太频繁了", "请稍后再试"), + ErrorCode.SMS_DAILY_LIMIT: ErrorMeta(429, "今日发送次数已达上限", "明天再试"), ErrorCode.INTERNAL_ERROR: ErrorMeta(500, "服务开小差了", "请稍后重试"), ErrorCode.DB_ERROR: ErrorMeta(500, "服务开小差了", "请稍后重试"), ErrorCode.TASK_STATE_INVALID: ErrorMeta(500, "任务状态异常", "请到任务中心重试"), @@ -120,6 +129,7 @@ ERROR_META: dict[ErrorCode, ErrorMeta] = { ErrorCode.LLM_SCENE_DEGRADED: ErrorMeta( 503, "该功能今日暂时关闭", "AI 服务今日已达服务上限,明天恢复" ), + ErrorCode.SMS_PROVIDER_FAILED: ErrorMeta(502, "短信服务暂时不可用", "请稍后重试"), } diff --git a/packages/shared/models.py b/packages/shared/models.py index 024aa93..018a74a 100644 --- a/packages/shared/models.py +++ b/packages/shared/models.py @@ -18,9 +18,11 @@ from datetime import datetime from decimal import Decimal from typing import Any +from pgvector.sqlalchemy import Vector from sqlalchemy import ( BigInteger, Boolean, + Computed, DateTime, Index, Integer, @@ -31,8 +33,9 @@ from sqlalchemy import ( Text, UniqueConstraint, func, + text, ) -from sqlalchemy.dialects.postgresql import JSONB +from sqlalchemy.dialects.postgresql import JSONB, TSVECTOR from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column @@ -304,3 +307,175 @@ class ObEvent(Base): Index("ix_ob_event_name_ts", "event_name", "server_ts"), Index("ix_ob_event_user_ts", "user_id", "server_ts"), ) + + +# --------------------------------------------------------------------------- +# kb 域(知识库,架构文档 §6.2) +# --------------------------------------------------------------------------- +class KbEntity(Base): + """12 类实体统一表:attrs JSONB 承载类目差异化字段(架构 §6.2). + + 状态机 draft→reviewing→published→archived 由 service 层校验; + published 实体不可直接改(version 递增需回 draft 重走评审)。 + """ + + __tablename__ = "kb_entity" + + entity_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False) + entity_type: Mapped[str] = mapped_column(String(32), nullable=False) + name: Mapped[str] = mapped_column(String(128), nullable=False) + attrs: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict) + status: Mapped[str] = mapped_column(String(16), nullable=False, default="draft") + version: Mapped[int] = mapped_column(Integer, nullable=False, default=1) + source: Mapped[str | None] = mapped_column(String(255)) + license: Mapped[str | None] = mapped_column(String(64)) + updated_by: Mapped[str | None] = mapped_column(String(64)) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + + __table_args__ = ( + UniqueConstraint("entity_type", "name", name="uq_kb_entity_type_name"), + Index("ix_kb_entity_type_status", "entity_type", "status"), + ) + + +class KbRelation(Base): + """邻接表 + 递归 CTE 做 1~3 跳(TD-09:不上 Neo4j).""" + + __tablename__ = "kb_relation" + + relation_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False) + from_id: Mapped[int] = mapped_column(BigInteger, nullable=False) + rel_type: Mapped[str] = mapped_column(String(64), nullable=False) + to_id: Mapped[int] = mapped_column(BigInteger, nullable=False) + weight: Mapped[Decimal] = mapped_column(Numeric(4, 3), nullable=False, default=Decimal("1")) + confidence: Mapped[Decimal] = mapped_column(Numeric(4, 3), nullable=False, default=Decimal("1")) + evidence: Mapped[str | None] = mapped_column(String(255)) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + + __table_args__ = ( + UniqueConstraint("from_id", "rel_type", "to_id", name="uq_kb_relation_triple"), + Index("ix_kb_relation_to", "to_id"), + ) + + +class KbRule(Base): + """决策表(TD-10 自研规则引擎)。conditions/effects 为 JSONB DSL.""" + + __tablename__ = "kb_rule" + + rule_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False) + rule_key: Mapped[str] = mapped_column(String(64), nullable=False) + category: Mapped[str] = mapped_column(String(32), nullable=False) + version: Mapped[int] = mapped_column(Integer, nullable=False, default=1) + status: Mapped[str] = mapped_column(String(16), nullable=False, default="draft") + priority: Mapped[int] = mapped_column(Integer, nullable=False, default=60) + conditions: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False) + effects: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False) + is_hard: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) + confidence: Mapped[Decimal] = mapped_column( + Numeric(4, 3), nullable=False, default=Decimal("0.9") + ) + evidence: Mapped[str | None] = mapped_column(String(255)) + author: Mapped[str | None] = mapped_column(String(64)) + reviewer: Mapped[str | None] = mapped_column(String(64)) + published_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + + __table_args__ = ( + UniqueConstraint("rule_key", name="uq_kb_rule_key"), + Index("ix_kb_rule_status_priority", "status", "priority"), + ) + + +class KbChunk(Base): + """向量 + 全文双索引,支撑混合检索(架构 §8.3). + + embedding_model/version 必填:没有版本号的向量不可重跑(T-P2-03)。 + tsv 是生成列——写入时不要手工填。 + """ + + __tablename__ = "kb_chunk" + + chunk_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False) + entity_id: Mapped[int] = mapped_column(BigInteger, nullable=False) + chunk_type: Mapped[str] = mapped_column(String(32), nullable=False, default="text") + text: Mapped[str] = mapped_column(Text, nullable=False) + embedding = mapped_column(Vector(1024), nullable=False) + embedding_model: Mapped[str] = mapped_column(String(64), nullable=False) + embedding_version: Mapped[str] = mapped_column(String(16), nullable=False) + tsv = mapped_column( + TSVECTOR, Computed("to_tsvector('simple', text)", persisted=True), nullable=False + ) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + + __table_args__ = (Index("ix_kb_chunk_entity", "entity_id"),) + + +class KbAsset(Base): + """图像资产。tryon_ready 是换装闭环的关键标志位(架构 §6.2).""" + + __tablename__ = "kb_asset" + + asset_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False) + entity_id: Mapped[int] = mapped_column(BigInteger, nullable=False) + asset_type: Mapped[str] = mapped_column(String(32), nullable=False) + cos_key: Mapped[str] = mapped_column(String(255), nullable=False) + spec_check: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict) + tryon_ready: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) + review_status: Mapped[str] = mapped_column(String(16), nullable=False, default="pending") + license: Mapped[str | None] = mapped_column(String(64)) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + + __table_args__ = ( + Index("ix_kb_asset_entity", "entity_id"), + Index("ix_kb_asset_tryon_ready", "tryon_ready", postgresql_where=text("tryon_ready")), + ) + + +class KbColor(Base): + """自建色板(TD-18:规避「四季色彩/CMB」商标)。Lab 用于 ΔE 数值匹配.""" + + __tablename__ = "kb_color" + + color_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False) + season_code: Mapped[str] = mapped_column(String(16), nullable=False) + role: Mapped[str] = mapped_column(String(8), nullable=False) + lab_l: Mapped[Decimal] = mapped_column(Numeric(6, 2), nullable=False) + lab_a: Mapped[Decimal] = mapped_column(Numeric(6, 2), nullable=False) + lab_b: Mapped[Decimal] = mapped_column(Numeric(6, 2), nullable=False) + hex: Mapped[str] = mapped_column(String(7), nullable=False) + name_cn: Mapped[str] = mapped_column(String(64), nullable=False) + + __table_args__ = (Index("ix_kb_color_season", "season_code"),) + + +class KbGap(Base): + """知识缺口埋点:无检索结果的查询,驱动内容生产(T-P2-06).""" + + __tablename__ = "kb_gap" + + gap_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False) + query_text: Mapped[str] = mapped_column(Text, nullable=False) + filter_ctx: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict) + hit_count: Mapped[int] = mapped_column(Integer, nullable=False, default=1) + first_seen_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + last_seen_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + + __table_args__ = (Index("ix_kb_gap_query", "query_text"),) diff --git a/pyproject.toml b/pyproject.toml index 1446ba0..8408254 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,9 +21,15 @@ dependencies = [ "orjson>=3.10", "prometheus-client>=0.21", "cos-python-sdk-v5>=1.9.44", + "tencentcloud-sdk-python-sms>=3.1.153", ] [project.optional-dependencies] +spike = [ + # 算法验证 SPIKE(T-P0-10/12/16)专用;模型文件在 models/(已 gitignore) + "onnxruntime>=1.20", + "opencv-python-headless>=4.10", +] dev = [ "pytest>=8.3", "pytest-asyncio>=0.24", diff --git a/uv.lock b/uv.lock index fa60890..626847c 100644 --- a/uv.lock +++ b/uv.lock @@ -706,6 +706,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/8e/50f46a9c0ce8d2861a394c1347caae037ea0431d2f67d7feb151cbc4649a/filelock-3.32.3-py3-none-any.whl", hash = "sha256:7f0ca4bcc0e181c60dbbd8aa9ab5b120ebb99e4e064e83636340056f833a1f09", size = 98901, upload-time = "2026-08-13T16:00:03.974Z" }, ] +[[package]] +name = "flatbuffers" +version = "25.12.19" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, +] + [[package]] name = "greenlet" version = "3.5.5" @@ -1185,6 +1193,7 @@ dependencies = [ { name = "sqlalchemy", extra = ["asyncio"] }, { name = "structlog" }, { name = "tenacity" }, + { name = "tencentcloud-sdk-python-sms" }, { name = "uvicorn", extra = ["standard"] }, ] @@ -1200,6 +1209,10 @@ dev = [ { name = "respx" }, { name = "ruff" }, ] +spike = [ + { name = "onnxruntime" }, + { name = "opencv-python-headless" }, +] [package.metadata] requires-dist = [ @@ -1212,6 +1225,8 @@ requires-dist = [ { name = "httpx", specifier = ">=0.28" }, { name = "import-linter", marker = "extra == 'dev'", specifier = ">=2.1" }, { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.13" }, + { name = "onnxruntime", marker = "extra == 'spike'", specifier = ">=1.20" }, + { name = "opencv-python-headless", marker = "extra == 'spike'", specifier = ">=4.10" }, { name = "orjson", specifier = ">=3.10" }, { name = "pgvector", specifier = ">=0.3.6" }, { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.0" }, @@ -1228,9 +1243,10 @@ requires-dist = [ { name = "sqlalchemy", extras = ["asyncio"], specifier = ">=2.0.36" }, { name = "structlog", specifier = ">=24.4" }, { name = "tenacity", specifier = ">=9.0" }, + { name = "tencentcloud-sdk-python-sms", specifier = ">=3.1.153" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.32" }, ] -provides-extras = ["dev"] +provides-extras = ["spike", "dev"] [[package]] name = "mypy" @@ -1295,6 +1311,130 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, ] +[[package]] +name = "numpy" +version = "2.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/80/db0b4559e57ec36362bedbb05530a87fafbcb6067708c946967a41d449e7/numpy-2.5.2.tar.gz", hash = "sha256:d482d171c406ae88c5b19cad3b6a1c4c5209f886ab74bc44c2c865c23f52d860", size = 20773161, upload-time = "2026-08-09T13:48:27.962Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/72/dccb0aaf40972777283303919f613964227266d0c13adebb79ac124f1c3e/numpy-2.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:14e373cfc6387177e8409dac3c7159be8eb05cd77096cd7c950268b86f62831c", size = 16891693, upload-time = "2026-08-09T13:44:51.702Z" }, + { url = "https://files.pythonhosted.org/packages/60/2e/b5aee50a1f74ac815cf8331812cb8251e29024025de462e0c047641c614c/numpy-2.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4bbd96c833ecc8cc069ce518078fc8c60cb9cbfb0fea5b7a803ad65035596d03", size = 11903109, upload-time = "2026-08-09T13:44:55.501Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f4/29e78102a80601cf034d4e9767022cffeca2c3b4c926e1754572ca95593d/numpy-2.5.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:6e8172ddfcf5cf74b811d372b570b83c60bd2de87a6fbfbebdadb4a9bd9c6cbb", size = 5350202, upload-time = "2026-08-09T13:44:58.401Z" }, + { url = "https://files.pythonhosted.org/packages/11/4b/dcd3b7eadaf4035d2c7a4289d232523a6964f602598ef7674e4bd7291f93/numpy-2.5.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:65f188481f1669e26f62b701e8205d19e460fa4a9b52a1414ba382330e4a3414", size = 6687736, upload-time = "2026-08-09T13:45:00.813Z" }, + { url = "https://files.pythonhosted.org/packages/e5/21/4947e0e9d6c9fc2e2ff15b8949049ee44f63adb9cacc729ab8793f97e712/numpy-2.5.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ee9c4eeb8454b3660a8b53493563c3e121c2fc94fbd72b848ef814ed7b676a9", size = 15612696, upload-time = "2026-08-09T13:45:04.151Z" }, + { url = "https://files.pythonhosted.org/packages/3a/5f/62d28cf019460c7f1394105b4d49d9911a9c444cb77ab0bd95a204c5a6de/numpy-2.5.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3cdec01fa790a186d430433fdd4d4ffb70eed6f0eeb4bf05c8dbe2dce0a9bcb8", size = 16722264, upload-time = "2026-08-09T13:45:07.714Z" }, + { url = "https://files.pythonhosted.org/packages/14/25/3f0be4c1b9fdf5dd5e708a6806978564d7c46a055c000496309ff2a2f8af/numpy-2.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7999d4ddb0c4025018373fd787510d46e04c769467af22869707b3c1cfd459ab", size = 16974396, upload-time = "2026-08-09T13:45:11.316Z" }, + { url = "https://files.pythonhosted.org/packages/22/72/6262cbdeeb45da9d971e40715f579d791603ba8ec0b5e2db1ac55454421d/numpy-2.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1f017dc0875c9209d219f97feceb7d54c2661bb243deb4114478e1295808af7", size = 18476044, upload-time = "2026-08-09T13:45:14.869Z" }, + { url = "https://files.pythonhosted.org/packages/36/33/29208b8b075bde62d26a81d14b358c42b0f69b6cabd98d4ff97f37f22b05/numpy-2.5.2-cp312-cp312-win32.whl", hash = "sha256:d6a48072864e3324e194a8fbb3c657bcc5b5c869dbc64c9537b1d5c862572c0a", size = 6072817, upload-time = "2026-08-09T13:45:17.867Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b9/87fea2769fe1c47c1b5b01d8310772c9d1a85d485de7cf386ef7a3332b02/numpy-2.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:28ac63476ec7651484215ee7fa15a1f78b57c14621f01e392afe17b9a1390ce4", size = 12464674, upload-time = "2026-08-09T13:45:20.734Z" }, + { url = "https://files.pythonhosted.org/packages/14/52/032b97e00461ab0809bbe4c588b035620e5a14b8cdee47ecddefc7b17d33/numpy-2.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:27650bb0e7140fa3d37b9923b4803645e0b125d190f326eecfd3f4dad8e8ade1", size = 10397131, upload-time = "2026-08-09T13:45:23.73Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d2/6b24738a0ef4557d189b150046cd07823c50e4273e8aebd651222e24306f/numpy-2.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8e4cb9a754c8a0c62eaa88273a5fba3391f4a610d1dee893c0755da31c083f15", size = 16886595, upload-time = "2026-08-09T13:45:27.323Z" }, + { url = "https://files.pythonhosted.org/packages/65/60/f2d208d366f263f39c6e69ed309290717aab41078b6d04c9be2a84fa2a07/numpy-2.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:52c808f96484f5571a5cc863775ce50247c17dfb3b0361f8ed6b4b0456f80080", size = 11896845, upload-time = "2026-08-09T13:45:31.638Z" }, + { url = "https://files.pythonhosted.org/packages/3c/79/81e0bf24f4d020a2b1d5cd297a9f60c3f24eeb116f9bba5870443f7b6a4a/numpy-2.5.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:29d81e97f668489cba8ebfd796b9bdd453525d35dd9e162e2daec94bf3fc7740", size = 5343880, upload-time = "2026-08-09T13:45:34.373Z" }, + { url = "https://files.pythonhosted.org/packages/ba/cc/e3141cf06d1a8a2c7e107543fe1269c1d1af760d4d683c0794a4ee1127c2/numpy-2.5.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afb3f0632d6b2e3ba04dbce8d1e48d321b369138b73830b5ca371a0e8d479d56", size = 6682264, upload-time = "2026-08-09T13:45:36.7Z" }, + { url = "https://files.pythonhosted.org/packages/29/f1/2a64a307d92c5d98f5255a4014eb43bb6103ee477087b61ecae44a3aa9b9/numpy-2.5.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0aadf13b60048d501e05fa699efaf7734e2494f3498a4c2a5521d822640324f3", size = 15609566, upload-time = "2026-08-09T13:45:39.518Z" }, + { url = "https://files.pythonhosted.org/packages/7b/44/59a1eb68e773c4098d107ef34a0dbdeca501d72ffcfbff9a7707343921ce/numpy-2.5.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29b86ff8a6cc556b47ec6b64b194815cc80e6bf5eedcc6cddfd65318cb0b4eee", size = 16709995, upload-time = "2026-08-09T13:45:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/8a/4c/3e54d4ddbc359a1295f8b633e8106bcd4d7d4a206e82df051bdfb3058755/numpy-2.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6950c4b7dd562453090548ba7f5da7e59f57f85663f15d5dcc60e249192f7e59", size = 16972511, upload-time = "2026-08-09T13:45:47.094Z" }, + { url = "https://files.pythonhosted.org/packages/f2/9f/02e371638ebf19b66d46231e4be52999e87f32d1961b113bc45656608b22/numpy-2.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9727f472d2f3888053b8a75ab0cb94745a9de224bb5846dbadc0092101bc71d", size = 18465609, upload-time = "2026-08-09T13:45:50.808Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ae/ad6645abc7a3510fe48e8ea1ab4598166f500057ef4ebf38bfad4f1577de/numpy-2.5.2-cp313-cp313-win32.whl", hash = "sha256:4f9744f9fbdcea0bc552e8f19e1f141f811a3f9bc2be2cc6e86d982cab23e3f4", size = 6070204, upload-time = "2026-08-09T13:45:54.111Z" }, + { url = "https://files.pythonhosted.org/packages/15/20/f3489f86d81ea460b2bcdceaed094142ca6579f6be0ec527b781d39afe68/numpy-2.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:85aaccb24182c25df891ad0ec333585967e115269d5f1b17f2c9ae005bc96657", size = 12460532, upload-time = "2026-08-09T13:45:57.167Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/35b31dde1b283b79de828b80f876afd8c94e28fe1e9c375f89e261cc4c0d/numpy-2.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:bd68ece1553d2023c09a4226d9e41c586ad2d20594d1a456186c33513d2cb3f2", size = 10396725, upload-time = "2026-08-09T13:46:00.478Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f8/c3b222bf075b50afd8e949a07a15c4b312a4a84bd8102a332bcd953cbbb4/numpy-2.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d787cf769c3baeb5f6235e778edb52c08dfa923789b5958f28e6450f96107cb1", size = 16885180, upload-time = "2026-08-09T13:46:03.939Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/2c1d4b1987795a92b5bbf7c24fe249ab96aa2573ab0d7604802c189d7b86/numpy-2.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:24b9dc2e3d84aa58523798805194e23e736f3f6ce2d1a5b92583ae734e6dbda8", size = 11907878, upload-time = "2026-08-09T13:46:07.045Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ee/d08226fc858044355983a6e5b94f08ff6f3969e0a2b160a4a89f0ddb3445/numpy-2.5.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:9e9413326d726c2545bfa65d2c0876871e8d8386e77f992c1d426e180bbd4323", size = 5354922, upload-time = "2026-08-09T13:46:10.04Z" }, + { url = "https://files.pythonhosted.org/packages/94/f0/6d3d933056440ebbc5e6bad92065fc6c26a48a84a36b1208580e94eea76c/numpy-2.5.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:60e902ac295855348a5ca2ea4c89108989a9f5fddfad3dfc0a8f36b10358567e", size = 6679168, upload-time = "2026-08-09T13:46:12.275Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3b/ecd49dd90033cceb2704d88ca905d4d7d89b0e8c739608754ffd325fa820/numpy-2.5.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50e500dc868e9313530ce12ba470fe50ff3afe3d62993ed6eff652dacd555b65", size = 15624501, upload-time = "2026-08-09T13:46:15.322Z" }, + { url = "https://files.pythonhosted.org/packages/c7/99/461bd36dbdfac6c1c53efa370bd55a83227542d0d118f1677dbf1a3dacd5/numpy-2.5.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318b9a4c845dbea06708a29c84ee429cc3065048db34cdb799047643492050ee", size = 16713701, upload-time = "2026-08-09T13:46:18.949Z" }, + { url = "https://files.pythonhosted.org/packages/f9/9c/2b251df9e8a5d647b62b0cbc1b90a91850c1cf4859ecb532fd0b4eacff6c/numpy-2.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:34c319e2963be042673fb46570501b2f06c41924e17e3563d58646b4380dfb68", size = 16986065, upload-time = "2026-08-09T13:46:23.006Z" }, + { url = "https://files.pythonhosted.org/packages/8f/25/20de43f53ff1390534a124475055a19f01fe10c920a0fd11b8e18d6d6052/numpy-2.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f06571a052127dc1b4e8b83029b4d1b20daa2b64a31cdd181fc6bc774e9000eb", size = 18470031, upload-time = "2026-08-09T13:46:27.102Z" }, + { url = "https://files.pythonhosted.org/packages/56/5e/0c577ca308d6da5eb79b546ba10bbe5b60148192194e2da060913b1de4f1/numpy-2.5.2-cp314-cp314-win32.whl", hash = "sha256:2cc779226e476d1e1f08c74068c419e60f41a9e0e069c92f6671d31d5c985e98", size = 6121028, upload-time = "2026-08-09T13:46:30.046Z" }, + { url = "https://files.pythonhosted.org/packages/15/5c/7bcbd5b11f94199073320410cddcbb80cee62415bfeb540874b265c2d922/numpy-2.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:7587f53dfbd5edc0f7b87c6217b4c6d2d1f2ef9c3da70bc1315e7db5f8d7ec9d", size = 12597627, upload-time = "2026-08-09T13:46:32.886Z" }, + { url = "https://files.pythonhosted.org/packages/87/bc/4d0b06fba0da90ccc75af62823cb9dcedb6c9ea0cffa058cb2c9ee773a77/numpy-2.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:3e4c367352d3747784248a227fbec218e193b56f7e6692e3b64fc805478ecfdf", size = 10680414, upload-time = "2026-08-09T13:46:36.036Z" }, + { url = "https://files.pythonhosted.org/packages/cd/17/f429aac9dc08833a0d0f188eba38c532a751b1a1f2ca6018a37b455cb321/numpy-2.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b879fb674276e331513fb136b78dbc6bd3c848309e0d841cfd63be3896c4cfc1", size = 12026967, upload-time = "2026-08-09T13:46:39.084Z" }, + { url = "https://files.pythonhosted.org/packages/ca/9f/d0849de96a2a4ceaa16662f18ee13eaa9c0aa418269fdc8c4857c56b11da/numpy-2.5.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f", size = 5473874, upload-time = "2026-08-09T13:46:42.075Z" }, + { url = "https://files.pythonhosted.org/packages/89/3c/8df216d4a4a5422a3de045301cf7df8ea47286d76f5cb7160b0128ac26b7/numpy-2.5.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:3a2f061cebd9e3d23bdcfaaded5e2293a4c6a5b60fa42df85d410a725ce621bf", size = 6789276, upload-time = "2026-08-09T13:46:44.387Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3a/20d7e9891c4ddfadd6ff8d95bf4b29f353d8e1770553de2099880551dfb9/numpy-2.5.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6df895598c0edcb41030126c89e0f353b07d93238116143b7405e937359736c4", size = 15659154, upload-time = "2026-08-09T13:46:47.538Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d6/f3aa3d2688bf501b858835c6bd087ae9b51a56ae6fca8e2b0990abd177af/numpy-2.5.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ab3d4a901f844ea836c3e80bf463c6a27d7f3c14e8e292fcf28d348b25b9bce", size = 16748909, upload-time = "2026-08-09T13:46:51.442Z" }, + { url = "https://files.pythonhosted.org/packages/7d/8f/1c5cae8d2baf86ab802ae97a00be55bc7e21ebc11b12bbc33376c5f05342/numpy-2.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:cebc2d6dbb605a7703d59751dea4bd6b0ab127a5a4338a6f432df1936fef8b26", size = 17027685, upload-time = "2026-08-09T13:46:55.095Z" }, + { url = "https://files.pythonhosted.org/packages/5c/27/71d3467404aedc1c24ce79610f91b52b0b0f466c43a701aa56fc75c145ab/numpy-2.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eaca7ff36f0f52e2111ec71f169d8fd3e889e7ddc0d2592e0d703fd8d3ce8fac", size = 18501181, upload-time = "2026-08-09T13:46:59.09Z" }, + { url = "https://files.pythonhosted.org/packages/14/2f/42921d27c40aea7e077f4a423ae509fd9220b028cd787bafefd8ab2b3a5f/numpy-2.5.2-cp314-cp314t-win32.whl", hash = "sha256:ddf47472af2e4280d79bac82304f5e80150211f1b9e614b760061d5fdfbb6eba", size = 6271085, upload-time = "2026-08-09T13:47:01.903Z" }, + { url = "https://files.pythonhosted.org/packages/75/e6/bad5f5d56de9b1971bac959963dda276d35c40f1854475005434bbe08692/numpy-2.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:44ef9675d908e65f9953063837c3277730f3f4437615a4cdab67b366cabaf884", size = 12787971, upload-time = "2026-08-09T13:47:04.963Z" }, + { url = "https://files.pythonhosted.org/packages/df/05/f608795cb34391acd67e38d94a3c36abd8d8576293a3a80727d7595c372c/numpy-2.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:eaa088384c46f519dacb93b7ec483a6d6b19a4a2085ae4f25ab9b1c43d387d1e", size = 10750306, upload-time = "2026-08-09T13:47:07.976Z" }, + { url = "https://files.pythonhosted.org/packages/33/c6/28de0191c5f82b7d42a0a51390ba98587048aa93a39fafb05bdbe6e8d00c/numpy-2.5.2-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f", size = 16885274, upload-time = "2026-08-09T13:47:11.439Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/973ca116000d244897e468ea1aff30b589e5022e3c8744b71706fe33bd57/numpy-2.5.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:50a68f4bacd8a2b33d8da3d2269d0d78500f86ea582e4786dc10f5ef2c2c6842", size = 11907846, upload-time = "2026-08-09T13:47:15.128Z" }, + { url = "https://files.pythonhosted.org/packages/78/d9/8c4b3937ef204cb2fd88d389ccd0f265a2ffb11f35a01d2064cf46714bd6/numpy-2.5.2-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:e79aba74ffaf5f78a050d777c184cddf8fdffabab38acf5f3ef1fecbc17895d6", size = 5354892, upload-time = "2026-08-09T13:47:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/b6ee65ea2999fdb7023935e108e6fb776ee4082aa15f159acfa857e578c8/numpy-2.5.2-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:9a0731745a72a184490a582fb4af2533512bd071ace67785b5fdffc0ae58dce8", size = 6679309, upload-time = "2026-08-09T13:47:20.456Z" }, + { url = "https://files.pythonhosted.org/packages/43/f3/acb18d8b137a393c8e7803a8c994c9e64bde3930692a69d826993113a159/numpy-2.5.2-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ec954036759bcee3aa484f8603bd9c14f3e776293b85578b8734c2d72777c69", size = 15625850, upload-time = "2026-08-09T13:47:24.365Z" }, + { url = "https://files.pythonhosted.org/packages/a9/bf/a8e9bb0db815a0e265b5744ebedd3af0bd5faad8604e5b50a1cd012f3c91/numpy-2.5.2-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc649493697006bc90614a5f0bbc8cb3cb1866715c474e473694968d7e6b99ab", size = 16713664, upload-time = "2026-08-09T13:47:27.965Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c3/6e913736b3dd6582344af32418b5fb9dab34282e8a8174ae1d54ceb0fc13/numpy-2.5.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:cf7de32f486e4ac9e2d93b810f9e9ac72a728dd46a32a0bb403222f27f653514", size = 16986749, upload-time = "2026-08-09T13:47:31.541Z" }, + { url = "https://files.pythonhosted.org/packages/80/09/7d3b23eff5c7428ef6c01e6f7052bb60d504c4d33e317b36b8959c24ad97/numpy-2.5.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2ffa7bacab3e2ee1b19ed31766bb60bb380b68c23f051e199c5cc598afd68710", size = 18470495, upload-time = "2026-08-09T13:47:35.364Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a4/68a321d825374f6eb677ffe8ef8c6b9a328304e6fd2e39d9530822776607/numpy-2.5.2-cp315-cp315-win32.whl", hash = "sha256:6b588cc8f902d6bff201c19fd00c43ab8545671e3554d014e12e14139e5e8617", size = 6120696, upload-time = "2026-08-09T13:47:38.561Z" }, + { url = "https://files.pythonhosted.org/packages/c8/23/deafbb1700f79fae9cd1e91220f133d124cc267de1b584da3fbf6db2f6cd/numpy-2.5.2-cp315-cp315-win_amd64.whl", hash = "sha256:07d4e89f3a9ab0a9ba24264ccdb642b3dd951b2281e8883a5481a4aa79cc31a7", size = 12597324, upload-time = "2026-08-09T13:47:41.401Z" }, + { url = "https://files.pythonhosted.org/packages/33/cd/3272ba105e3bbbdaeb11357eda31e7a6825ffe159e8171665660299a948f/numpy-2.5.2-cp315-cp315-win_arm64.whl", hash = "sha256:a610dc7e3c52edd39c2bc2375ff9c3fd59cb3ad00e4472d36f83bc1457145788", size = 10680466, upload-time = "2026-08-09T13:47:44.873Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0e/58370637b1bb70a5c9ce2b43f4b521ccb224e36ccb76a6596b17ae4b447c/numpy-2.5.2-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:40f4d451aed46a8046a1aae41c4e55fb3612273df9c502480135e1501576a34b", size = 16993947, upload-time = "2026-08-09T13:47:48.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/93/2abcb807712b289d6d60fe4cf30532f98974a8396d885650f3ba5a13026e/numpy-2.5.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c081cbe16ba1ab53078e5ff29013621e33c509eedab055775d956427712c236e", size = 12025331, upload-time = "2026-08-09T13:47:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3a/2898e003a5fbaf87e76c039b4ee1f5eb390471b4ffe74887c1f34c4e791e/numpy-2.5.2-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a", size = 5472336, upload-time = "2026-08-09T13:47:55.403Z" }, + { url = "https://files.pythonhosted.org/packages/61/a5/23f69d07c544597b29758b31b55c27dc9d541012a2c1496189fef702aec2/numpy-2.5.2-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:6a9bb119fb8dd21ba30b3f0e555b7e2b081bd9883af21ec9c1c633d161cda3a8", size = 6788387, upload-time = "2026-08-09T13:47:58.192Z" }, + { url = "https://files.pythonhosted.org/packages/15/ea/c0dbdbcf22f43782510a3e492dd3da73c6112b69cac8929d16d127536fc4/numpy-2.5.2-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a839318485284a6fb31be4f8f2c91c8f2cb22f4543c4a8903f12b0671ffe07cc", size = 15667096, upload-time = "2026-08-09T13:48:01.562Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5e/29c73c31748cdb0f7566642125ba17fd5b56780cddf891b085dab27e4466/numpy-2.5.2-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba0a474801b8dc67b66bf465548abc90e82b44d2611b5770f33008dcabffe8ec", size = 16751730, upload-time = "2026-08-09T13:48:05.706Z" }, + { url = "https://files.pythonhosted.org/packages/47/95/02501e8454796bb58dadf7a99d3181e0b464bf264e1003039572f9779fac/numpy-2.5.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:0a4035ae1129ff8777f08bfbd44f1e5d8e9c049ce0c2dd78fc0d92c13e7251c0", size = 17038686, upload-time = "2026-08-09T13:48:09.627Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b5/53a681d91b5c82687067d8ea5035e02d917b5509d6f334cb06484a954714/numpy-2.5.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:77843ca236b777e67f8d6b3660ea116e499612703a0ecd7093f316201eb9d8e2", size = 18507727, upload-time = "2026-08-09T13:48:13.744Z" }, + { url = "https://files.pythonhosted.org/packages/42/06/6e11443f7b64ee376c860506091103bf68f92d2cab9e8d96d4501babf07c/numpy-2.5.2-cp315-cp315t-win32.whl", hash = "sha256:7354826bc6f8f69402e9b7fe28d15fcd34feebd74f856f111585c5b0c9fb0251", size = 6269775, upload-time = "2026-08-09T13:48:17.543Z" }, + { url = "https://files.pythonhosted.org/packages/f1/18/195d6b86cd72dbbc501edfa778005fa6b87afd34c153e46028cd3a0938f4/numpy-2.5.2-cp315-cp315t-win_amd64.whl", hash = "sha256:e5651f3f87add730ee6608d915009e19c911fba0cb000c7e3ea994b7d768eb12", size = 12782559, upload-time = "2026-08-09T13:48:21.023Z" }, + { url = "https://files.pythonhosted.org/packages/b4/07/458c344f0f0c178f4481dad5cca790626ffe4c34eabf9467069d06ee4999/numpy-2.5.2-cp315-cp315t-win_arm64.whl", hash = "sha256:5f8e00be2ec6f45f4e8a41a527f68d44a7d96fee92a650e4d8b1326f77f61e6e", size = 10748103, upload-time = "2026-08-09T13:48:24.21Z" }, +] + +[[package]] +name = "onnxruntime" +version = "1.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flatbuffers" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "protobuf" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/f8/dcbe7700dca82fa540035abd3c868fe5ad0f86af00b9a3db7c2e27d15c7d/onnxruntime-1.28.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:26ff0fdd06efb6c155bae95387a09db1a2be89c7a03e4d0bffd5a171cc2826da", size = 19141362, upload-time = "2026-07-25T01:22:36.965Z" }, + { url = "https://files.pythonhosted.org/packages/28/5b/1d77e62097fdbe07e2dc827f389b1c4c0c275f6fab0369a8f46d2461af27/onnxruntime-1.28.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e81a23df16e7acb9d51b06d30cc098e49315ef9180f97bc2221d167b4b04d9c", size = 17050628, upload-time = "2026-07-25T01:21:40.481Z" }, + { url = "https://files.pythonhosted.org/packages/95/df/5486ab03e9be288d5268867054c8b04bebcf95bfd12e801c05cc67703dab/onnxruntime-1.28.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0a83bdb70d143cede762b677789bf2a7acca54b3fb82565601d5c30695aa933c", size = 19214257, upload-time = "2026-07-25T01:22:01.695Z" }, + { url = "https://files.pythonhosted.org/packages/3e/3b/986ca67c274932ba9ac5332fb10de56f643dfd433c74e33f8ae8f847cf24/onnxruntime-1.28.0-cp312-cp312-win_amd64.whl", hash = "sha256:c35064f9b3c43c81c5d5d282091401d0f1ff22796d93ccade4ea2ece5e137ab8", size = 13755036, upload-time = "2026-07-25T01:22:26.89Z" }, + { url = "https://files.pythonhosted.org/packages/1d/46/059dba81d46c6ba88e0c2d1c64321ac8098847d678423300a183d42ecbd6/onnxruntime-1.28.0-cp312-cp312-win_arm64.whl", hash = "sha256:e02feeb0165c5f13b4cc954738078d59b90128516ac12b671ee24a530242bf02", size = 13454462, upload-time = "2026-07-25T01:22:17.38Z" }, + { url = "https://files.pythonhosted.org/packages/9c/12/3807e2b17d9eb71d3cb78ed2ba76869b05c637c9b9d6112e636098b0c97a/onnxruntime-1.28.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:31410f544674f534c2f27348af52ef81682ca9c8719154bf4d48f0ef23823b1e", size = 19141759, upload-time = "2026-07-25T01:21:53.765Z" }, + { url = "https://files.pythonhosted.org/packages/c0/23/b46045c3bf67a9cf54c12f5df0f018a422c65fbb9d6072b10071bebfaae2/onnxruntime-1.28.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f649dd6f6452d12a8059888aa489fe519e062e18793dac72b9efa0f9fdb64135", size = 17049339, upload-time = "2026-07-25T01:21:43.005Z" }, + { url = "https://files.pythonhosted.org/packages/78/b6/8c5396e7894e77c5a7d1e026f3acb9dd39c4b5644e412e37a0055eaa3bc5/onnxruntime-1.28.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54fa221d669282bd8f582708ce4c96010a7e9fb0661f9006b37fe2fedafb73fe", size = 19214329, upload-time = "2026-07-25T01:22:04.133Z" }, + { url = "https://files.pythonhosted.org/packages/56/f1/51225c202edba4dfc94e1ea03f3d78f1aaf307da75fd792c0ce1946b2514/onnxruntime-1.28.0-cp313-cp313-win_amd64.whl", hash = "sha256:1a1a19175464665c9b8d50bc916f216cc0b569110045b7bbca8f9f290b186f58", size = 13755033, upload-time = "2026-07-25T01:22:29.302Z" }, + { url = "https://files.pythonhosted.org/packages/f4/db/f59f715edfdd96a051f32b5ef0e680a20a8755d4ecd75f63090e960e347a/onnxruntime-1.28.0-cp313-cp313-win_arm64.whl", hash = "sha256:cfab507abe09d6ffeb817eee07944d452fdc0b00fdcef34cab4db10a45e378c7", size = 13454175, upload-time = "2026-07-25T01:22:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/47/28/810314fa88647af9f4cdaf438a30ad1cfebebb53ded55499232d7a0094e6/onnxruntime-1.28.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac301f53b1930402fc46c368e268acfed02f3207272aaff05070d7e09f96f031", size = 17057307, upload-time = "2026-07-25T01:21:45.492Z" }, + { url = "https://files.pythonhosted.org/packages/3d/cc/9e9f193cc0f29f263a8f09ec08487aed6c96ee856d5fd77da32a425c1949/onnxruntime-1.28.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7f022a1103cae591c75fc4565589a515f2ddd14a6ac8e8a05812dfeda142e28", size = 19222954, upload-time = "2026-07-25T01:22:06.952Z" }, + { url = "https://files.pythonhosted.org/packages/4e/eb/952314c451d9463e5c9aed9978eec76cf32930d407d9ab8700dd0f4ea1ea/onnxruntime-1.28.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:8adff67a3f28257b37cfe945a7e952e4122666aa8c91a0380862e9fd4c2ed19f", size = 19143748, upload-time = "2026-07-25T01:21:56.297Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e9/139180b4dd810329aaa42c238b4e6383c906202d98609ae29d66eb7c32b1/onnxruntime-1.28.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc2565e487b4896fb988d6383577d875d958e071fc5f6c3550bd5d02ae98264b", size = 17051950, upload-time = "2026-07-25T01:21:48.606Z" }, + { url = "https://files.pythonhosted.org/packages/03/88/9432428273356ad3c8aa01f52c1b3e7f53c4c0192748f41ad983872b436b/onnxruntime-1.28.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6afdc83f1317c136e92fc29f5ee9f058de59d87c0b22cee3fdbfbaa0ccc2098a", size = 19214924, upload-time = "2026-07-25T01:22:09.727Z" }, + { url = "https://files.pythonhosted.org/packages/bb/e2/6feb3a43517aaf2b1bf7e46897ba5eb81a29717f7d7901420614d5ee4653/onnxruntime-1.28.0-cp314-cp314-win_amd64.whl", hash = "sha256:f2a3b9e30ce880d4ca54999cb313569e36da4f62eefe25f87be18f43e9a3a4d5", size = 14093738, upload-time = "2026-07-25T01:22:31.629Z" }, + { url = "https://files.pythonhosted.org/packages/fc/8f/83974a1e201dc2e58e5e7111bcaeb1ca2413e9c41f505d26419ee9e3dddf/onnxruntime-1.28.0-cp314-cp314-win_arm64.whl", hash = "sha256:07fb3cbe990d6bf0ab3c22bfbbfb0e314151266046ea6edb4a07f556b4258c5f", size = 13821117, upload-time = "2026-07-25T01:22:22.387Z" }, + { url = "https://files.pythonhosted.org/packages/0d/83/00e606bc25c756d76a267370c39b7516ad52f9cf134d7ff2bff8b6108bc4/onnxruntime-1.28.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e562d6e36a749f6764481c0ddb0f2af3d0b5a3c164291361d08803c557f369af", size = 17055518, upload-time = "2026-07-25T01:21:51.08Z" }, + { url = "https://files.pythonhosted.org/packages/94/a9/68707e1ce345cbdbcd4df65932ebc82a673e917d63eda0007ebcff948691/onnxruntime-1.28.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f6e92367ddce1e4d33cf295024f40192be6c6171a09208f515ba169ced06c8e", size = 19222976, upload-time = "2026-07-25T01:22:12.474Z" }, +] + +[[package]] +name = "opencv-python-headless" +version = "5.0.0.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/99/76b7c80252aa83c1af16393454aafd125a0287101afe8deb0a6821af0e30/opencv_python_headless-5.0.0.93.tar.gz", hash = "sha256:b82f9831daab90b725c7c1ee1b36cb5732c367096ac76d119e64e14eb70d5f3c", size = 81817738, upload-time = "2026-07-02T07:01:06.039Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/7c/8c8097891c509d98cd128493835c95631c80be6a8f37ed9d25716c2e16f1/opencv_python_headless-5.0.0.93-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:030ca5e0837a2963ab36ef896baa9767eb8d2b83353fb28af5a521e40dd8756f", size = 48322581, upload-time = "2026-07-02T05:50:34.207Z" }, + { url = "https://files.pythonhosted.org/packages/90/8c/eab2ad388c3cbab2a350c10c2ef19ce6bd099240afc31789032c996bab52/opencv_python_headless-5.0.0.93-cp37-abi3-macosx_14_0_x86_64.whl", hash = "sha256:1e55af3abfb462eeeabe5c775f12bdb36216d8a93a3583d69e6bd6e1d6ba7d00", size = 34782894, upload-time = "2026-07-02T05:51:39.856Z" }, + { url = "https://files.pythonhosted.org/packages/ec/78/afca939f40ffe2b2380bfa86f812b2f7d4acc5a27b27dc41b49cad7ce7b4/opencv_python_headless-5.0.0.93-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:10818d91510e05c04568ae12b5cd120779c70c01bf897b001a6221fe430df80f", size = 36521085, upload-time = "2026-07-02T06:55:24.429Z" }, + { url = "https://files.pythonhosted.org/packages/2b/97/8170e9819764c47e436c130d3ff6cfb73b58f923eae9d3a03d8982b04aec/opencv_python_headless-5.0.0.93-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:09a872a157c1376ab922a69bbf22f9a95bcc7b658a9d8b436a60212b02b2eeb4", size = 56563598, upload-time = "2026-07-02T06:55:47.355Z" }, + { url = "https://files.pythonhosted.org/packages/3a/98/1a28a7101e31801042b3098871a74b76c61581d328ef40774ff4edb53a56/opencv_python_headless-5.0.0.93-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:840bd717c21e5c11cadadc022a823315ea417f961213d06b4df010e019eb16f4", size = 39648433, upload-time = "2026-07-02T06:56:04.255Z" }, + { url = "https://files.pythonhosted.org/packages/9b/21/f6ef335f6e65724aa78b8d792b48d40a48c381715f1e62f5a5049e09d07e/opencv_python_headless-5.0.0.93-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ed709fdf9aa0bd1f2ed8549e71d19449b03a675bb581eb292285f6861953be37", size = 61204038, upload-time = "2026-07-02T06:56:41.823Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8f/b8756467ea991449a293797f6b3fa80fcfdd29598a0a60d1cd5715b96e61/opencv_python_headless-5.0.0.93-cp37-abi3-win32.whl", hash = "sha256:c6bcd96b185975ea240d22cfdb15a1f6d080cc95264cfbe2621f21bb144d89b9", size = 35411237, upload-time = "2026-07-02T05:50:12.901Z" }, + { url = "https://files.pythonhosted.org/packages/b8/88/763b967f7efd7226b82c9fae16d560cba049b1f0c036647e65c610fd636e/opencv_python_headless-5.0.0.93-cp37-abi3-win_amd64.whl", hash = "sha256:829717b6a95554f273e49e357cee3b3a2a26b6f4842fbc1bed2b45bdd8f87e0e", size = 43825962, upload-time = "2026-07-02T05:50:09.627Z" }, +] + [[package]] name = "orjson" version = "3.11.9" @@ -1430,6 +1570,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl", hash = "sha256:01c0891d7f9237d5e339f7d3e42cdae80b7534abb1c7c0e3352efba6231492f2", size = 392288, upload-time = "2026-07-26T20:56:12.512Z" }, ] +[[package]] +name = "protobuf" +version = "7.35.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/01/9ef0afd7999eb9badb3a768b4aedd78c86d4c65cfaf1958ab276199e76b4/protobuf-7.35.1.tar.gz", hash = "sha256:ce115a26fe0c39a2c29973d914d327e516a6455464489fe3cd1e51a1b354f81a", size = 458717, upload-time = "2026-06-11T21:55:40.257Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/03/8aeeb7458d22546bf64b5250ca1daeb5ff757d900e8e4a7476c6f0db843e/protobuf-7.35.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:24f857477359a85c0c235261b8ba905fd51b2562f4a64ca1df5473f29850cbf6", size = 433226, upload-time = "2026-06-11T21:55:31.719Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/dfb89eb0e652a1ff073c39a59fb5e3a83cfe9b57a2c83fa6d78270101767/protobuf-7.35.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:11d6b0ec246892d85215b0a13ca6e0233cf5284b68f0ac02646427f4ff88a799", size = 328847, upload-time = "2026-06-11T21:55:34.035Z" }, + { url = "https://files.pythonhosted.org/packages/0f/58/dc12f2cd484951524af6e3382c785869b9b3fb5e52ee95ae23add53ee8f9/protobuf-7.35.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:b73f9489a4b8b1c9cb1f8ed951c736392592edb24b9d6819f36d2e10b171d5b4", size = 344030, upload-time = "2026-06-11T21:55:34.941Z" }, + { url = "https://files.pythonhosted.org/packages/e4/be/5b3cfe508bfab6761414ff944e3366eb13be4fd71efcd69450f89ba39f43/protobuf-7.35.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:74758715c53d7158fb76caf4f0cfdacc5329a4b1bb994f865d6cf302d413a1c4", size = 327130, upload-time = "2026-06-11T21:55:35.921Z" }, + { url = "https://files.pythonhosted.org/packages/d8/bc/6d6c7ba8709c85f8f2c390b2b118d6fb08a783676a572271851bf45a7d22/protobuf-7.35.1-cp310-abi3-win32.whl", hash = "sha256:353652e4efd0bca5b5fc2656abf8307ef351f0cf938c9eba09f0e09c20a25c30", size = 428945, upload-time = "2026-06-11T21:55:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/0a/19/8d0cb6f20a1ef7b18f1c8986ad5783f22f84cce39c6ce9a6e645ea55192e/protobuf-7.35.1-cp310-abi3-win_amd64.whl", hash = "sha256:230a75ddfc2de4806e56696ce9640c1cdfdb6543b7cfce98d42a4c0a0e7bdb87", size = 439996, upload-time = "2026-06-11T21:55:38.123Z" }, + { url = "https://files.pythonhosted.org/packages/19/c7/5f7c636ec43e0c545e28d1f1db71990108306f7bdcb89f069ba97e428e7f/protobuf-7.35.1-py3-none-any.whl", hash = "sha256:4bc97768d8fe4ad6743c8a19403e314511ed9f6d13205b687e52421c023ac1b9", size = 171659, upload-time = "2026-06-11T21:55:39.155Z" }, +] + [[package]] name = "pycparser" version = "3.0" @@ -1872,6 +2027,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, ] +[[package]] +name = "tencentcloud-sdk-python-common" +version = "3.1.156" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/82/a69a3983e175bf80ec58615ea1daac159fc8086601423d87a54cfc505732/tencentcloud_sdk_python_common-3.1.156.tar.gz", hash = "sha256:7306bcaf3128ab3dbc96bc3117fe3562e303fb654e366bffa565d67cc85f4cd1", size = 22672, upload-time = "2026-08-13T07:15:59.396Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/dd/47dba883bfa116bb75fc3344bd0a4df6f65894070e4c4ac3d81b60aa0110/tencentcloud_sdk_python_common-3.1.156-py2.py3-none-any.whl", hash = "sha256:94711a6c8130a1a3f1f24e708e71f646e5aa561fe4fefc3e0ac37389732b3339", size = 34275, upload-time = "2026-08-13T07:15:57.163Z" }, +] + +[[package]] +name = "tencentcloud-sdk-python-sms" +version = "3.1.153" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tencentcloud-sdk-python-common" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/21/f22f5e3c7ab0665de70da8f09d488e266fd9d264f224bf46ff1013b7fe52/tencentcloud_sdk_python_sms-3.1.153.tar.gz", hash = "sha256:3d00d97bb9210095d507dd2b7649c2782663b50d3f5bf3029fd3e385d0610495", size = 52292, upload-time = "2026-08-09T18:50:40.494Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/ed/c7e77cc2c64d1b4270102c7cfdf6840c217e4c5f9e70d185ceaae6d2672e/tencentcloud_sdk_python_sms-3.1.153-py2.py3-none-any.whl", hash = "sha256:951dcdb0fb34ba6c2be4782ddfd87eb3b06820b9b6e8c6014429e4f6993ae631", size = 60246, upload-time = "2026-08-09T18:50:38.483Z" }, +] + [[package]] name = "typing-extensions" version = "4.16.0"