fix(txn+security): 仓储改 flush + 启动校验/兜底 + job.error 脱敏 + SSE 异常硬化
P0-1 SqlCredentialStore/save_draft 由自提交改 flush,端点/服务统一 commit (新增 CredentialStore.commit() 统一提交点;token 刷新落库显式提交); 补多凭据一请求中途失败整体回滚集成测试。 P0-2 启动校验 _fernet(enc_key) 快速失败 + catch-all Exception → ErrorEnvelope; credential_enc_key 改 SecretStr。 P0-3 run_job 异常分类:AppError 存 code+message,其余存通用文案不泄 str(exc)。 P0-4 评审/正文 SSE 失败先发 error 事件,尾部 commit 包 try/except。 P1-4 max_version 加 FOR UPDATE 行锁消除 TOCTOU。 P1-5 scan_overdue 谓词下推 + 批量 UPDATE RETURNING。 P1-10 移除 OAuth user_code 日志。 P2 provider_deps 改调网关 build_adapter;accept_service Committable Protocol; CORS 白名单收窄;request_id 安全字符集白名单;stdlib 日志接管;读端点 404 校验; httpx timeout;测试用合法 Fernet key;类型化响应模型(JobResponse/DimensionEntry/ ReviewConflictView/selling_points)+路由 ErrorEnvelope responses(供 codegen)。
This commit is contained in:
@@ -10,12 +10,11 @@ import uuid
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from openai import AsyncOpenAI
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from ww_config import get_settings
|
||||
from ww_db import get_session
|
||||
from ww_llm_gateway.adapters.base import Capabilities
|
||||
from ww_llm_gateway.adapters.openai_compat import OpenAICompatAdapter
|
||||
from ww_llm_gateway.factory import build_adapter
|
||||
from ww_shared import AppError, ErrorCode
|
||||
|
||||
from ww_api.security.credentials import (
|
||||
@@ -84,11 +83,12 @@ class GatewayProviderProbe:
|
||||
except CredentialKeyError as exc:
|
||||
raise AppError(ErrorCode.INTERNAL, str(exc)) from exc
|
||||
|
||||
client = AsyncOpenAI(api_key=api_key, base_url=base_url)
|
||||
adapter = OpenAICompatAdapter(provider=provider, client=client)
|
||||
# 经网关工厂构造适配器(base_url 单点归网关 build_adapter,不在此本地构造 AsyncOpenAI)。
|
||||
adapter = build_adapter(provider, api_key=api_key, base_url=base_url)
|
||||
try:
|
||||
# 最小探测:列模型即可验证 Key 有效(不消耗生成额度)。
|
||||
await client.models.list()
|
||||
# 最小探测:列模型即可验证 Key 有效(不消耗生成额度)。底层 AsyncOpenAI 由适配器持有
|
||||
# (已知 provider 为 OpenAI 兼容,见 _PROVIDER_BASE_URLS);读私有客户端属同仓既有约定。
|
||||
await adapter._client.models.list() # type: ignore[attr-defined] # noqa: SLF001
|
||||
except Exception as exc: # noqa: BLE001 — 任一失败都映射为 LLM 不可用
|
||||
raise AppError(
|
||||
ErrorCode.LLM_UNAVAILABLE,
|
||||
@@ -101,4 +101,4 @@ class GatewayProviderProbe:
|
||||
def get_provider_probe(
|
||||
store: Annotated[CredentialStore, Depends(get_credential_store)],
|
||||
) -> GatewayProviderProbe:
|
||||
return GatewayProviderProbe(store, get_settings().credential_enc_key)
|
||||
return GatewayProviderProbe(store, get_settings().credential_enc_key.get_secret_value())
|
||||
|
||||
Reference in New Issue
Block a user