Skip to content

Telegram shopper: check only the platform Gemini cap with recorded spend on - #1885

Merged
mishrasanjeev merged 1 commit into
mainfrom
fix/tenantless-gemini-cap-platform-only
Oct 31, 2026
Merged

mishrasanjeev merged 1 commit into
mainfrom
fix/tenantless-gemini-cap-platform-only

Conversation

@mishrasanjeev

@mishrasanjeev mishrasanjeev commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

The Telegram shopper (core/commerce/network_telegram.py, run_shopper) belongs to no tenant. Before every buyer turn it calls assert_under_gemini_cap(0.0) without a tenant, and that check applies the per-tenant Gemini cap (AGENTICORG_GEMINI_DAILY_USD_CAP, default $10) to every tenant's spend combined.

With AGENTICORG_RECORDED_SPEND_CONTROLS_ENABLED on (#1869, and now on in production), that combined total is real. So once all tenants' Gemini calls through the router add up to $10 in a UTC day, every Telegram buyer gets "Sorry, something went wrong on my side" until the day rolls over. The bot itself is not what spends that $10. Its own model calls go through LangChain, not the router, so they are never recorded.

This PR holds the shopper to the platform cap (AGENTICORG_GEMINI_PLATFORM_DAILY_USD_CAP, default $100) only.

  • New assert_under_gemini_platform_cap() in core/llm/router.py: the platform half of the existing check, moved into its own function. It still fails closed, and a cap of 0 still turns it off.
  • assert_under_gemini_cap() is unchanged in behaviour: it now calls the new function for its platform half, with the same checks, order and messages. Router calls without a tenant keep both caps.
  • run_shopper: with the flag on, it calls the platform-only check. With the flag off, it calls assert_under_gemini_cap(0.0) exactly as main does (AGENTS.md: the change sits behind the existing default-off flag).

Verdict (bug-fix checklist)

Item Verdict Evidence
Telegram shopper stops for the day once all tenants' router Gemini spend passes $10 Fixed in code, deploy pending Postgres replay through the real router path, as a NOBYPASSRLS role with production grants. Two tenants each make one priced call ($1.20 combined, each under a $1.19 per-tenant cap). The old no-tenant check refuses. The shopper now answers, and is refused at the platform cap before its model is called. Against main's code the same test fails with DailyBudgetExceeded: spent $1.2000 of $1.19/day.

Not merged and not deployed. Production has the flag on, so this takes effect on the next deploy with no env change. Rolling back means routing traffic back to the previous revision; there is no migration and no new setting.

Sibling paths

  • Router calls without a tenant (other callers of assert_under_gemini_cap through LLMRouter._call_gemini): unchanged. They keep the per-tenant cap applied to the platform total as well as the platform cap. Loosening those is a separate decision.
  • Callers of the cap check: core/llm/router.py (_call_gemini) and core/commerce/network_telegram.py (run_shopper) are the only two. No other code calls it.

Residuals

  • The shopper's own Gemini spend is not recorded, so it counts toward neither cap. This is the same as on main. Only the bot's rate limits bound it (AGENTICORG_NETWORK_TELEGRAM_CHAT_LIMIT_PER_HOUR, AGENTICORG_NETWORK_TELEGRAM_GLOBAL_LIMIT_PER_MINUTE). Recording LangChain-path spend would be its own change.
  • Existing shopper tests stub only assert_under_gemini_cap. They pass with the default settings CI uses. With the flag forced on in the environment, they would reach the real platform check, as other suites on main already would.

Test plan

  • tests/unit/test_network_telegram_spend_cap.py (new) covers:
    • the platform check passes above the per-tenant cap while the no-tenant router check still refuses;
    • it refuses at the platform cap, including through the estimate;
    • a cap of 0 turns it off, with no spend lookup;
    • it fails closed when the spend can't be read;
    • with the flag on, the shopper answers above the per-tenant total and is refused at the platform cap before its model is called;
    • with the flag off, the shopper keeps main's check.
  • tests/integration/test_llm_daily_spend_postgres.py: the new Telegram replay against Postgres, plus the existing cap, budget and migration tests. tests/integration/test_lead_delivery_postgres.py covers the flag-off path. 12 passed.
  • The replay against main's code fails as described in the verdict table, so the test reproduces the bug.
  • Existing Telegram, payment hand-off, spend-cap and recorded-spend unit and regression tests. On the CI-pinned SQLAlchemy 2.1 venv, 141 passed and 2 failed. Both failures are identical on unmodified main: that venv lacks grantex_agent_httpsig. On SQLAlchemy 2.0 / Python 3.14, the cap and Telegram set passed (61).
  • ruff check ., mypy on the touched modules (same four missing-package import notes as main) and the enterprise stability gates.
  • Backend preflight (SKIP_UI=1 bash scripts/preflight.sh): 12 of 13 steps passed, including ruff, mypy and bandit on the whole tree, the secret scan and the enterprise stability gates. The pytest step had 16,511 passed, 13 failed and 10 errors. I reran those 8 test files on this PR's base (61824fe), and the same 13 tests fail and the same 10 error there, so this change causes none of them. They come from the local Windows environment: for example, the local venv has langgraph-checkpoint-postgres 3.0.5, where the tests expect 3.2.0. CI's unit-tests job runs the same suites and passed on this commit.

The Telegram shopper belongs to no tenant, and a check without a tenant
applies the per-tenant Gemini cap to every tenant's spend combined. With
recorded-spend controls on, that total is real: once all tenants' router
Gemini spend reached the per-tenant cap ($10 by default) in a UTC day,
every Telegram buyer turn failed until the day rolled over.

- With AGENTICORG_RECORDED_SPEND_CONTROLS_ENABLED on, run_shopper checks
  only AGENTICORG_GEMINI_PLATFORM_DAILY_USD_CAP through the new
  assert_under_gemini_platform_cap(), which fails closed like the
  combined check. Off, it calls assert_under_gemini_cap() as before.
- assert_under_gemini_cap() delegates its platform half to the new
  function with the same checks, order and messages, so router calls
  without a tenant keep both caps.
- Unit tests cover the platform check and both shopper branches. A
  Postgres replay has two tenants' recorded spend pass the per-tenant
  cap: the shopper still answers, and is refused at the platform cap.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T16:30:25.752431Z b6c408b PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@mishrasanjeev
mishrasanjeev merged commit e942856 into main Oct 31, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant