Skip to content

fix: self-correcting tool resolution with suggestions and strict mode#3156

Open
praisonai-triage-agent[bot] wants to merge 2 commits into
mainfrom
claude/issue-3153-20260718-0850
Open

fix: self-correcting tool resolution with suggestions and strict mode#3156
praisonai-triage-agent[bot] wants to merge 2 commits into
mainfrom
claude/issue-3153-20260718-0850

Conversation

@praisonai-triage-agent

@praisonai-triage-agent praisonai-triage-agent Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #3153

Summary

Mistyped/unknown tool names previously produced a silently tool-less agent — a log-only warning with no suggestion and no way to fail fast. This makes the canonical core resolver (praisonaiagents/tools/resolver.py) loud and self-correcting, benefiting all three surfaces (Python, YAML, CLI) that share it.

Changes (src/praisonai-agents/praisonaiagents/tools/resolver.py)

  • Nearest-name suggestions: _available_tool_names() builds a catalogue from the registry, TOOL_MAPPINGS, and (optional) praisonai-tools __all__; _closest_names() uses difflib to suggest near matches.
  • Opt-in strict mode: resolve_tool_names(names, *, strict=None, on_unknown=None). strict=True (or PRAISONAI_STRICT_TOOLS=true) raises a typed ToolResolutionError carrying {unknown, suggestions}.
  • Backward compatible: default remains non-raising; the warning now includes suggestions, e.g. Unknown tool 'internet_serch'. Did you mean 'internet_search'? Run 'praisonai tools list'.
  • Optional on_unknown callback lets callers (e.g. the wrapper CLI) render suggestions their own way.

Scope

Kept minimal and lightweight per AGENTS.md — the single canonical resolver is the correct and only place needed to fix Python + YAML silent-drops. No Agent params added, no new modules/exports beyond the typed error.

Tests

Added strict/env/callback/suggestion tests to tests/unit/tools/test_tool_resolver.py. All 8 pass.

Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for resolving multiple tool names with clearer diagnostics.
    • Added typo-based suggestions for unknown tool names.
    • Added optional strict mode that reports unresolved tools as errors.
    • Added callback support for handling unknown tools programmatically.
    • Strict mode can be enabled through the PRAISONAI_STRICT_TOOLS environment setting.
  • Bug Fixes

    • Improved handling and reporting of invalid or misspelled tool names.

…fixes #3153)

Unknown/mistyped tool names now surface near-name suggestions via difflib
over the available-tool catalogue (registry + TOOL_MAPPINGS + praisonai-tools).
Adds opt-in strict mode (strict= arg / PRAISONAI_STRICT_TOOLS env) that raises
ToolResolutionError carrying unknown names and suggestions. Default stays
backward-compatible (warn + suggest, no raise).

Co-authored-by: MervinPraison <MervinPraison@users.noreply.github.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

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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.

@MervinPraison MervinPraison added pipeline/blocked:ci Blocked: CI not green on HEAD pipeline/blocked:no-final Blocked: no FINAL @claude trigger yet pipeline/final-claude-pending Reviews done; waiting for FINAL @claude labels Jul 18, 2026
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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: 3e8fcd93-d622-48d5-b4e2-cf436a5762b0

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

The tool resolver now detects unresolved names across multiple inputs, builds typo suggestions from available tools, supports callback or warning diagnostics, and raises ToolResolutionError when strict mode is enabled. Unit tests cover explicit strictness, environment-based strictness, callbacks, and suggestions.

Changes

Tool Resolution Diagnostics

Layer / File(s) Summary
Tool catalog and diagnostic construction
src/praisonai-agents/praisonaiagents/tools/resolver.py
The resolver discovers known tool names, computes close matches, formats consolidated unknown-tool messages, logs warnings, and defines ToolResolutionError.
Resolution modes and validation
src/praisonai-agents/praisonaiagents/tools/resolver.py, src/praisonai-agents/tests/unit/tools/test_tool_resolver.py
resolve_tool_names accumulates misses, supports explicit or environment-based strictness and callbacks, and raises typed errors in strict mode; tests validate these behaviors and typo suggestions.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested labels: pipeline/awaiting-merge-gate

Suggested reviewers: mervinpraison

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant resolve_tool_names
  participant resolve_tool_name
  participant _closest_names
  participant on_unknown
  participant ToolResolutionError

  Caller->>resolve_tool_names: provide tool names and resolution options
  resolve_tool_names->>resolve_tool_name: resolve each name
  resolve_tool_name-->>resolve_tool_names: resolved tool or unknown result
  resolve_tool_names->>_closest_names: compute suggestions for unknown names
  alt strict mode
    resolve_tool_names->>ToolResolutionError: raise unknown names and suggestions
  else callback provided
    resolve_tool_names->>on_unknown: report unknown names and suggestions
  end
  resolve_tool_names-->>Caller: return resolved tools
