-
-
Notifications
You must be signed in to change notification settings - Fork 0
Add a budget-aware escalation guard #331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,26 @@ | ||
| # Morning Report | ||
|
|
||
| ## Mission | ||
| Clarify where AgentGuard sits in the emerging agent security stack so the SDK is easier to position against identity, MCP-governance, and sandbox products without over-claiming. | ||
| Ship a portable advisor-style escalation guard in the public SDK without breaking the zero-dependency, local-first boundary. | ||
|
|
||
| ## What shipped | ||
| - added `docs/competitive/agent-security-stack.md`, a living positioning doc that places AgentGuard in the runtime behavior and budget layer | ||
| - updated `README.md` so the competitive-doc area links to both the Vercel gateway comparison and the broader security-stack framing | ||
| - regenerated `sdk/PYPI_README.md` | ||
| - added an unreleased changelog entry and proof bundle under `proof/agent-security-stack-positioning/` | ||
| - added `BudgetAwareEscalation`, `EscalationSignal`, and `EscalationRequired` | ||
| - supported four v1 escalation triggers: token count, confidence, tool-call depth, and a custom rule | ||
| - split the feature into a new stdlib-only `sdk/agentguard/escalation.py` core module so `guards.py` stayed under the repo's line-limit rule | ||
| - added a local Llama-to-Claude worked example plus a guide page | ||
| - updated README, examples docs, roadmap, changelog, and generated PyPI README | ||
|
|
||
| ## Why it matters | ||
| - this gives the SDK a cleaner answer to "how do you relate to identity, MCP governance, or sandboxing vendors?" | ||
| - it keeps the repo inside its actual architecture boundary: in-process runtime enforcement, not credential brokering or control-plane governance | ||
| - the README now points to that positioning directly, which is better for distribution than leaving the layer story implicit | ||
| - the SDK had hard stop guards, but no portable way to keep a cheap model on by default and escalate only the hard turns | ||
| - this gives users an advisor-style pattern without locking the repo to Anthropic's server-side tool or any hosted control plane | ||
| - the feature stays local-first: the guard decides, the app routes | ||
|
|
||
| ## Validation | ||
| - `python scripts/sdk_preflight.py` passed | ||
| - `python -m pytest sdk/tests/test_pypi_readme_sync.py -v` passed | ||
| - `python scripts/sdk_release_guard.py` passed | ||
| - full SDK suite passed: `687 passed` | ||
| - coverage passed at `92.75%` | ||
| - lint, structural checks, release guard, preflight, and bandit passed | ||
| - local example run produced both console proof and a trace artifact under `proof/budget-aware-escalation/` | ||
|
|
||
| ## Notes | ||
| - I intentionally kept this docs-only because the queue item is a positioning correction, not a runtime feature request | ||
| - I used the repo's actual architecture as the constraint: MCP is a read path, the SDK is the runtime layer, and the private dashboard remains out of scope | ||
| - I did not repeat the queue note's exact "more than doubled benchmark score" line in repo docs because I could not verify that exact wording from the primary Anthropic doc available in this environment | ||
| - the feature is intentionally explicit rather than "magic routing" inside provider patchers |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,35 +1,39 @@ | ||
| # PR Draft | ||
|
|
||
| ## Title | ||
| Position AgentGuard inside the emerging agent security stack | ||
| Add a budget-aware escalation guard for advisor-style model routing | ||
|
|
||
| ## Summary | ||
| - add a new competitive doc that places AgentGuard in the runtime behavior and budget layer, distinct from identity, MCP governance, and sandboxing | ||
| - update the README's competitive-doc links so the public repo points to both the gateway comparison and the broader stack framing | ||
| - regenerate the PyPI README so package docs stay aligned with the repo README | ||
| - add `BudgetAwareEscalation`, `EscalationSignal`, and `EscalationRequired` so apps can keep a cheaper default model and escalate only hard turns to a stronger model | ||
| - support four v1 signals: token count, confidence, tool-call depth, and a custom rule | ||
| - keep the SDK boundary intact: AgentGuard decides when to escalate; the app still owns the actual provider call | ||
|
|
||
| ## Scope | ||
| - `docs/competitive/agent-security-stack.md` | ||
| - `README.md` | ||
| - `CHANGELOG.md` | ||
| - `sdk/PYPI_README.md` | ||
| - `PR_DRAFT.md` | ||
| - `MORNING_REPORT.md` | ||
| - proof artifacts under `proof/agent-security-stack-positioning/` | ||
| - core guard implementation in `sdk/agentguard/escalation.py` | ||
| - public exports and guard-module compatibility re-exports | ||
| - tests for signal matching, next-call arming, exports, DX, smoke, and example execution | ||
| - one guide and one local-only example | ||
| - README / examples / changelog / roadmap / generated PyPI README sync | ||
| - proof artifacts under `proof/budget-aware-escalation/` | ||
|
|
||
| ## Non-goals | ||
| - no dashboard work | ||
| - no SDK runtime or MCP code changes | ||
| - no attempt to turn AgentGuard into the identity, governance, or sandbox layer | ||
| - no speculative vendor feature claims beyond clearly labeled layer framing | ||
| - no provider-specific routing adapter | ||
| - no hidden network behavior | ||
| - no new runtime dependencies | ||
| - no attempt to auto-switch OpenAI or Anthropic patchers under the hood | ||
|
|
||
| ## Proof | ||
| - `python scripts/sdk_preflight.py` | ||
| - `python -m pytest sdk/tests/test_pypi_readme_sync.py -v` | ||
| - `python -m ruff check sdk/agentguard/guards.py sdk/agentguard/escalation.py sdk/agentguard/__init__.py sdk/tests/test_guards.py sdk/tests/test_exports.py sdk/tests/test_dx.py sdk/tests/test_smoke.py sdk/tests/test_example_starters.py sdk/tests/test_architecture.py examples/budget_aware_escalation.py scripts/generate_pypi_readme.py` | ||
| - `python -m pytest sdk/tests -v --cov=agentguard --cov-report=term-missing --cov-fail-under=80` | ||
| - `python scripts/sdk_release_guard.py` | ||
| - `python scripts/sdk_preflight.py` | ||
| - `python -m bandit -r sdk/agentguard -s B101,B110,B112,B311 -q` | ||
| - `python scripts/generate_pypi_readme.py --write` | ||
| - `PYTHONPATH=sdk python examples/budget_aware_escalation.py` | ||
|
|
||
| ## Saved artifacts | ||
| - `proof/agent-security-stack-positioning/preflight.txt` | ||
| - `proof/agent-security-stack-positioning/pypi-sync.txt` | ||
| - `proof/agent-security-stack-positioning/release-guard.txt` | ||
| - `proof/agent-security-stack-positioning/git-diff.txt` | ||
| - `proof/budget-aware-escalation/example-output.txt` | ||
| - `proof/budget-aware-escalation/budget_aware_escalation_traces.jsonl` | ||
| - `proof/budget-aware-escalation/source-notes.md` | ||
| - `proof/budget-aware-escalation/blog-draft.md` |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| # Budget-Aware Escalation | ||
|
|
||
| `BudgetAwareEscalation` gives AgentGuard users a portable version of the "cheap executor, strong advisor" pattern without baking provider-specific routing into the SDK. | ||
|
|
||
| The shape is simple: | ||
|
|
||
| - keep a cheaper default model for routine work | ||
| - escalate only when the current turn looks hard | ||
| - wire the returned model choice into your own client call | ||
|
|
||
| This stays consistent with AgentGuard's core boundary: the SDK decides when a run is risky or expensive; your app still owns the actual model invocation. | ||
|
|
||
| ## Why this exists | ||
|
|
||
| Anthropic's official advisor-tool docs describe the same high-level pattern: pair a faster, lower-cost executor with a stronger advisor for hard long-horizon tasks, especially coding and agentic workflows. The queue item that triggered this work called out the durable pattern, not a vendor-specific API, and that is the right way to bring it into AgentGuard. | ||
|
|
||
| Source: | ||
| - [Anthropic advisor tool docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool) | ||
|
|
||
| ## What the guard does | ||
|
|
||
| `BudgetAwareEscalation` supports four trigger types in v1: | ||
|
|
||
| 1. token count over a threshold | ||
| 2. normalized confidence below a threshold | ||
| 3. tool-call depth over a threshold | ||
| 4. a custom rule that inspects the turn context | ||
|
|
||
| When a configured signal trips, the guard can: | ||
|
|
||
| - raise `EscalationRequired` so you switch to the stronger model explicitly, or | ||
| - arm the next call through the normal `auto_check(...)` path and let `select_model()` choose the stronger model on the next turn | ||
|
|
||
| ## Minimal example | ||
|
|
||
| ```python | ||
| from agentguard import BudgetAwareEscalation, EscalationRequired, EscalationSignal | ||
|
|
||
| guard = BudgetAwareEscalation( | ||
| primary_model="ollama/llama3.1:8b", | ||
| escalate_model="claude-opus-4-6", | ||
| escalate_on=( | ||
| EscalationSignal.TOKEN_COUNT(threshold=2000), | ||
| EscalationSignal.CONFIDENCE_BELOW(threshold=0.45), | ||
| ), | ||
| ) | ||
|
|
||
| model = guard.select_model() | ||
|
|
||
| # ... run the cheaper model first ... | ||
| guard.auto_check( | ||
| "llm.result", | ||
| { | ||
| "model": model, | ||
| "usage": {"total_tokens": 2430}, | ||
| "confidence": 0.39, | ||
| }, | ||
| ) | ||
|
|
||
| try: | ||
| guard.check() | ||
| next_model = guard.primary_model | ||
| except EscalationRequired as exc: | ||
| next_model = exc.target_model | ||
| print(exc.reason) | ||
| ``` | ||
|
|
||
| ## Worked example: local Llama to Claude | ||
|
|
||
| This repo includes a local-only example that simulates a local Llama turn, arms the escalation guard from the result, and then routes the next call to Claude: | ||
|
|
||
| ```bash | ||
| PYTHONPATH=sdk python examples/budget_aware_escalation.py | ||
| ``` | ||
|
|
||
| Expected output: | ||
|
|
||
| ```text | ||
| Turn 1 model: ollama/llama3.1:8b | ||
| Turn 2 model: claude-opus-4-6 | ||
| Escalation reason: token_count 2430 exceeded 2000 | ||
| Wrote budget_aware_escalation_traces.jsonl | ||
| ``` | ||
|
|
||
| The example also writes a local trace file so the escalation path is inspectable. | ||
|
|
||
| ## Signal semantics | ||
|
|
||
| ### Token count | ||
|
|
||
| Use when a turn is getting too expensive or too context-heavy for the cheaper model: | ||
|
|
||
| ```python | ||
| EscalationSignal.TOKEN_COUNT(threshold=2000) | ||
| ``` | ||
|
|
||
| The guard looks for: | ||
|
|
||
| - `token_count` | ||
| - `total_tokens` | ||
| - `usage.total_tokens` | ||
|
|
||
| ### Confidence below | ||
|
|
||
| Use when your runtime already exposes a normalized confidence score: | ||
|
|
||
| ```python | ||
| EscalationSignal.CONFIDENCE_BELOW(threshold=0.45) | ||
| ``` | ||
|
|
||
| The SDK does not invent confidence for you. If your provider exposes logprobs or another score, normalize it in your app and pass `confidence=...` or put `confidence` in the traced event payload. | ||
|
|
||
| ### Tool-call depth | ||
|
|
||
| Use when a turn is spiraling into too many tool hops: | ||
|
|
||
| ```python | ||
| EscalationSignal.TOOL_CALL_DEPTH(threshold=3) | ||
| ``` | ||
|
|
||
| The guard looks for: | ||
|
|
||
| - `tool_call_depth` | ||
| - `depth` | ||
| - `tool_calls` list length | ||
|
|
||
| ### Custom rule | ||
|
|
||
| Use when your own heuristics are stronger than any single built-in signal: | ||
|
|
||
| ```python | ||
| EscalationSignal.CUSTOM( | ||
| lambda ctx: (ctx.get("token_count") or 0) > 1500 and (ctx.get("confidence") or 1.0) < 0.5, | ||
| name="hard_turn", | ||
| ) | ||
| ``` | ||
|
|
||
| The custom rule receives a context dict with: | ||
|
|
||
| - `event_name` | ||
| - `event_data` | ||
| - `primary_model` | ||
| - `escalate_model` | ||
| - `current_model` | ||
| - `token_count` | ||
| - `confidence` | ||
| - `tool_call_depth` | ||
|
|
||
| ## Design boundary | ||
|
|
||
| This is intentionally not "transparent provider switching" inside AgentGuard's patchers. | ||
|
|
||
| Why: | ||
|
|
||
| - AgentGuard guards are runtime enforcement primitives, not provider adapters | ||
| - the SDK must stay zero-dependency | ||
| - routing policy is portable only if your app remains in control of the actual client call | ||
|
|
||
| So the contract is: | ||
|
|
||
| - AgentGuard tells you when escalation is needed | ||
| - your application decides how to invoke the stronger model | ||
|
|
||
| That keeps the feature compatible with local Ollama, raw HTTP clients, OpenAI-compatible endpoints, Anthropic, and future provider adapters without locking the SDK to one stack. |
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.