Skip to content

feat(dotAI): Dot AI LangChain4J - OpenRouter#36136

Open
ihoffmann-dot wants to merge 5 commits into
mainfrom
dot-ai-langchain-openrouter
Open

feat(dotAI): Dot AI LangChain4J - OpenRouter#36136
ihoffmann-dot wants to merge 5 commits into
mainfrom
dot-ai-langchain-openrouter

Conversation

@ihoffmann-dot

Copy link
Copy Markdown
Member

Summary

Adds OpenRouter as a supported provider. OpenRouter aggregates hundreds of models
(OpenAI, Anthropic, Meta, Mistral, DeepSeek, etc.) behind a single OpenAI-compatible
API and one API key — the strategy reuses the LangChain4J OpenAI model classes
pointed at the OpenRouter base URL.

  • Add OpenRouterModelProviderStrategy registered in LangChain4jModelFactory.STRATEGIES
  • No new dependency — reuses langchain4j-open-ai with base URL https://openrouter.ai/api/v1
  • endpoint config field overrides the base URL (proxies/gateways)
  • Chat and streaming chat supported; embeddings and image throw UnsupportedOperationException (OpenRouter has no such endpoints)
  • Add 7 unit tests in LangChain4jModelFactoryTest

Configuration

{
  "chat": {
    "provider": "openrouter",
    "model": "openai/gpt-4o",
    "apiKey": "sk-or-...",
    "maxTokens": 4096,
    "temperature": 0.7
  }
}

Notes

  • Model IDs use OpenRouter's namespaced form: openai/gpt-4o, anthropic/claude-sonnet-4, deepseek/deepseek-r1.
  • maxRetries is not supported by the streaming chat model and is ignored with a warning (same behavior as the OpenAI provider).

Related Issue

This PR fixes #36133
EPIC: dotAI Multi-Provider Support #33970

@claude

claude Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Claude finished @wezell's task in 1m 0s —— View job


Rollback Safety Analysis

  • Read rollback-unsafe categories reference
  • Get full PR diff
  • Analyze diff against all unsafe categories
  • Apply appropriate label

Result: ✅ Safe to Rollback

The changes in this PR have been analyzed against all rollback-unsafe categories:

Changed files:

  • LangChain4jModelFactory.java — Adds OpenRouterModelProviderStrategy to the strategy list and updates documentation comments
  • OpenRouterModelProviderStrategy.java (new file) — New strategy class that wraps LangChain4J's OpenAI client pointed at the OpenRouter base URL
  • ProviderConfig.java — Documentation-only update (Javadoc additions)
  • LangChain4jModelFactoryTest.java — New unit tests for the OpenRouter strategy

Category-by-category assessment:

Category Match? Reason
C-1 Structural Data Model Change ✅ None No database changes; no runonce tasks
C-2 ES Mapping Change ✅ None No Elasticsearch mapping touched
C-3 Content JSON Model Version Bump ✅ None No ImmutableContentlet or CURRENT_MODEL_VERSION changes
C-4 DROP TABLE / DROP COLUMN ✅ None No DDL statements anywhere
H-1 One-Way Data Migration ✅ None No data transformation tasks
H-2 RENAME TABLE / COLUMN ✅ None No renames
H-3 PK Restructuring ✅ None No PK changes
H-4 New ContentType Field Type ✅ None Not a Field subtype — this is an AI model provider
H-5 Storage Provider Change ✅ None No binary storage changes
H-6 DROP PROCEDURE / FUNCTION ✅ None No stored procedure changes
H-7 NOT NULL column without default ✅ None No DDL
H-8 VTL Viewtool Contract Change ✅ None No ViewTool implementations modified
M-1 Column Type Change ✅ None No DDL
M-2 Push Publishing Bundle Format ✅ None No bundle XML changes
M-3 REST / GraphQL API Contract Change ✅ None No REST endpoint changes
M-4 OSGi Interface Change ✅ None No public OSGi interfaces modified

This PR adds a pure application-layer feature: a new ModelProviderStrategy implementation that routes AI model requests to OpenRouter via the existing LangChain4J OpenAI client. No database, Elasticsearch, REST API, or storage changes are involved. Rolling back to N-1 simply removes the OpenRouter option from the AI provider list — N-1 starts and operates normally.

Label applied: AI: Safe To Rollback

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — qwen.qwen3-next-80b-a3b

New Issues

  • 🔴 Critical: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:85baseUrl() uses config.endpoint().isBlank() but ProviderConfig.endpoint() is not defined in the config schema; this silently defaults to DEFAULT_BASE_URL if endpoint is null/missing, bypassing validation and potentially exposing unconfigured external endpoints without user intent.
  • 🟠 High: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:49maxRetries is logged as ignored in streaming model but not enforced — caller may assume retry logic is active, leading to unhandled transient failures in production.
  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:85 — Assumption: ProviderConfig.endpoint() is a valid field in the config schema. What to verify: Confirm ProviderConfig has endpoint() getter and it's populated from config (e.g., via @JsonProperty or @ConfigProperty). If missing, this is a critical config bypass.
  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:25OpenAiChatModel.builder() is used directly; no validation that baseUrl is a valid HTTP URL or that apiKey is not a public key (e.g., sk- prefix check). This could allow injection via malformed URLs or accidental public key exposure.