Loading
🚥 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 clearly names the core change: self-correcting tool resolution with suggestions and strict mode.
Linked Issues check ✅ Passed The resolver now warns with suggestions by default and raises ToolResolutionError in strict mode, matching #3153's core requirements.
Out of Scope Changes check ✅ Passed The changes stay focused on the resolver and its tests; no unrelated functionality appears introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/issue-3153-20260718-0850

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.

@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR makes unknown tool resolution visible and optionally strict across shared agent surfaces. The main changes are:

  • Adds nearest-name suggestions for unknown tools.
  • Adds strict mode with typed resolution errors.
  • Adds custom unknown-tool callbacks and clearer warnings.
  • Preserves typed errors through Agent toolset resolution.
  • Exports the resolver API from the tools package.
  • Adds tests for strict mode, suggestions, callbacks, exports, and toolsets.

Confidence Score: 5/5

This looks safe to merge.

  • The toolset path now re-raises the original typed error before generic error wrapping.
  • The added test covers the previously broken toolset propagation path.
  • No blocking issue remains in the updated code.

Important Files Changed

Filename Overview
src/praisonai-agents/praisonaiagents/agent/agent.py Preserves typed tool-resolution errors raised while resolving tools from an Agent toolset.
src/praisonai-agents/praisonaiagents/tools/init.py Exports the resolver functions and typed resolution error from the tools package.
src/praisonai-agents/praisonaiagents/tools/resolver.py Adds suggestions, strict resolution, typed errors, callbacks, and improved unknown-tool warnings.
src/praisonai-agents/tests/unit/tools/test_tool_resolver.py Covers strict and environment behavior, callbacks, suggestions, exports, and typed error propagation.

Reviews (2): Last reviewed commit: "fix: preserve typed ToolResolutionError ..." | Re-trigger Greptile

_praisonai_tools_available: Optional[bool] = None


class ToolResolutionError(ValueError):

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.

P1 Toolset Path Erases Typed Error

When strict mode rejects a name supplied through an Agent toolset, the existing toolset handler catches this ValueError subclass and replaces it with a plain ValueError. Callers cannot catch ToolResolutionError or read its unknown and suggestions fields on that path, so the new self-correction contract works for direct tool lists but is lost for toolsets.

@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
  4. SDK value: review in depth whether the change genuinely adds value to the SDK — never add features for the sake of adding them. It must strengthen the SDK (simpler, more user-friendly, robust, world-class, secure). If it does not clearly add value, request changes or recommend rejecting/closing rather than merging scope creep
  5. Do not bloat the Agent class with additional params — only if absolutely required; we already support many params.
  6. Repo routing: agent-callable tools → PraisonAI-Tools; lifecycle plugins → PraisonAI-Plugins; optional sandbox backends → PraisonAI-Plugins (praisonai.sandbox entry point) — request changes if wrongly added to praisonaiagents/

Phase 2: FIX Valid Issues
7. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix
8. Also independently identify and fix any gaps or issues you find in the changed code — do not rely only on prior reviewer feedback
9. Push all code fixes directly to THIS branch (do NOT create a new PR)
10. Comment a summary of exact files modified and what you skipped

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

@MervinPraison MervinPraison added pipeline/awaiting-merge-gate FINAL done; waiting for merge gate / CI pipeline/blocked:cooldown Blocked: post-push or @claude cooldown and removed pipeline/final-claude-pending Reviews done; waiting for FINAL @claude pipeline/blocked:no-final Blocked: no FINAL @claude trigger yet labels Jul 18, 2026
@praisonai-triage-agent

praisonai-triage-agent Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

Claude finished @MervinPraison's task —— View job


Final Architecture Review — PR #3156 ✅ Approve (with fix applied)

  • Read AGENTS.md & resolver.py
  • Verify Greptile P1 (toolset path erases typed error)
  • Review SDK value / scope / backward-compat
  • Implement valid fixes
  • Run tests (10 passed)
  • Final verdict

Phase 1 — Architecture review per AGENTS.md

Check Result
Protocol-driven / correct routing ✅ Fix lives in the single canonical core resolver (praisonaiagents/tools/resolver.py) — the right place. No heavy impl added; no tools/plugins misrouted.
Backward compatible ✅ Default stays non-raising. Strict mode is opt-in (strict=/PRAISONAI_STRICT_TOOLS). Warning now includes suggestions — pure improvement.
Performance ✅ No hot-path cost. difflib suggestion + _available_tool_names() catalogue only run on the error path (when a name is unknown).
SDK value ✅ Genuine: turns a silent tool-less agent into a loud, self-correcting one across Python/YAML/CLI. Not scope creep.
Agent-class bloat Zero new Agent params. Behaviour is env/kwarg driven on the shared resolver.

Phase 2 — Valid issue found & fixed

Greptile P1 (VALID): Toolset path erased the typed error. ToolResolutionError subclasses ValueError, and Agent.__init__'s toolset handler caught (ValueError, KeyError) (agent.py:1782) and re-raised a plain ValueError, dropping .unknown / .suggestions. So strict mode's self-correction contract held for tools= but was lost for toolsets=.

