- 规则 GET 不存在 project → 404(原返误导性空 200,掩盖坏 id):list_rules 加 项目存在校验(仿 C1/H2)。 - 规则 content 纯空白 → 422(原 strip 前校验 min_length,空白行入库成垃圾): 改 StringConstraints(strip_whitespace=True, min_length=1)。 - 档位路由 tier 限定 writer/analyst/light(Tier Literal)→ 未知档位 422 (原接受任意字符串)。 回归测试:test_generation(GET rules 404)/ test_rules(空白 content 422)/ test_settings_providers(未知 tier 422)。门禁绿:ruff/format/mypy(210)/pytest。
79 lines
2.2 KiB
Python
79 lines
2.2 KiB
Python
"""提供商凭据/档位路由的请求/响应 schema(C3 / ARCH §4.7, §7.2)。
|
||
|
||
snake_case;响应一律 **掩码 Key**,绝不含明文。前端经 OpenAPI 生成 TS 类型消费。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from pydantic import BaseModel, Field
|
||
from ww_llm_gateway.types import Tier
|
||
|
||
|
||
class ProviderView(BaseModel):
|
||
"""已配置提供商(掩码视图)。"""
|
||
|
||
provider: str
|
||
masked_key: str # 形如 sk-…1234;明文永不出现
|
||
|
||
|
||
class TierRoutingView(BaseModel):
|
||
"""档位 → provider:model 路由(含回退链)。"""
|
||
|
||
tier: str
|
||
provider: str
|
||
model: str
|
||
fallback: list[str] = Field(default_factory=list)
|
||
|
||
|
||
class ProvidersResponse(BaseModel):
|
||
"""GET/PUT 响应:已配置提供商(掩码)+ 当前档位路由。"""
|
||
|
||
providers: list[ProviderView] = Field(default_factory=list)
|
||
tier_routing: list[TierRoutingView] = Field(default_factory=list)
|
||
|
||
|
||
class ProviderCredentialInput(BaseModel):
|
||
"""单条提供商凭据写入。"""
|
||
|
||
provider: str = Field(min_length=1)
|
||
api_key: str = Field(min_length=1) # 明文入站,加密入库,绝不回显
|
||
|
||
|
||
class TierRoutingInput(BaseModel):
|
||
"""单条档位路由写入。"""
|
||
|
||
# tier 限定已知档位 writer/analyst/light;未知档位 → 422(QA MEDIUM:原接受任意字符串)。
|
||
tier: Tier
|
||
provider: str = Field(min_length=1)
|
||
model: str = Field(min_length=1)
|
||
fallback: list[str] = Field(default_factory=list)
|
||
|
||
|
||
class ProvidersUpsertRequest(BaseModel):
|
||
"""PUT 请求:可同时 upsert 若干凭据与档位路由(幂等)。"""
|
||
|
||
credentials: list[ProviderCredentialInput] = Field(default_factory=list)
|
||
tier_routing: list[TierRoutingInput] = Field(default_factory=list)
|
||
|
||
|
||
class TestConnectionRequest(BaseModel):
|
||
"""POST /test:最小探测请求。"""
|
||
|
||
provider: str = Field(min_length=1)
|
||
|
||
|
||
class CapabilitiesView(BaseModel):
|
||
"""探测得到的能力矩阵(镜像网关 `Capabilities`)。"""
|
||
|
||
structured_output: bool = False
|
||
prefix_cache: bool = False
|
||
thinking: bool = False
|
||
|
||
|
||
class TestConnectionResponse(BaseModel):
|
||
"""POST /test 响应:连通性 + 能力矩阵。"""
|
||
|
||
provider: str
|
||
ok: bool
|
||
capabilities: CapabilitiesView
|