Skip to content

feat: Add on_tool_not_found event (#107)#115

Open
esreekarreddy wants to merge 1 commit intoopenonion:mainfrom
esreekarreddy:feature/on-tool-not-found
Open

feat: Add on_tool_not_found event (#107)#115
esreekarreddy wants to merge 1 commit intoopenonion:mainfrom
esreekarreddy:feature/on-tool-not-found

Conversation

@esreekarreddy
Copy link
Copy Markdown
Contributor

Summary

Add event that fires when the LLM requests a tool that doesn't exist, enabling better error messages and tool suggestions.

Closes #107

Motivation

Currently when the LLM calls a non-existent tool, the agent returns a generic "Tool 'x' not found" message with no additional context. Plugins have no way to intercept this to provide fuzzy matching, dynamic tool loading, or better diagnostics.

Changes

  • connectonion/core/events.py — New on_tool_not_found() wrapper function (supports decorator + multi-arg syntax)
  • connectonion/core/agent.py — Register on_tool_not_found in event registry; add _invoke_events_with_return() method that collects handler return values
  • connectonion/core/tool_executor.py — Store pending_tool in session before event fires; invoke on_tool_not_found via _invoke_events_with_return(); use handler's return value as error message if provided
  • connectonion/core/__init__.py — Export on_tool_not_found
  • connectonion/__init__.py — Export on_tool_not_found
  • CLAUDE.md — Updated events list
  • docs/concepts/events.md — Added table row, lifecycle diagram entry, and usage example
  • tests/unit/test_tool_not_found.py — 15 new tests
  • tests/unit/test_events.py — Added on_tool_not_found to wrapper type assertion
  • tests/unit/test_tool_executor.py — Added _invoke_events_with_return stub to FakeAgent
  • tests/unit/test_tool_executor_errors.py — Added _invoke_events_with_return stub to mock agents

API

from connectonion import Agent, on_tool_not_found

@on_tool_not_found
def suggest_tool(agent) -> str:
    pending = agent.current_session['pending_tool']
    tool_name = pending['name']
    available = list(agent.tools._tools.keys())

    similar = [t for t in available if t.startswith(tool_name[:3])]
    if similar:
        return f"Tool '{tool_name}' not found. Did you mean: {', '.join(similar)}?"
    return f"Tool '{tool_name}' not found. Available: {available}"

agent = Agent('my_agent', tools=[...], on_events=[suggest_tool])

Handler returns a string to override the default error message. Return None (or nothing) to keep the default "Tool 'x' not found" behavior.

Tests

All 83 relevant tests pass (15 new + 68 existing). Zero regressions.

tests/unit/test_tool_not_found.py: 15 passed
tests/unit/test_events.py: 39 passed
tests/unit/test_tool_executor.py: 10 passed
tests/unit/test_tool_executor_errors.py: 19 passed

Edge cases tested:

  • Fires when tool is missing, does not fire for existing tools
  • Handler return value overrides default message
  • Handler returning None uses default message
  • First non-None return wins across multiple handlers
  • pending_tool set in session before handler fires, cleared after
  • Trace status is not_found
  • Decorator and wrapper syntax both work
  • Multi-arg wrapper returns list
  • Real Agent registers event correctly
  • _invoke_events_with_return returns first non-None result

Add event that fires when LLM requests a tool that doesn't exist,
enabling custom error messages, fuzzy matching suggestions, and
tool usage analytics.
@esreekarreddy esreekarreddy force-pushed the feature/on-tool-not-found branch from 6f413da to fa33a07 Compare March 13, 2026 01:09
@esreekarreddy
Copy link
Copy Markdown
Contributor Author

Rebased onto upstream main (15 new commits merged, including PR-116 multimodal input support). No conflicts — upstream changes were in unrelated files (network, tool_approval refactor, file upload). All 83 tests still pass.

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.

Add on_tool_not_found event

1 participant