Skip to content

Implemented: AI plugin — LangChain4j integration for AI/LLM capabilities in OFBiz services (OFBIZ-13408)#244

Open
patelanil wants to merge 16 commits into
apache:trunkfrom
patelanil:feature/ai-plugin
Open

Implemented: AI plugin — LangChain4j integration for AI/LLM capabilities in OFBiz services (OFBIZ-13408)#244
patelanil wants to merge 16 commits into
apache:trunkfrom
patelanil:feature/ai-plugin

Conversation

@patelanil
Copy link
Copy Markdown
Contributor

Adds a new optional plugin (plugins/ai) that integrates LangChain4j 1.8.0 to bring
native AI/LLM capabilities to OFBiz services, Groovy scripts, and screen actions.

The plugin follows established OFBiz patterns throughout:

  • AiContainer implements the Container interface for lifecycle management,
    following the same pattern as BirtContainer in the birt plugin
  • AiFactory holds the singleton ChatModel instance, parallel to BirtFactory
  • AiWorker provides static generate() and generateStructured() utility methods,
    parallel to BirtWorker
  • AiServices exposes two standard OFBiz services: ai.generate and ai.generateStructured
  • Configuration via config/ai.properties, read with UtilProperties at startup

The design is provider-agnostic — a switch on ai.provider selects the LangChain4j
builder. The initial implementation supports OpenAI and any OpenAI-compatible endpoint
(Ollama, Groq, Together AI, Azure OpenAI) via ai.baseUrl. Additional providers
(Anthropic, Bedrock) can be added by extending the switch in AiContainer.java.

Dependencies added (both Apache 2.0):

  • dev.langchain4j:langchain4j:1.8.0
  • dev.langchain4j:langchain4j-open-ai:1.8.0

Smoke test verified end to end: ai.smokeTest service returned a live response
from OpenAI gpt-4o-mini.

patelanil added 13 commits May 8, 2026 20:33
- ofbiz-component.xml: registers plugin, container, service resource
- build.gradle: LangChain4j 1.8.0 dependencies (Apache 2.0)
- servicedef/services.xml: empty stub (populated in Step 6)
- config/ai.properties: gitignored, template only

Plugin compiles cleanly into root OFBiz jar.
Note: ai.properties is gitignored — not committed.

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
- Implements Container interface following BirtContainer pattern
- init() stores name and configFile only
- start() reads provider-agnostic ai.properties:
  ai.provider, ai.model, ai.apiKey, ai.baseUrl, ai.timeout
- Validates apiKey — fails fast with clear error if not configured
- Provider switch builds ChatModel interface (not OpenAiChatModel)
- openai case covers OpenAI, Groq, Ollama, Azure via baseUrl
- Additional providers (anthropic, bedrock) can be added in switch
- Stores singleton via AiFactory.setChatModel() (Step 3)
- stop() calls AiFactory.destroy()
- Note: ai.properties is gitignored — not committed

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
- Static factory parallel to BirtFactory pattern
- setChatModel(ChatModel) — called by AiContainer.start()
- getChatModel() — throws IllegalStateException if not initialized
- destroy() — called by AiContainer.stop()
- Compiles cleanly with AiContainer

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
- generate(dctx, messages) → String
- generateStructured(dctx, messages, schema) → Map<String,Object>
- TYPE_BUILDERS map pattern — no switch, extensible
- JSON Schema vocabulary: string, number, integer, boolean, array, object
- toChatMessages() converts List<Map> to LangChain4j ChatMessage list
- buildJsonObjectSchema() + buildSchemaElement() for schema conversion
- ResponseFormatType.JSON (JSON_SCHEMA does not exist in LangChain4j 1.8.0)
- Jackson ObjectMapper for JSON response parsing
- dctx parameter present for future use (audit logging, delegator)
- GeneralException wraps all failures with clear message

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
- generate(dctx, context) → calls AiWorker.generate, returns response
- generateStructured(dctx, context) → calls AiWorker.generateStructured,
  returns result Map
- ServiceUtil.returnSuccess/returnError pattern
- UtilGenerics.cast() for unchecked context parameter casts
- Pure delegation — no LangChain4j imports

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
- ai.generate: messages (List IN) → response (String OUT)
- ai.generateStructured: messages (List IN) + schema (Map IN) → result (Map OUT)
- configName optional IN on both services (reserved for future use)
- engine=java, location=org.apache.ofbiz.ai.AiServices

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
- AiTest.groovy: calls AiWorker.generate with test message
- ai.smokeTest service registered in services.xml
- Verified end to end: response 'Hello!' received from OpenAI
- Full stack confirmed: AiContainer → AiFactory → AiWorker → LangChain4j → OpenAI

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
- What the plugin does and JIRA reference OFBIZ-13408
- Architecture table: AiContainer, AiFactory, AiWorker, AiServices
- Installation and configuration instructions
- Multiple provider support via ai.baseUrl
- Usage examples: generate() and generateStructured()
- Available services table with IN/OUT params
- Smoke test instructions
- Guide for adding new providers

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
- ai.generate → aiGenerate
- ai.generateStructured → aiGenerateStructured
- ai.smokeTest → aiSmokeTest

Dot notation is not OFBiz convention for service names.
Updated services.xml and README.md.

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
- AiStructuredTest.groovy: calls AiWorker.generateStructured with
  simple schema [word: 'string']
- aiSmokeTestStructured service registered in services.xml
- Verified end to end: response [word:Helloreceived from OpenAI
- Validates key presence in response Map

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
- AiTest.groovy: add final String MODULE = 'AiTest.groovy'
- AiStructuredTest.groovy: add final String MODULE = 'AiStructuredTest.groovy'
- Replace all Debug.log string literal module arguments with MODULE
- Follows OFBiz Groovy script convention (ArtifactInfo.groovy pattern)

Ref: patelanil/ofbiz-dev#1
OFBIZ-13408
@patelanil
Copy link
Copy Markdown
Contributor Author

Fixed the SonarCloud violation — replaced the 'AiStructuredTest' string
literal with a final String MODULE constant following the OFBiz Groovy
script convention (same pattern as ArtifactInfo.groovy).

Also applied the same fix to AiTest.groovy proactively.

patelanil added 3 commits May 16, 2026 14:24
Add langchain4j-anthropic:1.8.0 and langchain4j-ollama:1.8.0 to build.gradle.

Refactor AiContainer to support openai, anthropic, and ollama via ai.properties
config — replaces single-provider switch block with buildChatModel() if/else-if.
AiContainer now holds the static ChatModel field and exposes getChatModel(),
following the ServiceContainer pattern. AiFactory is deleted.

Refactor AiWorker to reference AiContainer directly. Align both generate() and
generateStructured() to fetch chatModel before the try block with consistent null
guards — fixes a pre-existing NPE risk in generateStructured().
Both files are safe to track — ai.properties now uses placeholder values
only, and CLAUDE.md contains no credentials.
Document all three supported providers (openai, anthropic, ollama) with
inline comments, model examples, and placeholder API key. Follows OFBiz
convention of committing properties files with placeholder values.
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

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.

1 participant