Skip to content

fix: eliminate wrapper layer architectural gaps#1723

Open
praisonai-triage-agent[bot] wants to merge 5 commits into
mainfrom
claude/issue-1722-20260522-0813
Open

fix: eliminate wrapper layer architectural gaps#1723
praisonai-triage-agent[bot] wants to merge 5 commits into
mainfrom
claude/issue-1722-20260522-0813

Conversation

@praisonai-triage-agent
Copy link
Copy Markdown
Contributor

@praisonai-triage-agent praisonai-triage-agent Bot commented May 22, 2026

Fixes three architectural gaps in wrapper layer that violate project philosophy.

Gap 1 - Remove 790 lines of dead duplicate framework methods
Gap 2 - Complete async scheduler migration to prevent feature drift
Gap 3 - Replace global singletons with contextvars for thread safety

All changes maintain backward compatibility while aligning with DRY, multi-agent safe, async-safe principles.

Closes #1722

Summary by CodeRabbit

  • New Features

    • Introduced AsyncAgentScheduler for async-native periodic agent execution with schedule expressions, retry support, and execution statistics.
    • Implemented context-local runtime and tool resolution for improved concurrent operation handling.
  • Deprecations

    • Old async_agent_scheduler import path now shows deprecation warning; migrate to praisonai.scheduler.AsyncAgentScheduler.

Review Change Stack

Gap 1 - Remove dead duplicate framework runners (~790 lines):
- Delete _run_autogen, _run_autogen_v4, _run_ag2, _run_crewai, _run_praisonai from agents_generator.py
- These methods were never called (execution goes through framework_adapter.run())
- Eliminates maintenance drift between duplicate implementations

Gap 2 - Complete async scheduler migration:
- Move AsyncAgentScheduler to canonical location scheduler/async_agent_scheduler.py
- Reduce root async_agent_scheduler.py to thin re-export with deprecation warning
- Add TODO comments for missing features (timeout, max_cost, from_yaml, etc.)
- Maintains backward compatibility while stopping feature drift

Gap 3 - Replace global singletons with contextvars:
- interactive_tools.py: _shared_runtime β†’ _runtime_var for per-context config
- tool_resolver.py: _default_resolver β†’ _resolver_var for CWD isolation
- Prevents race conditions and config capture in concurrent execution
- Add reset_default_resolver() for daemon/IDE plugin invalidation

All fixes preserve backward compatibility while aligning with project philosophy:
DRY, multi-agent safe, async-safe, no global singletons, protocol-driven core.

πŸ€– Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@MervinPraison
Copy link
Copy Markdown
Owner

@coderabbitai review

@MervinPraison
Copy link
Copy Markdown
Owner

/review

@qodo-code-review
Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more β†’

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account β†’

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us β†’

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

βœ… Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f54e24cb-f917-42d9-9e5b-e4b83a7b58be

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • πŸ” Trigger review
πŸ“ Walkthrough

Walkthrough

This PR consolidates the async scheduler into the canonical scheduler package, replaces unsafe process-level singletons with context-local state via contextvars, deprecates the old root-level async_agent_scheduler module, and removes 790 lines of dead framework-specific runner methods from agents_generator.

Changes

Unified scheduler and context-safe architecture

Layer / File(s) Summary
Async scheduler core implementation
src/praisonai/praisonai/scheduler/async_agent_scheduler.py
Introduces AsyncAgentScheduler, AsyncAgentExecutorInterface, and AsyncPraisonAgentExecutor to provide async-native periodic execution with configurable schedule expressions, retry logic with backoff, event-loop affinity guarantees via _ensure_async_primitives(), atomic stats tracking under _stats_lock, and callback-driven success/failure handling.
Scheduler module wiring and lazy loading
src/praisonai/praisonai/scheduler/__init__.py
Extends __getattr__ to lazily expose AsyncAgentScheduler and create_async_agent_scheduler from the canonical async_agent_scheduler module.
Deprecation shim for backward compatibility
src/praisonai/praisonai/async_agent_scheduler.py
Converts the root-level module to a thin deprecation wrapper that emits a DeprecationWarning and re-exports the canonical scheduler API surface from .scheduler.async_agent_scheduler.
Context-local runtime and resolver caching
src/praisonai/praisonai/cli/features/interactive_tools.py, src/praisonai/praisonai/tool_resolver.py
Replaces process-wide singletons (_shared_runtime, _shared_agent_tools, _default_resolver) with per-context ContextVar-backed caches guarded by thread locks for first-creation safety. cleanup_runtime() and reset_default_resolver() clear context state when needed, enabling safe multi-context/multi-agent execution without binding resolver or runtime to first-call conditions.

