fix: streaming input guardrail, tool error-dict handling, per-instance token scoping - #4470
Conversation
Greptile SummaryThe PR revises per-team token reporting to filter aggregate and recent-interaction data by owned agent names.
Confidence Score: 2/5The PR is not yet safe to merge because token reports can still cross instance boundaries and can understate model and interaction usage after recent-record eviction. Name-based ownership cannot distinguish teams with colliding agent names, and supported unnamed agents still trigger process-global summary and detailed-report fallbacks; separately, rebuilding model and interaction totals from a bounded process-wide window leaves reports inconsistent with the unbounded token aggregates. Files Needing Attention: src/praisonai-agents/praisonaiagents/agents/agents.py
|
| Filename | Overview |
|---|---|
| src/praisonai-agents/praisonaiagents/agents/agents.py | Adds name-based token-report scoping and reconstructs report fields from filtered recent interactions. |
Reviews (4): Last reviewed commit: "fix: fully scope per-instance token repo..." | Re-trigger Greptile
| own_names = { | ||
| getattr(agent, "name", None) | ||
| for agent in (self.agents or []) | ||
| if getattr(agent, "name", None) | ||
| } |
There was a problem hiding this comment.
When two teams use the same agent name, the process-wide collector combines their metrics under one key and this filter includes the combined entry in both summaries, causing cross-team usage and cost misattribution. Instruction-based agents without explicit names also produce an empty scope and receive the complete process-wide summary.
Knowledge Base Used:
|
@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Review changes in this PR. Python SDK: praisonaiagents, praisonai. TypeScript SDK: src/praisonai-ts/. Do NOT modify src/praisonai-rust. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding. Phase 1: Review per AGENTS.md
Phase 2: FIX Valid Issues Phase 3: Final Verdict |
|
Claude finished @MervinPraison's task ββ View job Final Architecture Review β PR #4470
Phase 1 β Architecture (per AGENTS.md)
Phase 2 β Fixes implemented
Added two private helpers: Verified (two teams, overlapping process collector): Reviewed & accepted as-is (no change needed):
Known residual (documented, not blocking)
Phase 3 β Verdictβ Approve. All three P1 token-scoping findings resolved and verified; guardrail and tool-error changes are sound and add genuine SDK value (streaming security parity + self-correcting tool errors). The one residual (identical agent names across concurrent teams) is a deliberate lightweight trade-off, not a regression. Note: I can't submit a formal GitHub approval (bot restriction) β this comment is the review. |
| # process-global summary (which mixes in other instances' models and | ||
| # counts). The collector's ``by_agent`` aggregate cannot be split by | ||
| # model, so the per-interaction window is the correct source. | ||
| own_interactions = self._scoped_recent_interactions(own_names) |
There was a problem hiding this comment.
Recent window understates totals
When a team records more than 100 interactions, or another team's activity evicts its older records, _scoped_token_summary derives total_interactions and by_model from the bounded recent-interaction window while deriving token and agent totals from session-wide aggregates. The resulting public report understates interaction and model usage and is internally inconsistent.
Knowledge Base Used: Agent execution and workflows
|
@coderabbitai review |
|
/review |
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more β On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
|
|
Warning Review limit reachedNext included review available in 27 minutes. View limit detailsLimit details: Youβve used all 8 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: βοΈ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: π Files selected for processing (3)
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 |
|
@claude this PR has merge conflicts with |
1 similar comment
|
@claude this PR has merge conflicts with |
|
Claude finished @MervinPraison's task ββ View job Rebase onto latest
|
| File | Conflict | Resolution |
|---|---|---|
agent/chat_mixin.py |
streaming input guardrail | Kept main's approach. Main already runs _validate_input_with_guardrail in _start_stream before the durable run β a strict superset of the PR's intent that also avoids opening a durable run for a blocked prompt. _start_stream_impl keeps the explanatory comment only. |
agent/tool_execution.py |
tool error-dict handling | Kept main's superset. Now raise ToolExecutionError on is_retryable or raised_exception; plain tool-authored {"error": ...} still falls through to the LLM (PR intent preserved), while a raised exception (via _praison_retryable tag) correctly surfaces as a failure. |
agents/agents.py |
per-instance token scoping | Kept the fully-scoped version (_own_agent_names() + _scoped_recent_interactions(own_names)). Scopes by_agent, by_model, total_interactions, totals, and recent_interactions to owned agents. Removed the stale duplicate _scoped_recent_interactions(limit=20) helper introduced during the two-commit replay. |
Note: main had already merged a sibling fix for #4446 (PR #4462) using an alternate helper layout. The rebase reconciles this PR onto that, keeping this PR's clean fully-scoped implementation. No new Agent params, no new public API, no new deps β lightweight mandate honoured.
Verification
py_compile+ast.parseOK on all 3 filesAgentTeamimports; single consistent_scoped_recent_interactions(self, own_names: set)(no duplicate)- Functional isolation test (two teams sharing the process collector):
by_agent=['alice','bob'] by_model=['gpt-4'] interactions=2 tokens=42 recent_interactions β only alice/bob β ALL SCOPING ASSERTIONS PASSED
Skipped
- Full
pytest tests/β blocked by pre-existing missing optional deps (litellm) unrelated to these changes. Ran targeted import + behavioural checks instead.
42dfab5 to
9e2ac59
Compare
|
Claude finished @MervinPraison's task ββ View job Rebase onto latest
|
| File | State |
|---|---|
agents/agents.py |
Kept this PR's fully-scoped token-report implementation (_own_agent_names() + _scoped_recent_interactions(own_names) + _scoped_token_summary()). Scopes by_agent, by_model, total_interactions, totals, and recent_interactions to owned agents. No duplicate helpers β single consistent _scoped_recent_interactions(self, own_names: set). |
Lightweight mandate honoured: no new Agent params, no new public API, no new deps.
Verification
py_compileOK onagents/agents.py- Functional isolation test (two teams sharing the process collector):
A by_agent ['alice','bob'] by_model ['gpt-4'] interactions 2 tokens 42 B by_agent ['carol'] by_model ['claude'] interactions 1 tokens 150 recentA β {alice,bob} recentB β {carol} ALL SCOPING ASSERTIONS PASSED
Skipped
- Full
pytest tests/β blocked by pre-existing missing optional deps (litellm) unrelated to this change. Installedpydantic/richlocally to run the targeted behavioural + import checks instead.
Address Greptile P1 findings: by_model, total_interactions and recent_interactions in the token reports were still copied from the process-global TokenCollector summary, leaking other concurrent PraisonAIAgents instances' usage. Re-derive by_model and total_interactions from this instance's own interaction records and filter recent_interactions to owned agent names. Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
9e2ac59 to
b80cf44
Compare
Fixes #4446
Summary
Addresses all three findings in issue #4446, scoped to
src/praisonai-agents/praisonaiagents.1. Streaming input guardrail (
agent/chat_mixin.py)_start_stream_implβ the shared generator behinditer_stream()andstart(stream=True)β now runs_validate_input_with_guardrailbefore the first token, matchingchat()/achat(). The input side has no streaming constraint (the full prompt is known before dispatch); the output-side warning is untouched. Blocked input yields[Input blocked by guardrail: ...]and returns.2. Tool error dicts no longer crash the run (
agent/tool_execution.py)ToolExecutionErroris now only raised for denials/retryable transients. A plain tool-authored{"error": ...}(the documented convention inshell_tools,tavily_tools, etc.) falls through as a normal tool result so the LLM can see it and self-correct β mirroring the generic-exception branch inopenai_client.py.3. Per-instance token scoping (
agents/agents.py)get_token_usage_summary/get_detailed_token_report/display_token_usagenow filter the process-wideTokenCollectorto the instance's own agent names via a new private_scoped_token_summary()helper. This prevents cross-instance usage/cost leakage between concurrentPraisonAIAgentsinstances. Read-side filter reuses the collector's existingby_agentbreakdown β no new public API, no session-id threading β honouring the lightweight mandate. Falls back to the unfiltered summary when no named agents exist.Test plan
_start_stream_impl{"error": ...}dict instead of raisingRemaining suite failures are pre-existing (missing optional deps:
litellm; collection errors in unrelated modules), not caused by these changes.Generated with Claude Code