Two defects that only surface against the real DeepSeek API, both of which break the cost model rather than the code: deepseek-v4-flash enables thinking mode by default. The same single-turn question took 13.5s with it on and 1.8s with it off, and the thinking tokens are billed at the output rate. That misses NFR-PERF-02b (15s per turn) and inflates every conversational call. SceneConfig now carries an explicit `thinking` flag, all four V1 scenes set it to false, and the provider always sends the parameter rather than relying on the upstream default. Reasoning tokens are recorded separately in Usage.tokens_reasoning so the cost of ever turning it back on is visible. DeepSeek moves to peak/off-peak pricing on 2026-08-17, with peak (Beijing 09:00-12:00 and 14:00-18:00) at twice the off-peak rate. Reports are generated mostly during peak hours, so a single blended rate would leave ob_ai_call -- the only input to V2 pricing -- off by nearly a factor of two. PeakAwareRateTable resolves the rate at call time; cost is computed and stored then, never recomputed later at whatever the current rate happens to be. Also here: - Replace the live API key in the redaction test with a synthetic one. The test that proves we redact secrets was itself a secret leak waiting for its first commit. - Correct LLM_COST_RATES_JSON, which held placeholder rates. - Make ids.SnowflakeGenerator take an injectable clock so rollover and clock-rollback are testable without patching a slots class. - Rename exceptions to the -Error suffix (N818) and clear the remaining ruff, mypy and import-linter findings, including two dead lines: a duplicate pytestmark assignment and a no-op assert on a None return. Gates: ruff, mypy (96 files incl. tests), 4/4 import contracts, detect-secrets, 166 unit + 3 integration tests, migration up/down round-trip.
82 lines
4.8 KiB
Bash
Executable File
82 lines
4.8 KiB
Bash
Executable File
# 复制为 .env 后填入真实值。.env 已在 .gitignore 中,不要提交。
|
|
# dev/staging/prod 由部署平台注入环境变量,不使用 .env(架构文档 §11.2)。
|
|
|
|
APP_ENV=local
|
|
APP_NAME=meida-backend
|
|
API_PREFIX=/api/v1
|
|
DEBUG=true
|
|
LOG_LEVEL=INFO
|
|
LOG_JSON=true
|
|
# 需要额外脱敏的字段名,逗号分隔;默认清单见 packages/shared/logging.py
|
|
LOG_EXTRA_REDACT_KEYS=
|
|
|
|
# ── 数据库 / Redis ─────────────────────────────────────────────────────────
|
|
# 端口刻意用 55432 / 56379,避免与本机已有的 PostgreSQL、Redis 抢端口
|
|
DATABASE_URL=postgresql+asyncpg://meida:meida@localhost:55432/meida
|
|
DB_POOL_SIZE=10
|
|
DB_MAX_OVERFLOW=20
|
|
DB_ECHO=false
|
|
|
|
REDIS_URL=redis://localhost:56379/0
|
|
CELERY_BROKER_URL=redis://localhost:56379/1
|
|
CELERY_RESULT_BACKEND=redis://localhost:56379/2
|
|
|
|
# ── 鉴权 ──────────────────────────────────────────────────────────────────
|
|
# 生成:openssl rand -hex 32 。staging/prod 若沿用占位值会启动失败
|
|
JWT_SECRET=change-me-local-only-do-not-use-outside-local
|
|
JWT_ALGORITHM=HS256
|
|
JWT_ACCESS_TTL_SECONDS=900
|
|
JWT_ISSUER=meida
|
|
# 免登录换 token 的调试入口,仅本地/联调可开
|
|
DEV_AUTH_ENABLED=true
|
|
|
|
# ── LLM 路由 ──────────────────────────────────────────────────────────────
|
|
LLM_PRIMARY_PROVIDER=deepseek
|
|
LLM_BACKUP_PROVIDER=zhipu
|
|
LLM_STRUCTURED_MAX_RETRIES=2
|
|
LLM_CIRCUIT_FAILURE_THRESHOLD=3
|
|
LLM_CIRCUIT_COOLDOWN_SECONDS=30
|
|
# 按场景覆盖模型与采样参数,如 {"agent_chat":{"temperature":0.5}}
|
|
LLM_SCENE_OVERRIDES_JSON={}
|
|
|
|
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
|
|
DEEPSEEK_MODEL=deepseek-v4-flash
|
|
DEEPSEEK_TIMEOUT_SECONDS=30
|
|
|
|
ZHIPU_API_KEY=
|
|
ZHIPU_BASE_URL=https://open.bigmodel.cn/api/paas/v4
|
|
ZHIPU_MODEL=glm-4-flash
|
|
ZHIPU_TIMEOUT_SECONDS=30
|
|
|
|
# ── Prompt 缓存纪律 ───────────────────────────────────────────────────────
|
|
# strict:固定前缀里出现时间戳/随机值等易变 token 直接启动失败。
|
|
# 命中缓存的输入价格只有未命中的 1/30,前缀被污染会让输入成本翻数十倍。
|
|
LLM_CACHE_PREFIX_MODE=strict
|
|
PROMPTS_DIR=prompts
|
|
|
|
# ── 成本单价(单位:分 / 百万 token)──────────────────────────────────────
|
|
# 取自 DeepSeek 官方价目表。2026-08-17 00:00 起改为峰谷定价:
|
|
# 高峰(北京时间 09:00-12:00、14:00-18:00)单价是空闲时段的 2 倍。
|
|
# 因此这里要配两套,由 packages/llm/cost.py 按调用时刻选择。
|
|
# flash 空闲:缓存命中 ¥0.05 / 输入 ¥1.5 / 输出 ¥4.5 每百万 token
|
|
LLM_COST_RATES_JSON={"deepseek-v4-flash":{"input_cents_per_mtok":"150","cached_input_cents_per_mtok":"5","output_cents_per_mtok":"450"},"deepseek-v4-pro":{"input_cents_per_mtok":"450","cached_input_cents_per_mtok":"15","output_cents_per_mtok":"1350"},"glm-4-flash":{"input_cents_per_mtok":"10","cached_input_cents_per_mtok":"10","output_cents_per_mtok":"10"}}
|
|
# flash 高峰:缓存命中 ¥0.10 / 输入 ¥3 / 输出 ¥9 每百万 token
|
|
LLM_COST_RATES_PEAK_JSON={"deepseek-v4-flash":{"input_cents_per_mtok":"300","cached_input_cents_per_mtok":"10","output_cents_per_mtok":"900"},"deepseek-v4-pro":{"input_cents_per_mtok":"900","cached_input_cents_per_mtok":"30","output_cents_per_mtok":"2700"},"glm-4-flash":{"input_cents_per_mtok":"10","cached_input_cents_per_mtok":"10","output_cents_per_mtok":"10"}}
|
|
# 未登记的模型按这个价算。刻意取高档:宁可高估触发熔断,也不要低估成本形成盲区
|
|
LLM_DEFAULT_COST_RATE_JSON={"input_cents_per_mtok":"300","cached_input_cents_per_mtok":"10","output_cents_per_mtok":"900"}
|
|
LLM_PEAK_PRICING_ENABLED=true
|
|
|
|
# ── 全局日成本熔断(NFR-COST-02)─────────────────────────────────────────
|
|
# 按用户配额封不住总成本(总成本 = 用户数 × 人均,用户数无上限),
|
|
# 真正封顶的只有这个全局开关。1000_00 分 = ¥1000/天
|
|
LLM_DAILY_BUDGET_ENABLED=true
|
|
LLM_DAILY_BUDGET_CENTS=100000
|
|
LLM_BUDGET_TIMEZONE_OFFSET_HOURS=8
|
|
|
|
# ── 可观测性 ──────────────────────────────────────────────────────────────
|
|
OTEL_ENABLED=false
|
|
OTEL_SERVICE_NAME=meida-api
|
|
OTEL_EXPORTER_OTLP_ENDPOINT=
|
|
PROMETHEUS_ENABLED=true
|