feat: SHEK-1 per-tenant budget enforcement (SHEK-3 + SHEK-4 + SHEK-5 + SHEK-6 + SHEK-7)#28
Merged
Merged
Conversation
- budget() factory routes to TemporalBudget when tenant_id or backend is
set; defaults to window_seconds=86400*30 (30 days) when not provided
- TemporalBudget gains tenant_id param with validation (non-empty, requires
Redis backend), tenant_id property, and budget_name scoped to
shekel:tb:{name}:{tenant_id} in all backend calls
- 20 tests covering routing, validation, key isolation, BudgetConfigMismatch,
async parity; uses fakeredis + lupa for Lua script execution
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…Backend
Five new methods on both sync and async backends:
- get_tenant_spend(name, tenant_id) → float (reads usd:spent; 0.0 if absent)
- get_tenant_limit(name, tenant_id) → float | None (reads usd:max)
- set_tenant_limit(name, tenant_id, max_usd) (updates usd:max + spec_hash,
reading usd:window_s from Redis to recompute hash correctly)
- reset_tenant(name, tenant_id) (zeroes usd:spent, preserves limit + hash)
- list_tenants(name) → list[str] (SCAN shekel:tb:{name}:*, prefix-strip
for correct handling of tenant IDs containing ':')
18 new tests covering all five methods on both backends via fakeredis+lupa.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… CLI - Budget.summary_data() gains tenant_id: None field (baseline) - TemporalBudget.summary_data() overrides to inject _tenant_id value - TemporalBudget.summary() overrides to insert Tenant: <id> line after header - shekel run --output json now includes tenant_id field - New CLI command: shekel tenants --name <n> [--redis-url URL] [--output table|json] lists all tenants with SPENT, LIMIT, USED% columns; exits 1 when no Redis URL - 9 new tests: summary_data(), summary(), and all shekel tenants variants Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… per-tenant budgets Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… fix CI lupa dependency - docs/usage/per-tenant-budgets.md: new full MkDocs page (quick start, FastAPI SaaS example, async usage, Redis key scheme, quota management methods, shekel tenants CLI reference, limit-change flow, error reference) - docs/usage/distributed-budgets.md: add Per-Tenant Namespacing section - docs/api-reference.md: add tenant_id/backend/window_seconds params to budget(); tenant_id property on Budget; per-tenant methods table on RedisBackend; async equivalents on AsyncRedisBackend - README.md: new "Per-user / per-tenant enforcement" section - mkdocs.yml: wire Per-Tenant Budgets into the Usage Guide nav - pyproject.toml: fakeredis -> fakeredis[lua] so CI gets lupa and SCRIPT LOAD / EVALSHA work in tenant-budget tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… 336-338) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements SHEK-1: per-tenant / per-user LLM budget enforcement using the existing Redis backend with zero per-tenant configuration overhead.
This PR will accumulate commits for the full epic (SHEK-3 → SHEK-7). Currently contains SHEK-3.
SHEK-3 (this commit) —
tenant_idparam onbudget()budget()factory routes toTemporalBudgetwhentenant_idorbackendis set; default window 30 daysTemporalBudgetgainstenant_idparam + validation (non-empty, requires Redis backend)shekel:tb:{name}:{tenant_id}— single-line change in_record_spendBudgetConfigMismatchErrorfires when same(name, tenant_id)used with a differentmax_usdTest plan
mypy,black,isort,ruffall cleanCloses SHEK-3 (partial: SHEK-1)
🤖 Generated with Claude Code