Sequence Diagram(s)

This section is not applicable to the current changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #1722: Primary objective β€” remove dead duplicate framework runners, consolidate async scheduler, and replace unsafe process-level singletons with context-local state.
  • #1707: Both PRs remove the legacy _run_autogen, _run_autogen_v4, _run_ag2, _run_crewai, and _run_praisonai runner implementations from AgentsGenerator and move framework-specific behavior into adapters.
  • #1662: Changes to InteractiveRuntime lifecycle (now context-local instead of process-global) directly address async-safety issues when runtime is started on a throwaway event loop.
  • #1500: Moving ToolResolver to per-context implementation and introducing dedicated async_agent_scheduler module address the fragmented tool registry and daemon async bridge gaps.

Possibly related PRs

  • MervinPraison/PraisonAI#1681: Both modify agents_generator.py around _run_praisonai β€” #1681 changes InteractiveRuntime startup/shutdown, while this PR removes the runner entirely.
  • MervinPraison/PraisonAI#1691: Both modify tool_resolver.py default resolver caching β€” this PR uses per-context contextvars, while #1691 introduces a process-wide singleton.
  • MervinPraison/PraisonAI#1474: Both modify scheduler/async_agent_scheduler.py, refactoring scheduling/retry/callback logic and considering shared ScheduleParser/backoff_delay/safe_call utilities.

Suggested labels

Review effort 2/5

Suggested reviewers

  • MervinPraison

Poem

🐰 The scheduler hops to async shores,
No singletons guard the door,
Context-safe, each agent free,
Dead code trimmed from the tree. ✨

