feat: add LangGraph adapter with atomic fan-out reserve/settle and typed budget advisory (#33)#34
Conversation
…ped budget advisory (Floe-Labs#33)
|
Warning Review limit reached
Next review available in: 3 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughAdds an optional LangGraph adapter with guarded sync/async nodes, atomic budget reservation and settlement, typed advisory state, near-limit routing, a runnable example, package and documentation updates, and concurrency-focused acceptance tests. ChangesLangGraph budget-aware integration
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant StateGraph
participant guarded_node
participant BudgetGuard
participant WorkerNode
participant Router
StateGraph->>guarded_node: invoke worker branch
guarded_node->>BudgetGuard: reserve estimated cost
guarded_node->>WorkerNode: execute model call
WorkerNode-->>guarded_node: return usage update
guarded_node->>BudgetGuard: settle actual usage
guarded_node-->>StateGraph: write BudgetAdvisory
StateGraph->>Router: evaluate graph state
Router-->>StateGraph: select full step, cheap step, or END
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@README.md`:
- Around line 242-245: Update the README example’s guarded_node usage to pass a
conservative explicit estimated_cost for each initial fan-out branch, rather
than relying on the cold guard’s zero recorded cost. Revise the nearby guidance
to explain that this estimate is used for the first parallel reservation, while
retaining the existing guard limit and settle/release behavior.
In `@src/floe_guard/integrations/langgraph.py`:
- Around line 94-110: The usage extraction in _usage_from_update must handle
malformed prompt_tokens or completion_tokens without raising. Convert both
values defensively and return None when either cannot be parsed, allowing
_settle_update() to continue to the guard.release(reserved) cleanup path;
preserve valid numeric values and the existing missing-usage behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7637c83a-2dcf-4c8b-880c-44150019974f
📒 Files selected for processing (9)
.github/workflows/ci.ymlCHANGELOG.mdCONTRIBUTING.mdREADME.mdexamples/langgraph_budget_aware.pypyproject.tomlsrc/floe_guard/integrations/__init__.pysrc/floe_guard/integrations/langgraph.pytests/test_langgraph_adapter.py
… release hold on malformed usage)
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@README.md`:
- Around line 257-259: Update the README guidance around estimated_cost to
describe 0.01 as a conservative fixed hold reused on every invocation, while
noting that only omitted estimates use the guard’s last settled cost; in
src/floe_guard/integrations/langgraph.py lines 32-34, remove the claim that the
decorated node later re-estimates automatically.
In `@src/floe_guard/integrations/langgraph.py`:
- Around line 116-124: Update the exception handling around _usage_from_update()
to also catch OverflowError, alongside TypeError and ValueError. Ensure
guard.release(reserved) runs before re-raising so reservations are released for
all token-conversion failures.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3e0e68a0-794e-4b69-9e78-0fdece671bc2
📒 Files selected for processing (3)
README.mdsrc/floe_guard/integrations/langgraph.pytests/test_langgraph_adapter.py
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
OverflowError case addressed in 491e0b2 alongside TypeError and ValueError, with an inf-usage test locking the release path. |
|
up for review this is great! |
|
per #33 |
There was a problem hiding this comment.
Pull request overview
Adds first-class LangGraph integration to floe-guard, enabling concurrency-safe budgeting for StateGraph fan-out via the existing atomic reserve()/settle()/release() primitive, plus a typed budget advisory channel in LangGraph state to support “near limit” routing decisions.
Changes:
- Introduces
floe_guard.integrations.langgraphwithguarded_node(sync/async) and anAdvisoryChannelreducer (latest_advisory) to keep the freshestBudgetAdvisoryunder parallel writes. - Adds comprehensive acceptance tests covering fan-out ceiling enforcement, advisory-driven routing, async nodes, and hold-release behavior on error/malformed/no-usage paths.
- Wires packaging/docs/CI for the new optional extra
floe-guard[langgraph], including a no-network example.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
src/floe_guard/integrations/langgraph.py |
Implements LangGraph node wrapper with atomic reserve/settle/release and advisory injection + reducer. |
tests/test_langgraph_adapter.py |
Adds acceptance tests validating ceiling safety under fan-out concurrency and advisory-driven routing behavior. |
examples/langgraph_budget_aware.py |
Provides an end-to-end, no-network LangGraph example demonstrating near-limit tapering. |
pyproject.toml |
Adds langgraph optional dependency extra and keyword metadata. |
.github/workflows/ci.yml |
Installs the langgraph extra so adapter tests run in CI. |
README.md |
Documents LangGraph integration usage and points to the example. |
CHANGELOG.md |
Records the new LangGraph adapter and example under Unreleased. |
CONTRIBUTING.md |
Lists the new .[langgraph] extra for running adapter tests locally. |
src/floe_guard/integrations/__init__.py |
Updates integrations docstring to include floe-guard[langgraph]. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
# Conflicts: # .github/workflows/ci.yml # CHANGELOG.md
The LangGraph adapter adds new package source, so version-guard requires a bump over main's 0.5.0. Minor bump per SemVer (additive feature): pyproject version -> 0.6.0, __version__ in lockstep, and promote the CHANGELOG entry from Unreleased to a dated py 0.6.0 section.
Summary
LangGraph adapter for floe-guard (issue #33): every branch of a StateGraph fan-out gets its own atomic slice of the ceiling (reserve-before / settle-after, the same contract the OpenAI and Anthropic adapters use), and a typed BudgetAdvisory channel in graph state lets a router downshift models before the hard-stop.
Changes
src/floe_guard/integrations/langgraph.py:guarded_node(sync + async, decorator or plain wrapper):reserve()before the node runs,settle(reserved=...)from the usage it reports,release()on error or when the node metered its call elsewhere (no double-counting).latest_advisoryreducer +AdvisoryChannel, sostate["budget"]always carries the freshest utilization under parallel writes.tests/test_langgraph_adapter.py: 8 tests mapping the acceptance criteria. A real 16-branch over-budget StateGraph fan-out never crosseslimit_usd(the graph port of the 16-concurrent-agents test), a within-budget fan-out settles exactly with zero leaked holds, the router downshifts onnear_limitwith zeroBudgetExceeded, error and no-usage paths release the hold, async nodes are guarded, reducer ordering is write-order independent, and the example runs end to end on budget.examples/langgraph_budget_aware.py: LangGraph port ofexamples/budget_aware.py(no API key, no network). Full model until the 70% advisory trips, tapers to the cheap model, finishes at $0.0998 under a $0.10 ceiling.pyproject.toml:langgraph = ["langgraph>=1.0"]extra + keyword..github/workflows/ci.yml: install the extra in the test matrix so the adapter tests run instead of skipping (mirrors the langchain/litellm rationale). README section, CHANGELOG (Unreleased), CONTRIBUTING extras list, integrations docstring.Testing
pytestpasses: 123 passed, 1 skipped (baseline 115 passed, 1 skipped)ruff check .andruff format .are cleanAcceptance criteria from #33:
limit_usd(test_fan_out_over_budget_never_crosses_ceiling, 16 branches on the superstep thread pool)BudgetAdvisory; a router downshifts onnear_limitbefore anyBudgetExceeded(test_router_downshifts_on_near_limit_before_hard_stop)floe-guard[langgraph], mirroring the crewai/langchain extrasRelated issues
Closes #33
Summary by CodeRabbit