You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ofbiz.apache.org by "Anil K Patel (Jira)" <ji...@apache.org> on 2026/06/02 10:36:00 UTC

[jira] [Updated] (OFBIZ-13408) New Plugin: ai - Agent Framework for LLM Integration

     [ https://issues.apache.org/jira/browse/OFBIZ-13408?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Anil K Patel updated OFBIZ-13408:
---------------------------------
    Description: 
h2. Summary

New optional plugin ({{plugins/ai}}) that brings native AI agent capabilities to OFBiz — callable from any service, screen action, or scheduled job. No external AI SDK required.

h2. Motivation

AI/LLM capabilities are increasingly essential in enterprise applications. OFBiz currently has no built-in way to call AI providers from services. Developers who need AI in their OFBiz implementations must build this infrastructure themselves, each time, without consistency.

This plugin provides a reusable, convention-following integration that any OFBiz deployment can install without modifying framework code.

h2. Design

The plugin follows established OFBiz patterns throughout. The original design used LangChain4j; after initial smoke testing, LangChain4j was removed. Its functionality required fewer than 300 lines of direct HTTP code and would have added 15+ MB of transitive dependencies to the OFBiz runtime. The plugin now uses {{java.net.http.HttpClient}} and Jackson — both already present in the OFBiz runtime. No external AI SDK.

h3. Lifecycle — Container interface

Implements the Container interface for lifecycle management, same pattern as BirtContainer in the birt plugin. Registered in ofbiz-component.xml:

{code}
<container name="ai-container" loaders="main"
           class="org.apache.ofbiz.ai.container.AiContainer"/>
{code}

AiContainer.start() loads all provider, tool, and agent registries from XML files across all components. AiContainer.stop() cleanly shuts down.

h3. Configuration — ai.properties

Named provider blocks via config/ai.properties, read with UtilProperties:

{code}
ai.provider.openai-default.baseUrl=https://api.openai.com/v1
ai.provider.openai-default.apiKey=...
ai.provider.openai-default.model=gpt-4o
ai.provider.openai-default.timeout=60

ai.provider.anthropic-default.baseUrl=https://api.anthropic.com/v1
ai.provider.anthropic-default.apiKey=...
ai.provider.anthropic-default.model=claude-sonnet-4-5
ai.provider.anthropic-default.timeout=60
ai.provider.anthropic-default.extraHeaders=anthropic-version:2023-06-01

ai.provider.ollama-default.baseUrl=http://localhost:11434/v1
ai.provider.ollama-default.model=llama3.2
{code}

Any OpenAI-compatible endpoint is supported. Provider selection is configuration-only — no code change required.

h3. Declarative agents and tools — XML

Developers declare tools (OFBiz services) and agents in XML files placed in any component’s {{ai/}} directory. ToolCatalog and AgentRegistry scan all components at startup.

*tools.xml* — exposes any OFBiz service as an AI tool:
{code:xml}
<tools>
  <tool name="getMrpRecommendation"
        service-name="getMrpRecommendation"
        description="Gets MRP recommendations for a product facility"
        required-permission="MANUFACTURING_VIEW"
        requires-approval="false"/>
</tools>
{code}

*agent.xml* — declares an agent with provider, system prompt, and tool allow-list:
{code:xml}
<agents>
  <agent name="mrp-advisor" provider="openai-default" max-iterations="6">
    <system-prompt>You are an MRP advisor for a manufacturing company...</system-prompt>
    <tools>
      <tool name="getMrpRecommendation"/>
    </tools>
  </agent>
</agents>
{code}

Adding a new tool or agent requires only XML — no Java changes.

h3. Services

Single service entry point:
{code}
agentRun(agentName, userMessage, userLogin)
  optional IN:  threadId, approvalRequired
  OUT: assistantMessage, stopReason, iterationsUsed, threadId, proposalId
{code}

Supporting services:
{code}
getUsageSummary(agentName?, userLoginId?, fromDate?, thruDate?)
  OUT: totalRuns, totalInputTokens, totalOutputTokens, estimatedCostUsd

getConversationHistory(threadId) → ordered message list
archiveConversationThread(threadId)
approveAgentProposal(proposalId) → resumes agent loop, returns final answer
rejectAgentProposal(proposalId, rejectionReason) → LLM acknowledgment
{code}

h3. Database entities

Persistent audit trail for every run:

|| Entity || Purpose ||
| AiAgentRun | Every agentRun invocation — agent, user, tokens, status, messages |
| AiAgentToolCall | Every tool call within a run — arguments, result, timing |
| AiProviderCost | Cost-per-million-token rates by model for cost estimation |
| AiConversationThread | Named multi-turn conversation threads |
| AiConversationMessage | Individual messages within a thread (ordered by sequenceNum) |
| AiAgentProposal | Suspended runs awaiting human approval — full conversation state |
| AiAgentProposalTool | Individual tool calls within a pending proposal |

h3. Security

Tool execution is gated by the OFBiz permission model. Each tool declares a {{required-permission}} in its XML. Calls without sufficient permission fail gracefully — the LLM receives an error and continues reasoning.

Tools can declare {{requires-approval="true"}} for irreversible actions. The agent suspends mid-run, persists the full conversation state as an AiAgentProposal, and returns a {{proposalId}}. A human calls {{approveAgentProposal}} (which executes the tools and resumes the loop) or {{rejectAgentProposal}} (which returns a graceful LLM acknowledgment).

h3. Admin webapp at /ai

Full OFBiz web application mounted at {{/ai}} with six screens:

* *Agent List* — all registered agents; launch an interactive agent run
* *Run History* — paginated AiAgentRun list with filter by agent/user/status/date
* *Run Detail* — single run view with embedded tool-call list
* *Usage Dashboard* — aggregated token and estimated cost summary with filter
* *Proposal Review* — pending proposals with one-click Approve or Reject
* *Thread Explorer* — conversation thread list; message history view; archive action

Permission model: {{OFBTOOLS _VIEW}} for read; {{OFBTOOLS _UPDATE}} for approve/reject.

h3. Testing

{{testdef/AgentRunnerTest.java}} uses {{MockAiChatClient}} to test the agent loop, tool dispatch, result truncation, and max-iterations stop — no network, no API key required.

h2. What this does NOT do

- Does not modify any framework code
- Does not affect OFBiz if plugin is not installed
- Does not add any required dependencies to the framework
- Does not hardcode any specific AI provider

h2. Dependencies

None. Uses only {{java.net.http.HttpClient}} and Jackson, both already present in the OFBiz runtime.

h2. Plugin structure

{code}
plugins/ai/
├── ofbiz-component.xml
├── build.gradle
├── config/ai.properties + AiUiLabels.xml
├── servicedef/services.xml
├── entitydef/AiAgentEntities.xml
├── data/AiAgentSeedData.xml
├── ai/sample.tools.xml + sample.agent.xml
├── src/main/java/org/apache/ofbiz/ai/
│   ├── container/AiContainer.java
│   ├── agent/ProviderRegistry.java, ProviderConfig.java
│   ├── agent/ToolDescriptor.java, ToolCatalog.java
│   ├── agent/AgentDefinition.java, AgentRegistry.java
│   ├── agent/AiChatClient.java, AiHttpClient.java, MockAiChatClient.java
│   ├── agent/AgentRunner.java
│   └── AiWorker.java, AiServices.java, AiAgentServices.java
├── groovyScripts/
│   ├── AiTest.groovy, AiStructuredTest.groovy
│   ├── GetUsageSummary.groovy, GetConversationHistory.groovy
│   └── GetAgentList.groovy
├── testdef/AgentRunnerTest.java
└── webapp/ai/
    ├── WEB-INF/controller.xml + web.xml
    ├── index.jsp
    └── widget/ai/CommonScreens.xml, AiMenus.xml, AiScreens.xml, AiForms.xml
{code}

h2. Development

Development branch: [patelanil/ofbiz-plugins — feature/ai-plugin|https://github.com/patelanil/ofbiz-plugins/tree/feature/ai-plugin]

Full documentation: [OFBiz AI Plugin — Agent Framework for LLM Integration|https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=430407963]

Java 17 minimum (OFBiz trunk requirement).

  was:
## Summary

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

## Motivation

AI/LLM capabilities are increasingly essential in enterprise applications.
OFBiz currently has no built-in way to call AI providers from services.
Developers who need AI in their OFBiz implementations must build this 
infrastructure themselves, each time, without consistency.

This plugin provides a reusable, convention-following integration that any
OFBiz deployment can install without modifying framework code.

## Design

The plugin follows established OFBiz patterns throughout:

### Lifecycle — Container interface
Implements the Container interface for lifecycle management, same pattern
as BirtContainer in the birt plugin. Registered in ofbiz-component.xml:

    <container name="ai-container" loaders="main"
               class="org.apache.ofbiz.ai.container.AiContainer"/>

AiContainer.start() reads configuration, initializes LangChain4j ChatModel
clients, and stores them via AiFactory (same pattern as BirtFactory).
AiContainer.stop() cleanly destroys all clients.

### Configuration — ai.properties
Configuration via config/ai.properties, read with UtilProperties:

    ai.provider=openai
    ai.model=gpt-4o-mini
    ai.baseUrl=
    ai.apiKey=
    ai.timeout=60

Multiple named configurations are supported for multi-provider setups.

### Services — callable from any service definition
Two OFBiz services exposed via servicedef/services.xml:

    ai.generate              — List messages → String response
    ai.generateStructured    — List messages + Map schema → Map result

Callable from any service, screen action, or scheduled job via dispatcher:

    Map result = dispatcher.runSync("ai.generate",
        UtilMisc.toMap("messages", messages, "userLogin", userLogin))

### Utility class — AiWorker
Static utility for programmatic use in Java and Groovy service implementations,
parallel to BirtWorker:

    String response = AiWorker.generate(dctx, messages)
    Map result = AiWorker.generateStructured(dctx, messages, schema)

### Structured output schema
Schema uses JSON Schema vocabulary — provider-agnostic, no Java class names:

    Map schema = UtilMisc.toMap(
        "status",    UtilMisc.toMap("type", "string"),
        "amount",    UtilMisc.toMap("type", "number"),
        "approved",  UtilMisc.toMap("type", "boolean")
    )

## What this does NOT do

- Does not modify any framework code
- Does not affect OFBiz if plugin is not installed
- Does not add any required dependencies to the framework
- Does not hardcode any specific AI provider

## Dependencies

Both are Apache 2.0 licensed:
- dev.langchain4j:langchain4j:1.8.0
- dev.langchain4j:langchain4j-open-ai:1.8.0

LangChain4j supports OpenAI, Anthropic, Ollama, Groq, and any 
OpenAI-compatible endpoint. Provider selection is configuration-only.

## Plugin structure

    plugins/ai/
    ├── ofbiz-component.xml
    ├── config/
    │   └── ai.properties
    ├── servicedef/
    │   └── services.xml
    ├── src/main/java/org/apache/ofbiz/ai/
    │   ├── container/AiContainer.java
    │   ├── AiFactory.java
    │   ├── services/AiServices.java
    │   └── util/AiWorker.java
    └── build.gradle

## Implementation

Development branch:
https://github.com/patelanil/ofbiz-plugins/tree/feature/ai-plugin

Java 17 minimum (OFBiz trunk requirement).
LangChain4j 1.8.0 (Java 17 compatible, Apache 2.0).


> New Plugin: ai - Agent Framework for LLM Integration
> ----------------------------------------------------
>
>                 Key: OFBIZ-13408
>                 URL: https://issues.apache.org/jira/browse/OFBIZ-13408
>             Project: OFBiz
>          Issue Type: New Feature
>          Components: plugin
>            Reporter: Anil K Patel
>            Assignee: Anil K Patel
>            Priority: Trivial
>
> h2. Summary
> New optional plugin ({{plugins/ai}}) that brings native AI agent capabilities to OFBiz — callable from any service, screen action, or scheduled job. No external AI SDK required.
> h2. Motivation
> AI/LLM capabilities are increasingly essential in enterprise applications. OFBiz currently has no built-in way to call AI providers from services. Developers who need AI in their OFBiz implementations must build this infrastructure themselves, each time, without consistency.
> This plugin provides a reusable, convention-following integration that any OFBiz deployment can install without modifying framework code.
> h2. Design
> The plugin follows established OFBiz patterns throughout. The original design used LangChain4j; after initial smoke testing, LangChain4j was removed. Its functionality required fewer than 300 lines of direct HTTP code and would have added 15+ MB of transitive dependencies to the OFBiz runtime. The plugin now uses {{java.net.http.HttpClient}} and Jackson — both already present in the OFBiz runtime. No external AI SDK.
> h3. Lifecycle — Container interface
> Implements the Container interface for lifecycle management, same pattern as BirtContainer in the birt plugin. Registered in ofbiz-component.xml:
> {code}
> <container name="ai-container" loaders="main"
>            class="org.apache.ofbiz.ai.container.AiContainer"/>
> {code}
> AiContainer.start() loads all provider, tool, and agent registries from XML files across all components. AiContainer.stop() cleanly shuts down.
> h3. Configuration — ai.properties
> Named provider blocks via config/ai.properties, read with UtilProperties:
> {code}
> ai.provider.openai-default.baseUrl=https://api.openai.com/v1
> ai.provider.openai-default.apiKey=...
> ai.provider.openai-default.model=gpt-4o
> ai.provider.openai-default.timeout=60
> ai.provider.anthropic-default.baseUrl=https://api.anthropic.com/v1
> ai.provider.anthropic-default.apiKey=...
> ai.provider.anthropic-default.model=claude-sonnet-4-5
> ai.provider.anthropic-default.timeout=60
> ai.provider.anthropic-default.extraHeaders=anthropic-version:2023-06-01
> ai.provider.ollama-default.baseUrl=http://localhost:11434/v1
> ai.provider.ollama-default.model=llama3.2
> {code}
> Any OpenAI-compatible endpoint is supported. Provider selection is configuration-only — no code change required.
> h3. Declarative agents and tools — XML
> Developers declare tools (OFBiz services) and agents in XML files placed in any component’s {{ai/}} directory. ToolCatalog and AgentRegistry scan all components at startup.
> *tools.xml* — exposes any OFBiz service as an AI tool:
> {code:xml}
> <tools>
>   <tool name="getMrpRecommendation"
>         service-name="getMrpRecommendation"
>         description="Gets MRP recommendations for a product facility"
>         required-permission="MANUFACTURING_VIEW"
>         requires-approval="false"/>
> </tools>
> {code}
> *agent.xml* — declares an agent with provider, system prompt, and tool allow-list:
> {code:xml}
> <agents>
>   <agent name="mrp-advisor" provider="openai-default" max-iterations="6">
>     <system-prompt>You are an MRP advisor for a manufacturing company...</system-prompt>
>     <tools>
>       <tool name="getMrpRecommendation"/>
>     </tools>
>   </agent>
> </agents>
> {code}
> Adding a new tool or agent requires only XML — no Java changes.
> h3. Services
> Single service entry point:
> {code}
> agentRun(agentName, userMessage, userLogin)
>   optional IN:  threadId, approvalRequired
>   OUT: assistantMessage, stopReason, iterationsUsed, threadId, proposalId
> {code}
> Supporting services:
> {code}
> getUsageSummary(agentName?, userLoginId?, fromDate?, thruDate?)
>   OUT: totalRuns, totalInputTokens, totalOutputTokens, estimatedCostUsd
> getConversationHistory(threadId) → ordered message list
> archiveConversationThread(threadId)
> approveAgentProposal(proposalId) → resumes agent loop, returns final answer
> rejectAgentProposal(proposalId, rejectionReason) → LLM acknowledgment
> {code}
> h3. Database entities
> Persistent audit trail for every run:
> || Entity || Purpose ||
> | AiAgentRun | Every agentRun invocation — agent, user, tokens, status, messages |
> | AiAgentToolCall | Every tool call within a run — arguments, result, timing |
> | AiProviderCost | Cost-per-million-token rates by model for cost estimation |
> | AiConversationThread | Named multi-turn conversation threads |
> | AiConversationMessage | Individual messages within a thread (ordered by sequenceNum) |
> | AiAgentProposal | Suspended runs awaiting human approval — full conversation state |
> | AiAgentProposalTool | Individual tool calls within a pending proposal |
> h3. Security
> Tool execution is gated by the OFBiz permission model. Each tool declares a {{required-permission}} in its XML. Calls without sufficient permission fail gracefully — the LLM receives an error and continues reasoning.
> Tools can declare {{requires-approval="true"}} for irreversible actions. The agent suspends mid-run, persists the full conversation state as an AiAgentProposal, and returns a {{proposalId}}. A human calls {{approveAgentProposal}} (which executes the tools and resumes the loop) or {{rejectAgentProposal}} (which returns a graceful LLM acknowledgment).
> h3. Admin webapp at /ai
> Full OFBiz web application mounted at {{/ai}} with six screens:
> * *Agent List* — all registered agents; launch an interactive agent run
> * *Run History* — paginated AiAgentRun list with filter by agent/user/status/date
> * *Run Detail* — single run view with embedded tool-call list
> * *Usage Dashboard* — aggregated token and estimated cost summary with filter
> * *Proposal Review* — pending proposals with one-click Approve or Reject
> * *Thread Explorer* — conversation thread list; message history view; archive action
> Permission model: {{OFBTOOLS _VIEW}} for read; {{OFBTOOLS _UPDATE}} for approve/reject.
> h3. Testing
> {{testdef/AgentRunnerTest.java}} uses {{MockAiChatClient}} to test the agent loop, tool dispatch, result truncation, and max-iterations stop — no network, no API key required.
> h2. What this does NOT do
> - Does not modify any framework code
> - Does not affect OFBiz if plugin is not installed
> - Does not add any required dependencies to the framework
> - Does not hardcode any specific AI provider
> h2. Dependencies
> None. Uses only {{java.net.http.HttpClient}} and Jackson, both already present in the OFBiz runtime.
> h2. Plugin structure
> {code}
> plugins/ai/
> ├── ofbiz-component.xml
> ├── build.gradle
> ├── config/ai.properties + AiUiLabels.xml
> ├── servicedef/services.xml
> ├── entitydef/AiAgentEntities.xml
> ├── data/AiAgentSeedData.xml
> ├── ai/sample.tools.xml + sample.agent.xml
> ├── src/main/java/org/apache/ofbiz/ai/
> │   ├── container/AiContainer.java
> │   ├── agent/ProviderRegistry.java, ProviderConfig.java
> │   ├── agent/ToolDescriptor.java, ToolCatalog.java
> │   ├── agent/AgentDefinition.java, AgentRegistry.java
> │   ├── agent/AiChatClient.java, AiHttpClient.java, MockAiChatClient.java
> │   ├── agent/AgentRunner.java
> │   └── AiWorker.java, AiServices.java, AiAgentServices.java
> ├── groovyScripts/
> │   ├── AiTest.groovy, AiStructuredTest.groovy
> │   ├── GetUsageSummary.groovy, GetConversationHistory.groovy
> │   └── GetAgentList.groovy
> ├── testdef/AgentRunnerTest.java
> └── webapp/ai/
>     ├── WEB-INF/controller.xml + web.xml
>     ├── index.jsp
>     └── widget/ai/CommonScreens.xml, AiMenus.xml, AiScreens.xml, AiForms.xml
> {code}
> h2. Development
> Development branch: [patelanil/ofbiz-plugins — feature/ai-plugin|https://github.com/patelanil/ofbiz-plugins/tree/feature/ai-plugin]
> Full documentation: [OFBiz AI Plugin — Agent Framework for LLM Integration|https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=430407963]
> Java 17 minimum (OFBiz trunk requirement).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)