Repository navigation
feat: durable worker, ops dashboard, and cloud single-host deployment - #9
Conversation
- cap token buckets with stale cleanup and LRU eviction - only accept demo tokens or signed JWTs as rate-limit identities - add tenant-scoped GET /api/ops/summary with versioned cost estimates - document the contract and cover both changes with tests Co-Authored-By: Claude Code <noreply@anthropic.com>
- add the approver-only Agent ops page with queue, LLM, tool, token and cost panels - link recent failures to the existing analysis trace - serialize tests within a spec and retry fixture seed races - cover role denial, aggregation and trace navigation in Playwright Co-Authored-By: Claude Code <noreply@anthropic.com>
- require declared expected tools and order for every real case - fail closed when criteria are missing - stop spending budget on auth or balance failures - report versioned cost estimates and blocked status Co-Authored-By: Claude Code <noreply@anthropic.com>
- add 42 fixed synthetic queries and a hashing/BGE-M3 comparison runner - reuse production seed and search paths with isolated databases - bypass machine proxies only for loopback embedding gateways - fail closed when BGE-M3 is unavailable instead of substituting another model Co-Authored-By: Claude Code <noreply@anthropic.com>
- add the 12-call DeepSeek evaluation report and sanitized JSON - add the 42-query hashing baseline and BGE-M3 BLOCKED report - update evaluation and test reports with actual results and limits - refresh acceptance screenshots Co-Authored-By: Claude Code <noreply@anthropic.com>
- cover project background, architecture and three-length introductions - add 25 likely questions with answers, sources and live demos - separate verified execution from simulation, design and blocked work - include ten skeptical questions with honest limits Co-Authored-By: Claude Code <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e99a016f9e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| usage = run.get("usage") | ||
| if not isinstance(usage, dict): | ||
| runs_without_usage += 1 | ||
| unpriced_runs += 1 | ||
| continue |
There was a problem hiding this comment.
Exclude uninvoked models from missing-usage accounting
When an analysis has no anomaly or insufficient data, workflow.verify() persists an LLM record with status="not_requested" and no usage; disabling the provider similarly produces configured=False fallback records without making a model call. This branch treats all of these as unpriced calls, so even one routine no-anomaly analysis changes an otherwise fully priced window to partial and hides the cost on the dashboard. Preserve their status counts, but exclude records known not to have invoked a model from usage and pricing completeness calculations.
Useful? React with 👍 / 👎.
| cost = { | ||
| "status": cost_status, | ||
| "currency": "USD", | ||
| "amount_usd": round(cost_total, 6) if priced_runs else None, |
There was a problem hiding this comment.
Return null for the amount when pricing is incomplete
When a window contains both a priced run and a run with missing usage or an unknown model, this returns the priced subtotal as amount_usd even though the status is partial. The new /api/ops/summary contract in docs/CONTRACT.md explicitly requires amount_usd=null whenever any call cannot be priced; returning a numeric subtotal in the total field can cause API consumers to report an understated cost. Only populate this field when cost_status == "complete", or expose the known subtotal separately through a synchronized contract change.
AGENTS.md reference: AGENTS.md:L7-L7
Useful? React with 👍 / 👎.
- aggregate job counts in SQL and stream only bounded detail rows - read LLM history from durable audit events with legacy fallback - dedupe success and terminal audit events for the same model call - exclude no-call runs from token/cost denominators - return partial with amount_usd=null when any call is unpriced - expose known_subtotal_usd separately and sync frontend contract Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7da915e0c8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| estimate = estimate_cost_usd(run.get("model"), usage) | ||
| if estimate is None: | ||
| llm_stats["unpriced_runs"] += 1 | ||
| else: | ||
| llm_stats["priced_runs"] += 1 |
There was a problem hiding this comment.
Check per-round usage before marking a native run fully priced
For a native tool run where one provider response includes usage and another omits it, run_tool_loop() retains only the known tokens in its aggregate usage, while persisting the missing usage in rounds. Pricing that aggregate here marks the entire run as priced, so /api/ops/summary returns cost.status="complete" and a numeric total that excludes the unreported round. Check per-round usage completeness before incrementing priced_runs; incomplete native runs must leave the total unknown, with any known cost reported separately. This preserves the contract's requirement that every actual call have usage before the cost is complete.
AGENTS.md reference: AGENTS.md:L7-L7
Useful? React with 👍 / 👎.
Price each native Function Calling round independently so a missing Usage in any round makes the run partial instead of reusing another round's usage. Expose logical model calls separately from actual Provider requests and internal retries, and update the contract, frontend types and ops dashboard to match. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7aff53bd1d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| try { setOps(await api.ops(identity.token, opsHours)) } | ||
| catch (value) { showError(value) } | ||
| finally { setOpsLoading(false) } |
There was a problem hiding this comment.
Ignore stale responses when changing the ops window
If the user changes the statistics window while an ops request is pending, both requests can update the same state without checking which request is current. A slower 24-hour response can therefore overwrite a completed 7-day response, leaving the selector on “最近 7 天” while displaying 24-hour counts and costs indefinitely. An earlier request can also clear the loading flag while the current request is still pending. Add a request-sequence guard or cancellation, as the existing refresh callback does, and only let the current request update data, errors, and loading state.
Useful? React with 👍 / 👎.
- Disable request-scoped BackgroundTasks when the in-process worker is off - Add python -m app.worker CLI with --once and --poll-seconds - Include all analysis states in ops by_status so it sums to jobs.total - Guard ops window changes against stale responses - Sync tests, frontend types, contract, and runbook Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b0900bff8e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| attempts = _number(run.get("attempts")) or 0 | ||
| rounds = run.get("rounds") | ||
| has_rounds = isinstance(rounds, list) and bool(rounds) | ||
| if attempts == 0 and run.get("usage") is None and not has_rounds: | ||
| return False |
There was a problem hiding this comment.
Exclude native circuit-breaker rejections from provider accounting
When the circuit breaker is open, CircuitBreakerProvider._before_call() rejects the request without contacting the provider, but run_tool_loop() still records one attempt and an empty round (backend/app/llm_tools.py:461–467). This predicate therefore classifies native circuit rejections as actual calls, inflating model_calls and provider_requests and adding an unpriced call. Because the breaker is shared across tenants, a tenant with no actual requests can consequently receive cost.status="partial" instead of no_data after another tenant trips it. Preserve the rejection's fallback status, but exclude its synthetic attempt/round from actual-call accounting, consistent with the ops API contract.
AGENTS.md reference: AGENTS.md:L7-L7
Useful? React with 👍 / 👎.
- add cloud overlay with dedicated worker, one-shot init, and HTTPS-only frontend - add cloud_prepare for non-Git secrets, htpasswd, and TLS material - document Windows 11 SSH deployment, backup/restore, and troubleshooting - add ten-point local Compose acceptance report - remove interview-specific materials and wording - refresh handoff status for Alembic, worker, and tenant isolation Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2cfe045038
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| _write_private( | ||
| auth_dir / "htpasswd", | ||
| _htpasswd_line(values["PROFITOPS_ACCESS_USER"], values["PROFITOPS_ACCESS_PASSWORD"]), | ||
| ) |
There was a problem hiding this comment.
Make the generated htpasswd readable by Nginx workers
On a Linux host where the preparation script runs as root or a normal deployment user, _write_private() creates this file with mode 0600 and the host user's ownership. compose.cloud.yaml bind-mounts it unchanged, but the nginx:1.27-alpine frontend serves requests through unprivileged Nginx workers that cannot read it. Consequently, valid Basic Auth requests to /, /api/demo, and /api/auth/login return HTTP 500, preventing access to the cloud demo even while its healthcheck passes. Give the password file ownership/group permissions readable by the container worker, while retaining restrictive permissions for the environment file and TLS private key.
Useful? React with 👍 / 👎.
概要
本 PR 完成 ProfitOps AI 的最终工程收口:持久化任务队列与独立 Worker、JWT/RBAC 与多租户隔离、可观测性/限流/熔断、Alembic 迁移、原生 Tool Calling 与 RAG 评测,以及云端单机 Compose 部署准备。最新提交
2cfe045增加独立 Worker 的云端覆盖配置、一次性初始化服务、HTTPS 入口和部署文档,并在隔离环境完成十项 Compose 验收。所有业务执行保持 simulation,不对真实广告、采购、支付产生影响。本 PR 不自动合并;合并前需要 CI 通过和人工审查。
最新提交(2cfe045):云端单机 Compose
compose.cloud.yaml:基于现有compose.yaml的覆盖配置。backend 只运行 FastAPI 并关闭内置 Worker/BackgroundTasks;worker 复用后端镜像执行python -m app.worker;db 使用 PostgreSQL 16 + 持久卷;frontend 由 Nginx 提供 HTTPS。backend/app/init.py:一次性初始化入口,执行 schema 初始化、身份 Seed、演示数据和知识库 Seed。init服务先执行alembic upgrade head,再执行python -m app.init;backend/worker 通过service_completed_successfully等待它完成,避免全新数据库上的并发建表/Seed 竞态。frontend/nginx.cloud.conf:只发布 443;/、/api/demo、/api/auth/login由 HTTP Basic Auth 保护;/api/保持 Bearer 认证,避免 Nginx 覆盖 SPA 的Authorization头。scripts/cloud_prepare.py:生成不进入 Git 的随机数据库密码、JWT 密钥、analyst/approver token、Basic Auth 口令、htpasswd 和 TLS 材料;拒绝把开发.env当作云端配置文件。docs/CLOUD_DEPLOYMENT.md、docs/RUNBOOK.md、README.md:补充 Windows 11 SSH 部署、首次初始化/迁移、启停、备份恢复、排障、访问控制与验收记录。backend/tests、frontend/src/types.ts、docs/CONTRACT.md保持同步。本轮实际验证(2026-11-02)
.venv/Scripts/python.exe -m pytest backend/tests -qPROFITOPS_TEST_POSTGRES_URL的 PostgreSQL 专项npm --prefix frontend test -- --runnpm --prefix frontend run buildpython scripts/smoke.py --base-url http://127.0.0.1:8031docker compose -f compose.yaml -f compose.cloud.yaml --env-file .env.cloud.verify config --quietalembic -c alembic.ini upgrade head && python -m app.init0001_baseline、0002_tenant_scoped_idempotency;2 个身份、60 天演示数据、6 篇知识文档;重复执行幂等profitops-cloud-verify,PostgreSQL 16 + backend + worker + frontenddocs/CLOUD_DEPLOYMENT.mdconsoleErrors=[]、pageErrors=[]2cfe045LLM_PROVIDER=disabled、LLM_API_KEY="",付费调用 0Compose 十项验收摘要
queued。queued,steps=0。waiting_approval,attempts=1。StartedAt不变,新任务继续完成。queued。execution_id、simulation=true、replayed=true;审计包含recommendation.approve、execution.executed、execution.replayed。jobs.total=6、executions.total=2保持不变,分析仍为executed。127.0.0.1:8443->443/tcp;backend、worker、db 无宿主机端口。历史修复与评测证据
独立 Worker 与运维看板(b0900bf)
python -m app.worker消费数据库队列;条件 UPDATE 抢占、worker_id/lease_until心跳、attempts/max_attempts、指数退避、过期租约回收与 dead-letter。by_status包含全部 10 个分析状态且求和等于jobs.total。partial且amount_usd=null。真实 Agent 与 RAG 评测(历史,本轮未重跑)
未验证 / 阻塞
docs/CLOUD_DEPLOYMENT.md中的云端步骤需要在目标主机复验。安全边界
.env、API Key、数据库、缓存或构建产物;云端密钥、htpasswd 和 TLS 材料均由.gitignore排除。/api/demo与登录入口位于 Nginx Basic Auth 之后;demo 身份仍不是正式认证。🤖 Generated with Claude Code