876217d0ca
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 16s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 24s
CI / typecheck (pull_request) Successful in 37s
CI / security (pull_request) Successful in 44s
CI / unit_tests (pull_request) Successful in 2m40s
CI / integration_tests (pull_request) Successful in 3m19s
CI / docker (pull_request) Successful in 42s
CI / coverage (pull_request) Successful in 5m10s
CI / lint (push) Successful in 12s
CI / build (push) Successful in 16s
CI / quality (push) Successful in 21s
CI / security (push) Successful in 32s
CI / typecheck (push) Successful in 37s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Failing after 2m36s
CI / docker (push) Has been skipped
CI / integration_tests (push) Successful in 3m16s
CI / coverage (push) Successful in 5m3s
CI / benchmark-publish (push) Successful in 17m54s
CI / benchmark-regression (pull_request) Successful in 31m55s
Implements Forgejo issue #584: three-tier budget hierarchy (per-plan -> per-session -> per-org) with tightest limit winning. Domain models: - BudgetLevel enum (PLAN, SESSION, ORG) - BudgetCheckResult (frozen Pydantic model with exceeded_level, warning) - SessionCostBudget (tracks per-session accumulated cost) - OrgCostAccumulator (tracks per-org accumulated cost) - ThreadSafeOrgCostAccumulator (thread-safe wrapper with snapshot) Application services: - CostBudgetService: manages budget state, enforces hierarchy, emits BUDGET_WARNING (once per session) and BUDGET_EXCEEDED events - AutonomyGuardrailService: extended with associate_plan_with_session, check_budget_hierarchy, record_plan_cost_to_session methods Configuration: - Settings: session_max_cost_usd, org_max_cost_usd, budget_warning_threshold Integration: - Session model: cost_budget field, as_cli_dict includes budget data - DI container: CostBudgetService registered as Singleton - CLI session show: cost budget panel display Tests: - 54 Behave scenarios (features/cost_budgets.feature) - 11 Robot Framework integration tests (robot/cost_budgets.robot) - ASV benchmarks (benchmarks/bench_budget_check.py) All nox stages pass: lint, typecheck, unit_tests, coverage_report (98%). CLOSES #584
153 lines
5.4 KiB
Python
153 lines
5.4 KiB
Python
"""ASV benchmarks for per-session and per-org cost budget checks.
|
|
|
|
Measures the performance of:
|
|
- SessionCostBudget creation and cost recording
|
|
- OrgCostAccumulator creation and cost recording
|
|
- CostBudgetService.check_budget_hierarchy with varying configurations
|
|
- AutonomyGuardrailService.check_budget_hierarchy integration
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
import cleveragents # noqa: E402
|
|
|
|
importlib.reload(cleveragents)
|
|
|
|
from cleveragents.application.services.autonomy_guardrail_service import ( # noqa: E402
|
|
AutonomyGuardrailService,
|
|
)
|
|
from cleveragents.application.services.cost_budget_service import ( # noqa: E402
|
|
CostBudgetService,
|
|
)
|
|
from cleveragents.domain.models.core.autonomy_guardrails import ( # noqa: E402
|
|
AutonomyGuardrails,
|
|
)
|
|
from cleveragents.domain.models.core.cost_budget import ( # noqa: E402
|
|
OrgCostAccumulator,
|
|
SessionCostBudget,
|
|
ThreadSafeOrgCostAccumulator,
|
|
)
|
|
|
|
|
|
class SessionCostBudgetSuite:
|
|
"""Benchmark SessionCostBudget operations."""
|
|
|
|
def time_creation_defaults(self) -> None:
|
|
"""Benchmark creating a SessionCostBudget with defaults."""
|
|
SessionCostBudget()
|
|
|
|
def time_creation_with_cap(self) -> None:
|
|
"""Benchmark creating a SessionCostBudget with a cap."""
|
|
SessionCostBudget(max_cost_usd=100.0)
|
|
|
|
def time_record_cost(self) -> None:
|
|
"""Benchmark recording cost to a session budget."""
|
|
b = SessionCostBudget(max_cost_usd=1000.0)
|
|
for _ in range(100):
|
|
b.record_cost(1.0)
|
|
|
|
def time_utilization(self) -> None:
|
|
"""Benchmark utilization calculation."""
|
|
b = SessionCostBudget(max_cost_usd=100.0, total_cost=50.0)
|
|
for _ in range(1000):
|
|
b.utilization()
|
|
|
|
def time_would_exceed(self) -> None:
|
|
"""Benchmark would_exceed check."""
|
|
b = SessionCostBudget(max_cost_usd=100.0, total_cost=50.0)
|
|
for _ in range(1000):
|
|
b.would_exceed(10.0)
|
|
|
|
|
|
class OrgCostAccumulatorSuite:
|
|
"""Benchmark OrgCostAccumulator operations."""
|
|
|
|
def time_creation(self) -> None:
|
|
"""Benchmark creating an OrgCostAccumulator."""
|
|
OrgCostAccumulator(org_id="bench-org", max_cost_usd=1000.0)
|
|
|
|
def time_record_cost(self) -> None:
|
|
"""Benchmark recording cost to an org accumulator."""
|
|
acc = OrgCostAccumulator(org_id="bench-org", max_cost_usd=10000.0)
|
|
for _ in range(100):
|
|
acc.record_cost(1.0)
|
|
|
|
def time_thread_safe_record(self) -> None:
|
|
"""Benchmark thread-safe cost recording."""
|
|
inner = OrgCostAccumulator(org_id="bench-org", max_cost_usd=10000.0)
|
|
ts = ThreadSafeOrgCostAccumulator(inner)
|
|
for _ in range(100):
|
|
ts.record_cost(1.0)
|
|
|
|
|
|
class CostBudgetServiceSuite:
|
|
"""Benchmark CostBudgetService hierarchy checks."""
|
|
|
|
def setup(self) -> None:
|
|
"""Configure service with session and org budgets."""
|
|
self._svc = CostBudgetService(warning_threshold=0.8)
|
|
self._svc.configure_session_budget(
|
|
"bench-sess", max_cost_usd=1000.0, org_id="bench-org"
|
|
)
|
|
self._svc.configure_org_budget("bench-org", max_cost_usd=5000.0)
|
|
|
|
def time_hierarchy_check_allowed(self) -> None:
|
|
"""Benchmark hierarchy check that is allowed."""
|
|
self._svc.check_budget_hierarchy(
|
|
session_id="bench-sess",
|
|
plan_cost=1.0,
|
|
)
|
|
|
|
def time_hierarchy_check_with_plan_budget(self) -> None:
|
|
"""Benchmark hierarchy check with plan budget."""
|
|
self._svc.check_budget_hierarchy(
|
|
session_id="bench-sess",
|
|
plan_cost=1.0,
|
|
plan_budget=100.0,
|
|
plan_spent=10.0,
|
|
)
|
|
|
|
def time_record_plan_cost(self) -> None:
|
|
"""Benchmark recording plan cost through the service."""
|
|
svc = CostBudgetService()
|
|
svc.configure_session_budget("rs", max_cost_usd=100000.0)
|
|
for _ in range(100):
|
|
svc.record_plan_cost("rs", 1.0)
|
|
|
|
|
|
class GuardrailBudgetIntegrationSuite:
|
|
"""Benchmark AutonomyGuardrailService budget hierarchy integration."""
|
|
|
|
def setup(self) -> None:
|
|
"""Configure guardrail service with budget hierarchy."""
|
|
self._cbs = CostBudgetService(warning_threshold=0.8)
|
|
self._cbs.configure_session_budget(
|
|
"g-sess", max_cost_usd=1000.0, org_id="g-org"
|
|
)
|
|
self._cbs.configure_org_budget("g-org", max_cost_usd=5000.0)
|
|
self._gs = AutonomyGuardrailService(cost_budget_service=self._cbs)
|
|
self._gs.configure_guardrails("g-plan", AutonomyGuardrails(tool_budget=500.0))
|
|
self._gs.associate_plan_with_session("g-plan", "g-sess")
|
|
|
|
def time_check_budget_hierarchy(self) -> None:
|
|
"""Benchmark guardrail service budget hierarchy check."""
|
|
self._gs.check_budget_hierarchy("g-plan", 1.0)
|
|
|
|
def time_record_plan_cost_to_session(self) -> None:
|
|
"""Benchmark recording plan cost through guardrail service."""
|
|
cbs = CostBudgetService()
|
|
cbs.configure_session_budget("rps", max_cost_usd=100000.0)
|
|
gs = AutonomyGuardrailService(cost_budget_service=cbs)
|
|
gs.configure_guardrails("rp", AutonomyGuardrails(tool_budget=100000.0))
|
|
gs.associate_plan_with_session("rp", "rps")
|
|
for _ in range(100):
|
|
gs.record_plan_cost_to_session("rp", 1.0)
|