Fix applied (pushed to this branch):

  • praisonaiagents/agent/agent.py — re-raise ToolResolutionError untouched before the generic (ValueError, KeyError) handler, preserving .unknown/.suggestions on the toolset path.
  • praisonaiagents/tools/__init__.py — export resolve_tool_name, resolve_tool_names, ToolResolutionError (+ __all__) so strict callers can actually except ToolResolutionError (completes the contract; consistent with existing ToolValidationError/ToolTimeoutError exports). Verified no circular import (resolver's registry/mapping imports are lazy).
  • tests/unit/tools/test_tool_resolver.py — added TestToolsetStrictPropagation regression (typed error survives the toolsets= path) + an export test.

Skipped (intentionally): no new Agent params, no new modules, no docs/wrapper changes — kept minimal per AGENTS.md.

Phase 3 — Verdict

Approve. The one blocking P1 is resolved; the change is lightweight, backward-compatible, well-scoped to the core resolver, and now fully consistent across tools= and toolsets=.

Tests: 10 passed (tests/unit/tools/test_tool_resolver.py), package imports clean.

Note: I can't submit a formal GitHub approval (bot restriction), but no blocking issues remain from my review.

Job run · branch claude/issue-3153-20260718-0850

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
src/praisonai-agents/praisonaiagents/tools/resolver.py (1)

77-107: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider logging exceptions instead of suppressing them.

The static analysis tools flagged multiple instances of catching a blind Exception and passing without logging. While the intention here is to make tool-name discovery best-effort, silently swallowing all exceptions can mask underlying issues (e.g., syntax or attribute errors during module initialization). Consider logging these exceptions at the debug level to aid troubleshooting.

💡 Proposed refactor to log exceptions
 def _available_tool_names() -> List[str]:
     """Best-effort catalogue of known tool names for suggestions."""
     names: set = set()
 
     try:
         from .registry import get_registry
 
         names.update(get_registry().list_tools())
-    except Exception:
-        pass
+    except Exception as e:
+        logger.debug("Failed to list tools from registry: %s", e)
 
     try:
         from . import TOOL_MAPPINGS
 
         names.update(TOOL_MAPPINGS.keys())
-    except Exception:
-        pass
+    except Exception as e:
+        logger.debug("Failed to read TOOL_MAPPINGS: %s", e)
 
     global _praisonai_tools_available
     if _praisonai_tools_available is None:
         _praisonai_tools_available = importlib.util.find_spec("praisonai_tools") is not None
     if _praisonai_tools_available:
         try:
             import praisonai_tools
 
             names.update(getattr(praisonai_tools, "__all__", []) or [])
-        except Exception:
-            pass
+        except Exception as e:
+            logger.debug("Failed to inspect praisonai_tools: %s", e)
 
     return sorted(names)
🤖 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-agents/praisonaiagents/tools/resolver.py` around lines 77 -
107, Update the exception handlers in _available_tool_names to log caught
exceptions at debug level while preserving best-effort discovery and continued
execution. Use the module’s existing logger if available, and include contextual
messages for registry, TOOL_MAPPINGS, and praisonai_tools discovery failures.

Source: Linters/SAST tools

🤖 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.

Nitpick comments:
In `@src/praisonai-agents/praisonaiagents/tools/resolver.py`:
- Around line 77-107: Update the exception handlers in _available_tool_names to
log caught exceptions at debug level while preserving best-effort discovery and
continued execution. Use the module’s existing logger if available, and include
contextual messages for registry, TOOL_MAPPINGS, and praisonai_tools discovery
failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5beb9b31-a1f9-4118-9386-eb53c4986b8d

📥 Commits

Reviewing files that changed from the base of the PR and between 74b5f0f and 6028acc.

📒 Files selected for processing (2)
  • src/praisonai-agents/praisonaiagents/tools/resolver.py
  • src/praisonai-agents/tests/unit/tools/test_tool_resolver.py

Strict-mode toolset resolution flattened ToolResolutionError (a ValueError
subclass) into a plain ValueError, dropping .unknown/.suggestions. Re-raise
the typed error before the generic handler and export the resolver API from
praisonaiagents.tools so strict callers can catch it.

Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
@MervinPraison MervinPraison added pipeline/blocked:stale-final Blocked: FINAL stale after new commits and removed pipeline/blocked:stale-final Blocked: FINAL stale after new commits labels Jul 18, 2026
@praisonai-triage-agent praisonai-triage-agent Bot removed the pipeline/blocked:cooldown Blocked: post-push or @claude cooldown label Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pipeline/awaiting-merge-gate FINAL done; waiting for merge gate / CI pipeline/blocked:ci Blocked: CI not green on HEAD

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fail-fast, self-correcting tool resolution: mistyped tool names silently produce a tool-less agent

1 participant