Existing

  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/ProviderConfig.java:53 — Prior finding still present: apiKey is passed directly to LangChain4J without masking or audit logging — sensitive key may be logged in error paths or debug logs.

Resolved

  • dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/LangChain4jModelFactory.java:23 — Added OpenRouterModelProviderStrategy to STRATEGIES — resolves prior absence of OpenRouter support.
  • dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/LangChain4jModelFactory.java:14 — Updated supported providers list to include openrouter — aligns with implementation.

Run: #28259831651 · tokens: in: 4173 · out: 795 · total: 4968

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — qwen.qwen3-next-80b-a3b

New Issues

  • 🔴 Critical: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:85baseUrl() uses config.endpoint().isBlank() but ProviderConfig.endpoint() is not validated as a valid HTTP URL; allows arbitrary injection of malformed or malicious endpoints without validation, risking SSRF or API hijacking
  • 🟠 High: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:49maxRetries is logged as ignored in streaming model but not enforced; unhandled transient failures may cause silent request loss without retry fallback
  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:85 — Assumption: ProviderConfig.endpoint() is a valid field in the config schema. What to verify: Confirm ProviderConfig has endpoint() getter and is populated from config (diff shows doc update but no code change to ProviderConfig class definition)
  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:25OpenAiChatModel.builder() is used directly without validating baseUrl is a valid HTTP URL or apiKey is not a public key; risks injection or exposure if config is tampered

Existing

  • 🔴 Critical: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:85 — baseUrl() uses config.endpoint().isBlank() but endpoint field may not be defined in ProviderConfig, silently defaulting to openrouter.ai and bypassing config validation
  • 🟠 High: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:49 — maxRetries is logged as ignored in streaming model but not enforced, risking unhandled transient failures
  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:85 — Assumption: ProviderConfig.endpoint() is a valid field in the config schema. What to verify: Confirm ProviderConfig has endpoint() getter and is populated from config
  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:25 — OpenAiChatModel.builder() is used directly without validating baseUrl is a valid HTTP URL or apiKey is not a public key, risking injection or exposure

Resolved

  • dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/LangChain4jModelFactory.java:23 — Added OpenRouterModelProviderStrategy to STRATEGIES array — resolves prior omission
  • dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/ProviderConfig.java:18 — Updated config doc to include openrouter and endpoint — resolves prior documentation gap

Run: #28260153579 · tokens: in: 4696 · out: 1198 · total: 5894

@ihoffmann-dot ihoffmann-dot marked this pull request as ready for review June 26, 2026 19:36
.apiKey(config.apiKey())
.modelName(config.model())
.baseUrl(baseUrl(config));
if (config.temperature() != null) builder.temperature(config.temperature());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

one single line if and no { } 🙄

@fabrizzio-dotCMS fabrizzio-dotCMS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

just one minor nit

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🤖 dotBot Review (Bedrock)

Reviewed 4 file(s); 4 candidate(s) → 3 confirmed, 0 uncertain (unverified, kept for review).

Confirmed findings

  • 🟡 Medium dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/LangChain4jModelFactory.java:26 — Missing GoogleAIModelProviderStrategy in STRATEGIES list
    The STRATEGIES list in LangChain4jModelFactory.java (line 26) includes OpenRouter, OpenAI, Azure, and AWS Bedrock but omits GoogleAIModelProviderStrategy. A grep search found GoogleAIModelProviderStrategy.java exists, indicating it's a valid strategy that should be registered here to maintain Google AI support as per documentation.
  • 🟡 Medium dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/LangChain4jModelFactory.java:17 — Vertex AI documentation inaccuracy
    The code comment states Vertex AI is 'chat-only', but the VertexAIProviderStrategy allows 'chat' and 'streamingChat' without enforcing this restriction. The actual Vertex AI client may support other operations, leading to potential UnsupportedOperationExceptions if used beyond chat, which isn't clearly documented or handled.
  • 🟡 Medium dotCMS/src/main/java/com/dotcms/ai/client/langchain4j/OpenRouterModelProviderStrategy.java:39 — Missing validation for modelType format in OpenRouterModelProviderStrategy
    The methods buildChatModel and buildStreamingChatModel use modelType directly without validating its format. OpenRouter requires namespaced model IDs (e.g., 'openai/gpt-4o'), but the current implementation does not check if modelType adheres to this format. This could lead to runtime errors when invalid model names are provided, as the API call would fail. Adding validation ensures that only properly formatted model IDs are used.

us.deepseek.r1-v1:0 · Run: #28272291702 · tokens: in: 20640 · out: 6449 · total: 27089 · calls: 8 · est. ~$0.063

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

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[FEATURE] dotAI: LangChain4J integration — Phase 2 (OpenRouter)

3 participants