πŸš₯ Pre-merge checks | βœ… 5
βœ… Passed checks (5 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The title 'fix: eliminate wrapper layer architectural gaps' accurately summarizes the main theme of the PR: removing dead code, consolidating implementations, and replacing unsafe singletons to fix architectural issues.
Linked Issues check βœ… Passed The PR successfully addresses all three main objectives from issue #1722: (1) removes ~790 lines of dead framework runner methods in agents_generator.py, (2) consolidates async scheduler to scheduler/async_agent_scheduler.py with thin deprecation re-export, and (3) replaces global singletons with ContextVar-backed context-local state in interactive_tools.py and tool_resolver.py.
Out of Scope Changes check βœ… Passed All changes are scoped to fixing the three wrapper layer architectural gaps. The modifications to async_agent_scheduler.py's init.py, while supporting the migration, are in-scope additions for exposing moved functionality.
Docstring Coverage βœ… Passed Docstring coverage is 95.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/issue-1722-20260522-0813

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.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@MervinPraison
Copy link
Copy Markdown
Owner

@copilot Do a thorough review of this PR. Read ALL existing reviewer comments above from Qodo, Coderabbit, and Gemini first β€” incorporate their findings.

Review areas:

  1. Bloat check: Are changes minimal and focused? Any unnecessary code or scope creep?
  2. Security: Any hardcoded secrets, unsafe eval/exec, missing input validation?
  3. Performance: Any module-level heavy imports? Hot-path regressions?
  4. Tests: Are tests included? Do they cover the changes adequately?
  5. Backward compat: Any public API changes without deprecation?
  6. Code quality: DRY violations, naming conventions, error handling?
  7. Address reviewer feedback: If Qodo, Coderabbit, or Gemini flagged valid issues, include them in your review
  8. Suggest specific improvements with code examples where possible

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 22, 2026

Greptile Summary

This PR removes ~795 lines of dead framework-dispatch methods from agents_generator.py, promotes the async scheduler to a canonical location under praisonai.scheduler, and migrates two global singletons (_shared_runtime and _default_resolver) to contextvars.ContextVar for per-context isolation.

  • Gap 1 (dead code removal): _run_autogen, _run_autogen_v4, _run_ag2, _run_crewai, and _run_praisonai are confirmed unreferenced in the file and are safely deleted.
  • Gap 2 (async scheduler migration): praisonai/async_agent_scheduler.py becomes a thin deprecation shim re-exporting from the new canonical praisonai/scheduler/async_agent_scheduler.py; the warning is promoted from PendingDeprecationWarning to DeprecationWarning, but the test file's suppressor was not updated to match.
  • Gap 3 (ContextVar isolation): Both the interactive-runtime cache and the tool resolver now use ContextVar, preventing CWD lock-in between agents; reset_default_resolver() is added as an explicit invalidation hook for daemon/IDE scenarios.

Confidence Score: 4/5

Safe to merge after fixing the test deprecation filter; the dead-code deletion and ContextVar migrations are correct and the new canonical scheduler implementation is functionally sound.

The test file imports the deprecated shim inside a warnings.catch_warnings() block that suppresses PendingDeprecationWarning, but the warning emitted by the shim was upgraded to DeprecationWarning in this same PR. Any CI run with -W error::DeprecationWarning will fail to collect the entire test file. All other changes β€” dead-code removal, scheduler migration, and ContextVar isolation β€” are well-scoped and do not introduce regressions.

src/praisonai/tests/unit/scheduler/test_async_agent_scheduler.py β€” the warning filter class mismatch will cause test-collection failures under strict warning configs.

Important Files Changed

Filename Overview
src/praisonai/praisonai/agents_generator.py Removes 795 lines of dead framework dispatch methods (_run_praisonai, _run_autogen, _run_autogen_v4, _run_ag2, _run_crewai) confirmed unreferenced; also updates a stale comment from the previous review cycle.
src/praisonai/praisonai/async_agent_scheduler.py Converts to a thin deprecation shim that re-exports from the canonical scheduler package. Warning upgraded from PendingDeprecationWarning to DeprecationWarning, but the test file's suppressor still targets PendingDeprecationWarning.
src/praisonai/praisonai/cli/features/interactive_tools.py Replaces process-global _shared_runtime singleton with ContextVar-based per-context isolation; cleanup_runtime correctly clears only the current context's runtime.
src/praisonai/praisonai/scheduler/init.py Adds lazy-load entries for AsyncAgentScheduler and create_async_agent_scheduler to the scheduler package's getattr dispatcher.
src/praisonai/praisonai/scheduler/async_agent_scheduler.py New canonical async scheduler implementation with corrected get_stats_sync (was a broken coroutine-returning stub), separate asyncio.TimeoutError handling with backoff, and loop-bound async primitives.
src/praisonai/praisonai/tool_resolver.py Replaces process-level singleton + threading.Lock with ContextVar-based per-context resolver; adds reset_default_resolver() for explicit invalidation in daemon/IDE plugin scenarios.
src/praisonai/tests/unit/scheduler/test_async_agent_scheduler.py Updates logger patch paths to the new canonical module; but the import-time warning suppressor still uses PendingDeprecationWarning while the warning was upgraded to DeprecationWarning, causing CI failures under -W error.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant OldShim as praisonai.async_agent_scheduler (deprecated shim)
    participant Canonical as praisonai.scheduler.AsyncAgentScheduler
    participant SchedulerInit as praisonai.scheduler.__init__
    participant ContextVar as contextvars.ContextVar

    Note over Caller,ContextVar: Deprecation shim re-export path
    Caller->>OldShim: import AsyncAgentScheduler
    OldShim-->>Caller: DeprecationWarning (was PendingDeprecationWarning)
    OldShim->>Canonical: re-export AsyncAgentScheduler

    Note over Caller,ContextVar: New canonical path
    Caller->>SchedulerInit: from praisonai.scheduler import AsyncAgentScheduler
    SchedulerInit->>Canonical: lazy __getattr__ load

    Note over Canonical,ContextVar: Async scheduler lifecycle
    Caller->>Canonical: scheduler.start(schedule_expr)
    Canonical->>Canonical: _ensure_async_primitives() β€” bind to event loop
    loop Every interval
        Canonical->>Canonical: _execute_with_retry(max_retries)
        alt TimeoutError
            Canonical->>Canonical: backoff_delay + asyncio.sleep
        else Exception
            Canonical->>Canonical: backoff_delay + asyncio.sleep
        end
    end
    Caller->>Canonical: scheduler.stop()

    Note over Caller,ContextVar: ContextVar isolation
    Caller->>ContextVar: _resolver_var.get() β€” None on first call
    ContextVar-->>Caller: creates new ToolResolver anchored to CWD
    Caller->>ContextVar: _resolver_var.set(resolver)
    Caller->>ContextVar: reset_default_resolver() to invalidate
Loading

Reviews (4): Last reviewed commit: "fix: resolve merge conflicts by removing..." | Re-trigger Greptile

Comment thread src/praisonai/praisonai/scheduler/async_agent_scheduler.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/praisonai/praisonai/tool_resolver.py (1)

502-506: ⚠️ Potential issue | 🟑 Minor | ⚑ Quick win

Stale docstring references "process-level" cache.

The docstring still says "uses a process-level cached resolver" but the implementation now uses a context-level cache via ContextVar. Update the documentation to reflect the new behavior.

πŸ“ Fix stale docstring
     Note:
-        When resolver=None, uses a process-level cached resolver that is anchored
+        When resolver=None, uses a context-level cached resolver that is anchored
         to the working directory at first call. For test isolation or multi-project
         CLIs, pass an explicit resolver instance.
πŸ€– Prompt for 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.

In `@src/praisonai/praisonai/tool_resolver.py` around lines 502 - 506, The
docstring for the resolver parameter is stale: instead of saying "process-level
cached resolver" it should state that when resolver=None the implementation uses
a ContextVar-based (context-level) cached resolver anchored to the working
directory at first call; update the sentence in the docstring (the paragraph
describing resolver=None) to mention ContextVar/context-level cache and keep the
guidance to pass an explicit resolver for test isolation or multi-project CLIs,
referencing the resolver=None behavior and the ContextVar-based cache.
🧹 Nitpick comments (2)
src/praisonai/praisonai/scheduler/async_agent_scheduler.py (1)

128-128: πŸ’€ Low value

Remove unused _cancel_event attribute.

_cancel_event is declared but never used anywhere in the class. This appears to be dead code that should either be removed or have a TODO comment explaining its intended future use.

🧹 Proposed fix to remove unused attribute
         self._primitives_lock = threading.Lock()
         self._stop_event: Optional[asyncio.Event] = None
-        self._cancel_event: Optional[asyncio.Event] = None
         self._stats_lock: Optional[asyncio.Lock] = None
         self._bound_loop: Optional[asyncio.AbstractEventLoop] = None

And in _ensure_async_primitives:

         with self._primitives_lock:
             if self._bound_loop is not loop:
                 self._stop_event = asyncio.Event()
-                self._cancel_event = asyncio.Event()
                 self._stats_lock = asyncio.Lock()
                 self._bound_loop = loop
πŸ€– Prompt for 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.

In `@src/praisonai/praisonai/scheduler/async_agent_scheduler.py` at line 128, The
_cancel_event attribute declared on AsyncAgentScheduler is unused dead code;
remove the declaration "self._cancel_event: Optional[asyncio.Event] = None" and
any related unused references, or if you intend to keep it for future use add a
clear TODO comment near the declaration explaining planned usage; also update
the _ensure_async_primitives method if it attempted to initialize or reference
_cancel_event to reflect the removal or the documented future intent.
src/praisonai/praisonai/cli/features/interactive_tools.py (1)

214-216: πŸ’€ Low value

Silent config mismatch when runtime is already cached.

Once a runtime is created for a context, subsequent calls with a different config will silently return the cached runtime, ignoring the new config. This could lead to unexpected behavior where a different workspace or approval_mode is silently ignored.

Consider logging a warning when the cached runtime's config differs from the requested config, or document this as intentional "first-caller-wins" behavior per context.

πŸ”§ Optional: add mismatch detection
 def _get_shared_runtime(config: ToolConfig):
     bundle = _runtime_var.get()
     if bundle is not None:
+        # Optionally warn if config differs from cached runtime's config
+        cached_runtime, _ = bundle
+        # Note: would require storing config alongside bundle to compare
         return bundle
πŸ€– Prompt for 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.

In `@src/praisonai/praisonai/cli/features/interactive_tools.py` around lines 214 -
216, The cached runtime returned by _runtime_var.get() can silently ignore a new
config; update the code in the function that obtains the runtime (where bundle =
_runtime_var.get()) to detect when bundle is not None and its stored config
(e.g., bundle.config or bundle.workspace/approval_mode fields) differs from the
incoming requested config parameter, and emit a warning or logger.warn message
describing the mismatch (include both requested and cached values) so callers
are notified; alternatively, if "first-caller-wins" is intended, add a clear
docstring comment in that function explaining the behavior and why mismatches
are ignored.
πŸ€– 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 `@src/praisonai/praisonai/scheduler/async_agent_scheduler.py`:
- Around line 361-372: The TimeoutError handler currently logs and sets last_exc
but skips the backoff sleep, causing rapid retries; modify the
asyncio.TimeoutError except block in async_agent_scheduler (where attempt,
max_retries, backoff_delay, last_exc, and logger are used) to behave like the
generic Exception handler by computing wait_time = backoff_delay(attempt) and
awaiting asyncio.sleep(wait_time) when attempt < max_retries - 1, ensuring the
same backoff delay is applied before retrying after timeouts.

---

Outside diff comments:
In `@src/praisonai/praisonai/tool_resolver.py`:
- Around line 502-506: The docstring for the resolver parameter is stale:
instead of saying "process-level cached resolver" it should state that when
resolver=None the implementation uses a ContextVar-based (context-level) cached
resolver anchored to the working directory at first call; update the sentence in
the docstring (the paragraph describing resolver=None) to mention
ContextVar/context-level cache and keep the guidance to pass an explicit
resolver for test isolation or multi-project CLIs, referencing the resolver=None
behavior and the ContextVar-based cache.

---

Nitpick comments:
In `@src/praisonai/praisonai/cli/features/interactive_tools.py`:
- Around line 214-216: The cached runtime returned by _runtime_var.get() can
silently ignore a new config; update the code in the function that obtains the
runtime (where bundle = _runtime_var.get()) to detect when bundle is not None
and its stored config (e.g., bundle.config or bundle.workspace/approval_mode
fields) differs from the incoming requested config parameter, and emit a warning
or logger.warn message describing the mismatch (include both requested and
cached values) so callers are notified; alternatively, if "first-caller-wins" is
intended, add a clear docstring comment in that function explaining the behavior
and why mismatches are ignored.

In `@src/praisonai/praisonai/scheduler/async_agent_scheduler.py`:
- Line 128: The _cancel_event attribute declared on AsyncAgentScheduler is
unused dead code; remove the declaration "self._cancel_event:
Optional[asyncio.Event] = None" and any related unused references, or if you
intend to keep it for future use add a clear TODO comment near the declaration
explaining planned usage; also update the _ensure_async_primitives method if it
attempted to initialize or reference _cancel_event to reflect the removal or the
documented future intent.
πŸͺ„ 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 84ca7ebe-3080-4cb2-a526-67c401f2dc20

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 6971d8e and 173d40c.

πŸ“’ Files selected for processing (6)
  • src/praisonai/praisonai/agents_generator.py
  • src/praisonai/praisonai/async_agent_scheduler.py
  • src/praisonai/praisonai/cli/features/interactive_tools.py
  • src/praisonai/praisonai/scheduler/__init__.py
  • src/praisonai/praisonai/scheduler/async_agent_scheduler.py
  • src/praisonai/praisonai/tool_resolver.py
πŸ’€ Files with no reviewable changes (1)
  • src/praisonai/praisonai/agents_generator.py

Comment on lines +361 to +372
except asyncio.TimeoutError as e:
last_exc = e
logger.error(f"Async execution timeout on attempt {attempt + 1}: {e}")
# Continue to retry logic
except Exception as e:
last_exc = e
logger.error(f"Async agent execution failed on attempt {attempt + 1}: {e}")

if attempt < max_retries - 1:
wait_time = backoff_delay(attempt)
logger.info(f"Waiting {wait_time}s before async retry...")
await asyncio.sleep(wait_time)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor | ⚑ Quick win

TimeoutError skips backoff delay before retry.

When asyncio.TimeoutError is caught, the code falls through to the next iteration without applying the backoff delay, unlike general exceptions which wait before retrying. This inconsistency could cause rapid retry attempts after timeouts.

πŸ› Proposed fix to apply backoff for timeouts
             except asyncio.TimeoutError as e:
                 last_exc = e
                 logger.error(f"Async execution timeout on attempt {attempt + 1}: {e}")
-                # Continue to retry logic
+                if attempt < max_retries - 1:
+                    wait_time = backoff_delay(attempt)
+                    logger.info(f"Waiting {wait_time}s before async retry...")
+                    await asyncio.sleep(wait_time)
             except Exception as e:
🧰 Tools
πŸͺ› Ruff (0.15.13)

[warning] 365-365: Do not catch blind exception: Exception

(BLE001)

πŸ€– Prompt for 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.

In `@src/praisonai/praisonai/scheduler/async_agent_scheduler.py` around lines 361
- 372, The TimeoutError handler currently logs and sets last_exc but skips the
backoff sleep, causing rapid retries; modify the asyncio.TimeoutError except
block in async_agent_scheduler (where attempt, max_retries, backoff_delay,
last_exc, and logger are used) to behave like the generic Exception handler by
computing wait_time = backoff_delay(attempt) and awaiting
asyncio.sleep(wait_time) when attempt < max_retries - 1, ensuring the same
backoff delay is applied before retrying after timeouts.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 22, 2026

@copilot Do a thorough review of this PR. Read ALL existing reviewer comments above from Qodo, Coderabbit, and Gemini first β€” incorporate their findings.

Review areas:...

Reviewed thoroughly and incorporated all available reviewer output.

Findings from other reviewers:

  • Qodo: paused for this user (no actionable findings).
  • CodeRabbit: only "review in progress"/trigger comments, no code issues posted yet.
  • Gemini: no PR review comment was posted; gemini workflow runs on this branch failed before jobs started (0 jobs), so no actionable review output there.

My review results:

  1. Bloat: scope is focused on the 3 stated architectural gaps; no extra feature creep found.
  2. Security: no hardcoded secrets, no unsafe eval/exec added; local tools execution remains gated by PRAISONAI_ALLOW_LOCAL_TOOLS.
  3. Performance: no heavy new module-level imports on hot paths; contextvar usage is lightweight and appropriate.
  4. Tests: added compatibility follow-up and fixed scheduler logger patch target in tests; targeted suite passed (65/65).
  5. Backward compat: added logger re-export on praisonai.async_agent_scheduler and kept deprecation path intact.
  6. Code quality: fixed stale comments/docs caused by migration.

Applied in commit 4f26544.

One concrete improvement suggestion (optional follow-up): add an explicit test for reset_default_resolver() context invalidation, e.g.
reset_default_resolver(); assert resolve_tool("...") uses resolver bound after CWD switch.

@MervinPraison
Copy link
Copy Markdown
Owner

@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: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.

Phase 1: Review per AGENTS.md

  1. Protocol-driven: check heavy implementations vs core SDK
  2. Backward compatible: ensure zero feature regressions
  3. Performance: no hot-path regressions

Phase 2: FIX Valid Issues
4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix
5. Push all code fixes directly to THIS branch (do NOT create a new PR)
6. Comment a summary of exact files modified and what you skipped

Phase 3: Final Verdict
7. If all issues are resolved, approve the PR / close the Issue
8. If blocking issues remain, request changes / leave clear action items

@praisonai-triage-agent
Copy link
Copy Markdown
Contributor Author

praisonai-triage-agent Bot commented May 22, 2026

Claude finished @MervinPraison's task β€”β€” View job


Final Architecture Review & Issue Resolution βœ…

Performed comprehensive review and fixed all valid issues identified by reviewers.

Todo List:

  • Read AGENTS.md architecture guidelines
  • Review existing feedback from Greptile, CodeRabbit, Copilot
  • Analyze PR changes against architecture requirements
  • Fix valid issues identified by reviewers
  • Validate backward compatibility
  • Push fixes to branch
  • Provide final verdict

Issues Fixed

βœ… Critical Issue 1: Hot Retry Loop on Timeout (P1)

  • Problem: asyncio.TimeoutError branch skipped backoff delay causing immediate retries
  • Fix: Added proper backoff delay for timeout errors same as generic exceptions
  • File: scheduler/async_agent_scheduler.py:361-367

βœ… Critical Issue 2: Missing Public API Methods (P1)

  • Problem: get_stats_async() and get_stats_sync() methods missing from new implementation
  • Fix: Restored all three methods (get_stats(), get_stats_async(), get_stats_sync())
  • Impact: Full backward compatibility maintained

βœ… Issue 3: Unused Dead Code (P2)

  • Problem: _cancel_event attribute declared but never used
  • Fix: Removed unused attribute and references
  • File: scheduler/async_agent_scheduler.py:128,143

βœ… Issue 4: Misleading Lock Pattern (P1)

  • Problem: Process-wide lock with context-local ContextVar provided no actual protection
  • Fix: Removed unnecessary lock, documented first-caller-wins behavior
  • File: cli/features/interactive_tools.py:202,218

Architecture Compliance βœ…

βœ… Protocol-driven: Changes correctly move heavy implementation to wrapper layer
βœ… Performance: No regressions - lightweight ContextVar usage, no heavy module imports
βœ… Multi-agent safe: Successfully replaces global singletons with context-local state
βœ… Async-safe: Proper asyncio patterns, event loop affinity, timeout handling
βœ… DRY approach: Removes 790 lines of duplicate framework runner code
βœ… Backward compatible: Full deprecation warnings and re-exports maintained

Final Verdict: βœ… APPROVED

This PR successfully fixes the three wrapper layer architectural gaps as described:

  1. Gap 1 βœ… - Removed 790 lines of dead duplicate framework methods
  2. Gap 2 βœ… - Completed async scheduler migration with proper deprecation path
  3. Gap 3 βœ… - Replaced global singletons with context-safe ContextVar implementation

All critical reviewer issues have been resolved. The PR maintains full backward compatibility while properly aligning with AGENTS.md architectural principles. Ready for merge.

Modified Files

  • scheduler/async_agent_scheduler.py - Fixed timeout backoff, restored missing API methods, removed dead code
  • cli/features/interactive_tools.py - Removed unnecessary process-wide lock, improved documentation

praisonai-triage-agent Bot and others added 2 commits May 22, 2026 08:36
- Fix hot retry loop on asyncio.TimeoutError by adding backoff delay
- Remove unused _cancel_event attribute from AsyncAgentScheduler
- Add missing get_stats_async() and get_stats_sync() methods for backward compatibility
- Remove unnecessary process-wide lock from context-local runtime creation
- Document first-caller-wins behavior for runtime config

Addresses critical issues identified by Greptile and CodeRabbit reviewers.
Maintains full backward compatibility while fixing architectural gaps.

Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
Use direct synchronous access instead of trying to coordinate with event loop.
This prevents timeouts and maintains backward compatibility.
@MervinPraison
Copy link
Copy Markdown
Owner

@claude fix teh merge conflict and push the changes to this same branch

@praisonai-triage-agent
Copy link
Copy Markdown
Contributor Author

praisonai-triage-agent Bot commented May 22, 2026

Claude finished @MervinPraison's task β€”β€” View job


Final Status: Merge Conflicts Successfully Resolved βœ…

Todo List:

  • Check git status for merge conflicts
  • Identify merge conflicts in agents_generator.py
  • Examine the conflict to understand the changes needed
  • Resolve merge conflicts by removing dead framework runner methods
  • Review previous reviewer feedback and verify fixes are applied
  • Stage resolved changes
  • Commit resolved changes
  • Push changes to branch
  • Verify changes are applied

Summary

Successfully resolved merge conflicts in src/praisonai/praisonai/agents_generator.py by removing the 790+ lines of dead duplicate framework runner methods (_run_autogen, _run_autogen_v4, _run_ag2, _run_crewai, _run_praisonai). This aligns with the PR's objective to eliminate "Gap 1" from issue #1722.

Issues Addressed

βœ… All Previous Reviewer Issues Verified as Fixed:

  • TimeoutError backoff fix: Properly implemented - timeout errors now include backoff delay before retry
  • Missing get_stats methods: All present - get_stats(), get_stats_async(), and get_stats_sync() are implemented
  • Unused _cancel_event: Removed - no longer in codebase
  • Docstring updates: Applied - tool_resolver.py correctly mentions "context-local cached resolver"

Changes Applied

Commit: d0cb8a54 - "fix: resolve merge conflicts by removing dead framework runner methods"

  • Removed 790+ lines of duplicate framework methods
  • Maintains backward compatibility through framework adapter pattern
  • Preserves all architectural improvements from previous commits
  • All reviewer feedback has been addressed

The branch claude/issue-1722-20260522-0813 is now up to date and ready for merge. All three architectural gaps from issue #1722 have been successfully addressed while maintaining full backward compatibility.


Removes 790+ lines of duplicate framework methods (_run_autogen, _run_autogen_v4,
_run_ag2, _run_crewai, _run_praisonai) that are now handled by framework adapters.

This completes Gap 1 from issue #1722: eliminate dead duplicate framework methods
while maintaining backward compatibility through the adapter pattern.

All previous reviewer feedback has been addressed:
- TimeoutError backoff delay fixed in scheduler
- Missing get_stats methods restored
- Unused _cancel_event removed
- Context-local docstring updated

Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrapper gaps: dead duplicate framework runners, divergent async scheduler, unsafe shared singletons in src/praisonai/praisonai

2 participants