{"id":43840192,"url":"https://github.com/wisemanIV/strands-costguard","last_synced_at":"2026-02-17T18:00:46.860Z","repository":{"id":327958295,"uuid":"1113059015","full_name":"wisemanIV/strands-costguard","owner":"wisemanIV","description":"This package provides a Strands-native “cost guard” layer for AI agents, focused on controlling and attributing LLM and tool spend within Strands-based workflows. It enforces budgets and policies at run time while emitting persistent, OpenTelemetry-style metrics so teams can monitor and optimize cost across tenants, strands, and workflows.","archived":false,"fork":false,"pushed_at":"2025-12-10T17:46:17.000Z","size":252,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-10T17:58:54.957Z","etag":null,"topics":["aws-cost-management","aws-cost-saving","strands","strands-agents"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wisemanIV.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-09T13:16:53.000Z","updated_at":"2025-12-10T17:46:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/wisemanIV/strands-costguard","commit_stats":null,"previous_names":["wisemaniv/strands-costguard"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/wisemanIV/strands-costguard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wisemanIV%2Fstrands-costguard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wisemanIV%2Fstrands-costguard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wisemanIV%2Fstrands-costguard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wisemanIV%2Fstrands-costguard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wisemanIV","download_url":"https://codeload.github.com/wisemanIV/strands-costguard/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wisemanIV%2Fstrands-costguard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29552225,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T17:56:56.811Z","status":"ssl_error","status_checked_at":"2026-02-17T17:56:55.544Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-cost-management","aws-cost-saving","strands","strands-agents"],"created_at":"2026-02-06T05:00:23.728Z","updated_at":"2026-02-17T18:00:46.852Z","avatar_url":"https://github.com/wisemanIV.png","language":"Python","funding_links":[],"categories":["Community Projects"],"sub_categories":["For PyPI Packages"],"readme":"# Strands CostGuard\n\nA cost management library for the [Strands Agents SDK](https://github.com/strands-agents/sdk-python) with budget enforcement, adaptive model routing, and OpenTelemetry-compatible metrics.\n\n## Features\n\n- **Budget Enforcement**: Define budgets at tenant, strand, workflow, and run levels with configurable limits and actions\n- **Adaptive Model Routing**: Automatically route to fallback models based on budget utilization and other conditions\n- **Cost Tracking**: Track and attribute costs by tenant, strand, workflow, run, model, and tool\n- **OpenTelemetry Metrics**: Emit cost metrics compatible with OTel collectors for long-term storage and analysis\n- **Flexible Policies**: Configure via YAML files or environment variables\n- **Persistent Budget State**: Optional Valkey/Redis persistence for budget state across restarts\n\n## Requirements\n\n- Python 3.10+\n- [Strands Agents SDK](https://github.com/strands-agents/sdk-python) 0.1.0+\n\n## Installation\n\n```bash\npip install strands-costguard\n```\n\nFor persistence support:\n\n```bash\npip install strands-costguard[valkey]\n```\n\n## Quick Start\n\n### Running the Examples\n\n```bash\n# Install the package in development mode\npip install -e .\n\n# Run the basic usage example\npython examples/basic_usage.py\n```\n\n### Basic Usage\n\n```python\nfrom strands_costguard import (\n    CostGuard,\n    CostGuardConfig,\n    FilePolicySource,\n    ModelUsage,\n)\n\n# Initialize Cost Guard\nconfig = CostGuardConfig(\n    policy_source=FilePolicySource(path=\"./policies\"),\n    enable_budget_enforcement=True,\n    enable_routing=True,\n    enable_metrics=True,\n)\n\nguard = CostGuard(config=config)\n\n# Start a run\ndecision = guard.on_run_start(\n    tenant_id=\"prod-tenant\",\n    strand_id=\"analytics_assistant\",\n    workflow_id=\"data_analysis\",\n    run_id=\"run-123\",\n)\n\nif not decision.allowed:\n    print(f\"Run rejected: {decision.reason}\")\nelse:\n    # Execute your agent loop...\n\n    # Before model calls\n    model_decision = guard.before_model_call(\n        run_id=\"run-123\",\n        model_name=\"gpt-4o\",\n        stage=\"planning\",\n        prompt_tokens_estimate=500,\n    )\n\n    # Use the effective model (may be downgraded)\n    effective_model = model_decision.effective_model\n\n    # After model calls\n    guard.after_model_call(\n        run_id=\"run-123\",\n        usage=ModelUsage.from_response(\n            model_name=effective_model,\n            prompt_tokens=500,\n            completion_tokens=200,\n        ),\n    )\n\n    # End the run\n    guard.on_run_end(\"run-123\", \"completed\")\n\n# Shutdown (flushes metrics)\nguard.shutdown()\n```\n\n## Configuration\n\n### Budget Policies (budgets.yaml)\n\n```yaml\nbudgets:\n  - id: \"tenant-default\"\n    scope: \"tenant\"\n    match:\n      tenant_id: \"*\"\n    period: \"monthly\"\n    max_cost: 1000.0\n    soft_thresholds: [0.7, 0.9, 1.0]\n    hard_limit: true\n    on_soft_threshold_exceeded: \"DOWNGRADE_MODEL\"\n    on_hard_limit_exceeded: \"REJECT_NEW_RUNS\"\n\n  - id: \"analytics-strand\"\n    scope: \"strand\"\n    match:\n      strand_id: \"analytics_assistant\"\n    period: \"daily\"\n    max_cost: 50.0\n    max_runs_per_period: 1000\n    max_concurrent_runs: 100\n    constraints:\n      max_iterations_per_run: 8\n      max_tool_calls_per_run: 20\n      max_model_tokens_per_run: 30000\n```\n\n### Routing Policies (routing.yaml)\n\n```yaml\nrouting_policies:\n  - id: \"default-routing\"\n    match:\n      strand_id: \"*\"\n    stages:\n      - stage: \"planning\"\n        default_model: \"gpt-4o-mini\"\n        max_tokens: 2000\n      - stage: \"synthesis\"\n        default_model: \"gpt-4o\"\n        fallback_model: \"gpt-4o-mini\"\n        trigger_downgrade_on:\n          soft_threshold_exceeded: true\n          remaining_budget_below: 5.0\n```\n\n### Pricing Table (pricing.yaml)\n\n```yaml\npricing:\n  currency: \"USD\"\n  models:\n    \"gpt-4o\":\n      input_per_1k: 2.50\n      output_per_1k: 10.00\n    \"gpt-4o-mini\":\n      input_per_1k: 0.15\n      output_per_1k: 0.60\n  tools:\n    \"web_search\":\n      cost_per_call: 0.01\n```\n\n## Lifecycle Hooks\n\nCost Guard integrates with your agent runtime via lifecycle hooks:\n\n| Hook | When Called | Returns |\n|------|-------------|---------|\n| `on_run_start()` | Before starting a new run | `AdmissionDecision` |\n| `on_run_end()` | After a run completes | None |\n| `before_iteration()` | Before each agent loop iteration | `IterationDecision` |\n| `after_iteration()` | After each iteration completes | None |\n| `before_model_call()` | Before each model call | `ModelDecision` |\n| `after_model_call()` | After each model call | None |\n| `before_tool_call()` | Before each tool call | `ToolDecision` |\n| `after_tool_call()` | After each tool call | None |\n\n## OpenTelemetry Metrics\n\n### Enabling OTLP Export\n\nTo export metrics to an OpenTelemetry collector, configure StrandsTelemetry before initializing CostGuard:\n\n```python\nfrom strands.telemetry.config import StrandsTelemetry\nfrom strands_costguard import CostGuard, CostGuardConfig, FilePolicySource\n\n# Configure telemetry with OTLP export\ntelemetry = StrandsTelemetry()\ntelemetry.setup_otlp_exporter(endpoint=\"http://localhost:4317\")\ntelemetry.setup_meter(enable_otlp_exporter=True)\n\n# Initialize CostGuard (will use the global MeterProvider)\nconfig = CostGuardConfig(\n    policy_source=FilePolicySource(path=\"./policies\"),\n    enable_metrics=True,\n)\nguard = CostGuard(config=config)\n```\n\n**Requirements:**\n- An OpenTelemetry collector running at the specified endpoint (default: `localhost:4317`)\n- For local development, you can run a collector with Docker:\n  ```bash\n  docker run -p 4317:4317 otel/opentelemetry-collector:latest\n  ```\n\n**Disabling OTLP Export:**\n\nIf you don't have a collector running, disable OTLP export to avoid connection errors:\n\n```python\ntelemetry.setup_meter(enable_otlp_exporter=False)\n```\n\n### Metrics Reference\n\nCost Guard emits the following metrics:\n\n| Metric | Type | Description |\n|--------|------|-------------|\n| `genai.cost.total` | Counter | Total cost in currency units |\n| `genai.cost.model` | Counter | Cost per model |\n| `genai.cost.tool` | Counter | Cost per tool |\n| `genai.tokens.input` | Counter | Total input tokens |\n| `genai.tokens.output` | Counter | Total output tokens |\n| `genai.agent.iterations` | Counter | Agent loop iterations |\n| `genai.agent.tool_calls` | Counter | Tool calls |\n| `genai.cost.downgrade_events` | Counter | Model downgrade events |\n| `genai.cost.rejection_events` | Counter | Run rejection events |\n\nMetrics include resource attributes:\n- `service.name`, `service.namespace`, `deployment.environment`\n- `strands.tenant_id`, `strands.strand_id`, `strands.workflow_id`\n\n## Budget Scopes and Priority\n\nBudgets can be defined at multiple scopes, with higher priority scopes taking precedence:\n\n1. **Global** (lowest priority) - Default limits for all\n2. **Tenant** - Organization-level limits\n3. **Strand** - Agent definition limits\n4. **Workflow** (highest priority) - Specific workflow limits\n\nWhen multiple budgets match, constraints are merged with more specific budgets taking priority.\n\n## Threshold Actions\n\nWhen budget soft thresholds are exceeded:\n\n| Action | Effect |\n|--------|--------|\n| `LOG_ONLY` | Log warning, continue normally |\n| `DOWNGRADE_MODEL` | Switch to fallback models |\n| `LIMIT_CAPABILITIES` | Reduce max tokens/iterations |\n| `HALT_NEW_RUNS` | Reject new runs |\n\nWhen hard limits are exceeded:\n\n| Action | Effect |\n|--------|--------|\n| `HALT_RUN` | Stop the current run |\n| `REJECT_NEW_RUNS` | Reject new runs only |\n\n## Development\n\n```bash\n# Install dev dependencies\npip install -e \".[dev]\"\n\n# Run tests\npytest\n\n# Type checking\nmypy src/\n\n# Linting\nruff check src/\n```\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FwisemanIV%2Fstrands-costguard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FwisemanIV%2Fstrands-costguard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FwisemanIV%2Fstrands-costguard/lists"}