Skip to content

CAMEL-24322: Add tool-calling support via AiToolRegistry - #25289

Open
gnodet wants to merge 2 commits into
mainfrom
fix/CAMEL-24322
Open

CAMEL-24322: Add tool-calling support via AiToolRegistry#25289
gnodet wants to merge 2 commits into
mainfrom
fix/CAMEL-24322

Conversation

@gnodet

@gnodet gnodet commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Claude Code on behalf of gnodet

Extends the camel-openai component to discover and execute Camel route tools registered via the shared AiToolRegistry, alongside existing MCP tools. This implements Step 6 of the unified AI tool abstraction design (CAMEL-23382).

Route authors can now expose Camel routes as AI tools via ai-tool: consumer endpoints and have them automatically available to OpenAI models during function-calling loops — no MCP server required.

Changes

  • Dependency: Add camel-ai-tool compile dependency to camel-openai
  • Configuration: Add tags parameter to OpenAIConfiguration for filtering tools by tag from the shared AiToolRegistry
  • Converter: Create AiToolSpecToOpenAI that transforms AiToolSpec into OpenAI ChatCompletionFunctionTool using parametersJsonSchema (follows the same pattern as McpToolConverter)
  • OpenAIProducer: Extend the agentic loop to discover Camel route tools via AiToolRegistry.getOrCreate(context), convert them to OpenAI function tools, and dispatch via AiToolExecutor.execute() with exchange isolation (ExchangeHelper.createCopy)
  • OpenAIToolExecutionProducer: Same extension for manual tool-loop routes
  • Error handling: Both producers check Camel route tools first, then fall back to MCP; hallucinated tool name and tool execution error strategies apply to both sources
  • Generated files: Updated catalog JSON, endpoint DSL factory, configurer, and URI factory
  • Tests: 8 unit tests in AiToolSpecToOpenAITest covering full spec conversion, no parameters, no description, default type, required arrays, empty schema, invalid schema, and additionalProperties:false

Usage Example

// Register a route as an AI tool
from("ai-tool:getWeather?description=Get current weather&tags=weather"
    + "&parameters.city=string&parameters.city.description=City name&parameters.city.required=true")
    .process(exchange -> {
        String city = exchange.getIn().getHeader("city", String.class);
        exchange.getIn().setBody("Sunny, 22°C in " + city);
    });

// OpenAI will auto-discover and call it
from("direct:chat")
    .to("openai:chat-completion?model=gpt-4o&tags=weather&autoToolExecution=true");

Test Plan

  • All 163 existing unit tests pass (including updated OpenAIToolErrorStrategyTest)
  • 8 new AiToolSpecToOpenAITest tests pass
  • Code formatted with mvn formatter:format impsort:sort
  • Generated files regenerated and committed (catalog JSON, endpoint DSL factory)
  • CI build passes

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

Extend the camel-openai component to discover and execute Camel route
tools registered via the shared AiToolRegistry, alongside existing MCP
tools. This implements Step 6 of the unified AI tool abstraction design
(CAMEL-23382).

Changes:
- Add camel-ai-tool compile dependency to camel-openai
- Add 'tags' configuration parameter to OpenAIConfiguration for
  filtering tools by tag from the shared AiToolRegistry
- Create AiToolSpecToOpenAI converter that transforms AiToolSpec
  into OpenAI ChatCompletionFunctionTool using parametersJsonSchema
- Extend OpenAIProducer to discover Camel route tools and dispatch
  them via AiToolExecutor in the agentic loop, with exchange isolation
- Extend OpenAIToolExecutionProducer similarly for manual tool loops
- Add AiToolSpecToOpenAITest with 8 test cases covering full spec,
  no params, no description, default type, required arrays, empty
  schema, invalid schema, and additionalProperties:false
- Update error messages to reference generic "tool source" instead
  of MCP-specific wording

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@gnodet
gnodet marked this pull request as ready for review August 3, 2026 22:08
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well-structured integration of AiToolRegistry into the OpenAI component with good converter test coverage and correct backward compatibility. Five observations — two about code quality, three about documentation/security polish.

Additional observations (on lines not in the diff):

  1. Stale descriptions in OpenAIConfiguration.java: toolExecutionErrorStrategy (line 219) says "Strategy for handling exceptions thrown during MCP tool execution" but now also governs Camel route tool errors. Similarly, hallucinatedToolNameStrategy (line 230) says "tool not found in any MCP server" — the runtime error message was correctly updated to "tool source" but the annotations weren't. Both descriptions and the catalog JSON should be updated.

  2. Stale class Javadoc in OpenAIToolExecutionProducer.java (line 48): Still says "Producer that executes MCP tool calls" — should be updated to reflect that this now handles both MCP and Camel route tools.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of @gnodet


int maxIterations = config.getMaxToolIterations();

Set<String> availableToolNames = new java.util.LinkedHashSet<>();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

FQCN violation: new java.util.LinkedHashSet<>() should use the simple class name with an import. The project convention states: "Do NOT use fully qualified class names in Java code." Note that OpenAIToolExecutionProducer in this same PR correctly imports java.util.LinkedHashSet.

Suggested change
Set<String> availableToolNames = new java.util.LinkedHashSet<>();
Set<String> availableToolNames = new LinkedHashSet<>();

(Also add import java.util.LinkedHashSet; alongside the existing import java.util.LinkedHashMap;)

@@ -633,6 +671,89 @@ private void processNonStreamingAgentic(
"Max tool iterations (%d) exceeded. Tools called: %s".formatted(maxIterations, toolCallsLog));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Code duplication: discoverCamelRouteTools(), executeCamelRouteTool(), and the result-handling logic are duplicated nearly verbatim between OpenAIProducer and OpenAIToolExecutionProducer (~80 lines each). The duplication is slightly inconsistent — this class extracts result handling into handleCamelToolResult(), while OpenAIToolExecutionProducer inlines the same logic.

Consider extracting the shared code into a package-private helper class (similar to how AiToolSpecToOpenAI is already a shared utility).

throw e;
}
LOG.warn("Camel route tool '{}' execution failed: {}", spec.getName(), e.getMessage(), e);
return "Error: Tool execution failed: " + e.getMessage();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Security nit: The AiToolResult Javadoc warns: "Framework adapters MUST NOT return [ExecutionError.message()] verbatim to the LLM without sanitization." The handleCamelToolResult() method correctly returns the generic "Error: Tool execution failed" for ExecutionError, but this outer catch returns e.getMessage() which could include internal details. Consider using the same sanitized message:

Suggested change
return "Error: Tool execution failed: " + e.getMessage();
return "Error: Tool execution failed";

The same pattern applies to OpenAIToolExecutionProducer at line 291.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant