fix(llm): price by call time and stop paying for thinking tokens
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.
This commit is contained in:
@@ -1,76 +1,79 @@
|
||||
.DEFAULT_GOAL := help
|
||||
SHELL := /bin/bash
|
||||
.PHONY: help install up down logs migrate downgrade revision run worker \
|
||||
test test-unit test-integration lint fmt typecheck imports secrets check smoke psql redis-cli
|
||||
|
||||
UV := $(shell command -v uv 2>/dev/null)
|
||||
COMPOSE := docker compose -f deploy/docker-compose.dev.yml
|
||||
|
||||
ifdef UV
|
||||
RUN := uv run
|
||||
else
|
||||
RUN := .venv/bin/python -m
|
||||
endif
|
||||
help: ## 列出所有目标
|
||||
@grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
|
||||
| awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
.PHONY: help install lint fmt typecheck importlint test test-unit migrate makemigration up down dev worker smoke secrets clean
|
||||
|
||||
help: ## Show this help
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
install: ## Create the venv and install runtime + dev dependencies
|
||||
ifdef UV
|
||||
# ── 环境 ────────────────────────────────────────────────────────────────────
|
||||
install: ## 安装依赖(uv 管理,含开发依赖)
|
||||
uv sync --extra dev
|
||||
uv run pre-commit install || true
|
||||
else
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install --upgrade pip
|
||||
.venv/bin/pip install -e ".[dev]"
|
||||
.venv/bin/pre-commit install || true
|
||||
endif
|
||||
uv run pre-commit install
|
||||
|
||||
lint: ## ruff check + ruff format --check
|
||||
up: ## 起 PostgreSQL(pgvector) + Redis
|
||||
$(COMPOSE) up -d postgres redis
|
||||
|
||||
down: ## 停容器(保留数据卷)
|
||||
$(COMPOSE) down
|
||||
|
||||
logs: ## 跟踪容器日志
|
||||
$(COMPOSE) logs -f --tail=100
|
||||
|
||||
psql: ## 连进数据库
|
||||
$(COMPOSE) exec postgres psql -U meida -d meida
|
||||
|
||||
redis-cli: ## 连进 Redis
|
||||
$(COMPOSE) exec redis redis-cli
|
||||
|
||||
# ── 数据库 ──────────────────────────────────────────────────────────────────
|
||||
migrate: ## 迁移到最新版本
|
||||
uv run alembic upgrade head
|
||||
|
||||
downgrade: ## 回退一个版本
|
||||
uv run alembic downgrade -1
|
||||
|
||||
revision: ## 生成迁移脚本:make revision m="描述"
|
||||
uv run alembic revision --autogenerate -m "$(m)"
|
||||
|
||||
# ── 运行 ────────────────────────────────────────────────────────────────────
|
||||
run: ## 本地起 API(热重载)
|
||||
uv run uvicorn apps.api.main:app --reload --host 0.0.0.0 --port 8000
|
||||
|
||||
worker: ## 本地起 Celery worker
|
||||
uv run celery -A apps.worker.celery_app.celery_app worker -Q cpu,io -l INFO
|
||||
|
||||
# ── 质量防线(与 CI 一致,见 .github/workflows/ci.yml)─────────────────────
|
||||
lint: ## ruff 检查 + 格式校验
|
||||
uv run ruff check .
|
||||
uv run ruff format --check .
|
||||
|
||||
fmt: ## Auto-fix lint issues and format
|
||||
fmt: ## 自动修复与格式化
|
||||
uv run ruff check --fix .
|
||||
uv run ruff format .
|
||||
|
||||
typecheck: ## mypy strict on packages/domain + packages/llm, normal elsewhere (§16.2)
|
||||
typecheck: ## mypy(domain 与 llm 为 strict)
|
||||
uv run mypy packages/domain packages/llm
|
||||
uv run mypy apps packages/shared packages/identity packages/billing packages/task packages/agent packages/compliance
|
||||
uv run mypy apps packages/shared packages/identity packages/billing packages/task \
|
||||
packages/agent packages/compliance
|
||||
|
||||
importlint: ## Enforce the dependency rules of 架构文档 §4.2
|
||||
imports: ## 分层依赖契约(架构文档 §4.2)
|
||||
uv run lint-imports
|
||||
|
||||
test: lint typecheck importlint test-unit ## Full local quality gate (mirrors CI)
|
||||
|
||||
test-unit: ## pytest with coverage
|
||||
uv run pytest
|
||||
|
||||
secrets: ## Scan the working tree for committed credentials
|
||||
secrets: ## 敏感信息扫描
|
||||
uv run detect-secrets scan --baseline .secrets.baseline
|
||||
|
||||
migrate: ## Apply Alembic migrations to $$DATABASE_URL
|
||||
uv run alembic upgrade head
|
||||
test: test-unit ## 默认只跑单测(不连库、不发网络请求)
|
||||
|
||||
makemigration: ## Autogenerate a migration: make makemigration m="add xxx"
|
||||
uv run alembic revision --autogenerate -m "$(m)"
|
||||
test-unit:
|
||||
uv run pytest
|
||||
|
||||
up: ## Start PostgreSQL(pgvector) + Redis for local development
|
||||
$(COMPOSE) up -d postgres redis
|
||||
$(COMPOSE) ps
|
||||
test-integration: ## 需要 make up && make migrate
|
||||
MEIDA_RUN_INTEGRATION=1 uv run pytest -m integration
|
||||
|
||||
down: ## Stop the local stack (keeps volumes)
|
||||
$(COMPOSE) down
|
||||
check: lint typecheck imports test secrets ## 提交前一把过
|
||||
|
||||
dev: ## Run the API with autoreload
|
||||
uv run uvicorn apps.api.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
|
||||
worker: ## Run a Celery worker consuming the cpu/io queues
|
||||
uv run celery -A apps.worker.celery_app.celery_app worker -Q cpu,io -l info
|
||||
|
||||
smoke: ## End-to-end proof: compose + migrate + API + real DeepSeek call
|
||||
# ── 真实链路验证(会花钱)──────────────────────────────────────────────────
|
||||
smoke: ## 真调 DeepSeek,打印 ob_ai_call 与 bl_ledger
|
||||
bash scripts/smoke.sh
|
||||
|
||||
clean: ## Remove caches and build artefacts
|
||||
rm -rf .pytest_cache .mypy_cache .ruff_cache .coverage htmlcov dist build
|
||||
find . -type d -name __pycache__ -prune -exec rm -rf {} +
|
||||
|
||||
Reference in New Issue
Block a user