CAMEL-24288: Add IBM watsonx.ai support for TUI/CLI AI prompt (F8) - #25208
Conversation
Add watsonx as a new LlmClient.ApiType that uses the IBM watsonx.ai model gateway (OpenAI-compatible). The implementation: - Adds ApiType.watsonx with auto-detection via WATSONX_APIKEY and WATSONX_URL environment variables - Reuses the OpenAI wire format (generate, chatWithTools) since the watsonx model gateway is OpenAI-API-compatible - Adds watsonx-specific URL normalization for the /ml/gateway/v1 path - Defaults to ibm/granite-3.1-8b-instruct model and us-south region - Adds watsonx to the TUI AiProviderSelector and switch popup - Updates CLI help text in Ask and Explain commands - Includes unit tests for URL normalization and provider selection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
🌟 Thank you for your contribution to the Apache Camel project! 🌟 🐫 Apache Camel Committers, please review the following items:
|
gnodet
left a comment
There was a problem hiding this comment.
Review: Looks good ✅
Clean, well-structured PR that adds IBM watsonx.ai as a new AI provider following existing patterns consistently. Tests are adequate (10 new URL normalization tests + updated provider selector tests), documentation is updated, and credentials handling is correct (Bearer auth via WATSONX_APIKEY env var). The design decision to reuse the OpenAI wire format is well-justified since the watsonx model gateway is OpenAI-API-compatible.
Minor observations (non-blocking)
-
listWatsonxModels()auth header construction (LlmClient.java~L422): Manually constructs auth headers instead of reusingbuildOpenAiAuthHeaders(resolveApiKey())which produces the same result.listOpenAiModels()at L397 uses the shared helper. Minor inconsistency, functionally correct. -
JUnit vs AssertJ in
AiProviderSelectorTest: The new assertions use JUnit-style (assertEquals,assertTrue) rather than AssertJ. This matches the existing style in this test file, but the newLlmClientWatsonxTestcorrectly uses AssertJ — nice consistency there. -
Harden.javahas its ownApiTypeenum (with onlyollamaandopenai) — pre-existing limitation, not a regression from this PR, but worth tracking for future unification withLlmClient.ApiType. -
Good design decisions throughout: first-class
ApiType.watsonx(proper TUI visibility),isGenericPlaceholderModel()for clean provider switching, and proper Bearer auth flowing throughapplyOpenAiAuthorization().
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
|
🧪 CI tested the following changed modules:
🔬 Scalpel shadow comparison — Scalpel: 8 tested, 8 compile-only — current: 6 all testedMaveniverse Scalpel detected 16 affected modules (current approach: 6).
|
Address review feedback: use the shared helper instead of manually constructing Bearer auth headers, consistent with listOpenAiModels(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for the review! Addressed the feedback:
The other two observations are acknowledged:
Claude Code on behalf of Guillaume Nodet |
Update DEFAULT_WATSONX_MODEL from ibm/granite-3.1-8b-instruct to ibm/granite-4-1-8b-instruct — the latest Granite generation available on watsonx.ai with improved tool calling and instruction following. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| } | ||
| apiType = ApiType.watsonx; | ||
| apiKey = key; | ||
| String endpoint = System.getenv("WATSONX_URL"); |
There was a problem hiding this comment.
This env var is not documented.
gnodet
left a comment
There was a problem hiding this comment.
The two follow-up commits cleanly address prior feedback (Granite 4.1 default, buildOpenAiAuthHeaders reuse). CI is green. One unresolved item remains:
Unresolved review feedback: @ammachado noted that WATSONX_URL (read in tryWatsonx() to allow endpoint override for non-US-South IBM Cloud regions like eu-de or jp-tok) is not documented anywhere — not in the --url option description, not in the .adoc docs, not in the Explain.java help text, and not in the AiPanel error message. By contrast, WATSONX_APIKEY is documented in all of those locations. Adding WATSONX_URL to the help text would aid discoverability.
For example, Explain.java line 202 could be updated:
printer().printErr(" 5. camel explain my-route.yaml --api-type=watsonx (uses WATSONX_APIKEY, WATSONX_URL)");
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
Add WATSONX_URL (and AZURE_OPENAI_ENDPOINT) to the --url option description in Ask and Explain commands so users know the endpoint can be configured via environment variable without --url flag. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gnodet
left a comment
There was a problem hiding this comment.
Review: Looks good ✅
Re-reviewed after new commits. The previous finding (undocumented WATSONX_URL env var) has been fully addressed in commit 81c1b20 — WATSONX_URL is now documented in the --url option description in Ask.java, Explain.java, both .adoc pages, the generated metadata JSON, and the Explain.java help text example.
The three follow-up commits cleanly address all prior review feedback:
buildOpenAiAuthHeadersreuse- Granite 4.1 default model upgrade
WATSONX_URLdocumentation
Code quality is good: 10 new unit tests using AssertJ, updated AiProviderSelectorTest with watsonx de-duplication test, consistent Bearer auth flow via applyOpenAiAuthorization, and proper URL normalization for the watsonx model gateway. CI is fully green.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
Summary
Add IBM watsonx.ai as a first-class AI provider for the Camel JBang TUI/CLI AI prompt feature (F8 key), following the patterns established by Azure OpenAI (CAMEL-24276) and Gemini/Vertex AI (CAMEL-24275).
Changes
LlmClient.java: AddwatsonxtoApiTypeenum, auto-detection viaWATSONX_APIKEYenv var, URL normalization for the watsonx model gateway (/ml/gateway/v1/chat/completions), Bearer token auth, default modelibm/granite-4-1-8b-instruct, default URLhttps://us-south.ml.cloud.ibm.comAiProviderSelector.java: Add watsonx to the TUI provider switch popupAiPanel.java: IncludeWATSONX_APIKEYin the error message hintAsk.java/Explain.java: Update CLI help text to document watsonx option and env varLlmClientWatsonxTest.java(new): 10 unit tests for URL normalization (chat/models paths, trailing slashes, regions, full URL preservation) and OpenAI-format delegationAiProviderSelectorTest.java: Updated existing tests + new watsonx de-duplication testDesign decisions
ApiType.watsonx(not piggybacked onopenai): gives watsonx first-class visibility in the TUI provider selector, since watsonx URLs don't look like OpenAI URLsgenerateOpenAi()/chatOpenAiFormat()are reused — no new HTTP logicWATSONX_APIKEY(notWATSONX_API_KEY): follows IBM's documented naming convention, consistent with the existingcamel-ibm-watsonx-aicomponentResolves: CAMEL-24288
Claude Code on behalf of Guillaume Nodet