ADR-048: iDempiere AI Hub - Unified Architecture

Status

ACCEPTED - Final Consolidated Architecture

Supersedes: ADR-043, ADR-046, ADR-047

Date

2025-12-11

Context and Problem Statement

iDempiere development requires AI assistance across multiple interfaces:

  1. Developer CLI - Terminal-based natural language interface for development tasks
  2. AI IDE Integration - Claude Code/Desktop integration via Model Context Protocol (MCP)
  3. ERP Backend API - REST API for ZK/Angular clients to provide chat-driven UI

Current situation:

Goal: Create a single AI Hub that serves all three use cases with shared business logic, unified knowledge base, and consistent tool framework.

Decision

Build idempiere-cli as a unified AI Hub for iDempiere with three execution modes:

┌────────────────────────────────────────────────────────────────┐
│               iDempiere AI Hub (idempiere-cli)                 │
│                   Quarkus 3.27 + Java 17+                      │
├────────────────────────────────────────────────────────────────┤
│                                                                │
│  Mode 1: CLI                Mode 2: MCP Server                 │
│  ┌──────────────────┐       ┌──────────────────┐              │
│  │ ask command      │       │ HTTP/SSE         │              │
│  │ Terminal UI      │       │ Port 8765        │              │
│  │ LangChain4j      │       │ Claude Code      │              │
│  └──────────────────┘       └──────────────────┘              │
│                                                                │
│  Mode 3: Chat API Backend                                     │
│  ┌────────────────────────────────────────────┐               │
│  │ REST API (OpenAI-compatible)               │               │
│  │ POST /v1/chat/completions                  │               │
│  │ GET  /v1/models                            │               │
│  │ Port 8081                                  │               │
│  │ iDempiere ZK/Angular clients               │               │
│  └────────────────────────────────────────────┘               │
│                                                                │
│  ┌────────────────────────────────────────────────────────┐   │
│  │            Shared Core Infrastructure                  │   │
│  ├────────────────────────────────────────────────────────┤   │
│  │ • Agent Layer (LangChain4j AI Services)                │   │
│  │ • Tool Framework (40+ tools, shared logic)             │   │
│  │ • RAG Knowledge Base (Wiki, KB, AD metadata)           │   │
│  │ • Guardrails (Input/Output/Execution validation)       │   │
│  │ • Observability (Audit, Metrics, Cost tracking)        │   │
│  │ • Multi-Provider Support (Ollama, Claude, GPT, Bedrock)│   │
│  └────────────────────────────────────────────────────────┘   │
│                                                                │
└────────────────────────────────────────────────────────────────┘

Architecture Components

1. Execution Modes (Profile-Based)

Mode Profile Port Primary Interface Use Case
CLI (default) 0 (disabled) Picocli commands Developer terminal usage
MCP Server mcp 8765 HTTP/SSE Claude Code/Desktop integration
Chat API satellite 8081 REST API iDempiere UI backend (ZK/Angular)

Configuration:

# CLI mode (default)
java -jar idempiere-hub-runner.jar ask "list C_ tables"

# MCP Server mode
java -Dquarkus.profile=mcp -jar idempiere-hub-runner.jar server mcp

# Chat API mode
java -Dquarkus.profile=chat-api -jar idempiere-hub-runner.jar server satellite

2. Agent Layer (AI Service)

Two AI service implementations based on context:

A. CliRouterAgent (CLI + MCP modes)

@RegisterAiService(tools = {
    RegistryTools.class,    // AD metadata queries
    TableTools.class,       // Table CRUD
    QueryTools.class,       // SQL queries
    GeneratorTools.class,   // Code generation
    DoctorTools.class,      // Diagnostics
    RestDataTools.class,    // iDempiere REST API
    RagTools.class         // Knowledge search
})
public interface CliRouterAgent {
    String route(@UserMessage String request);
}

B. ChatAgent (Chat API mode)

@RegisterAiService(
    tools = { /* same tools */ },
    chatMemoryProvider = SatelliteMemoryProvider.class
)
public interface ChatAgent {

    // Synchronous chat
    String chat(@UserMessage String request);

    // Streaming chat
    Multi<String> streamChat(@UserMessage String request);
}

Why two agents?

3. Tool Framework

Three-layer architecture:

┌─────────────────────────────────────────────────────┐
│  Layer 1: Shared Business Logic (Framework-agnostic)│
│  org.idempiere.cli.ai.shared/                       │
│  ├─ RegistryToolLogic.java                          │
│  ├─ QueryToolLogic.java                             │
│  ├─ TableToolLogic.java                             │
│  ├─ GeneratorToolLogic.java                         │
│  ├─ DoctorToolLogic.java                            │
│  ├─ RestDataToolLogic.java                          │
│  └─ MigrationScriptToolLogic.java                   │
└─────────────────────────────────────────────────────┘
           │                    │
           ▼                    ▼
┌────────────────────┐  ┌────────────────────┐
│ Layer 2a:          │  │ Layer 2b:          │
│ LangChain4j Tools  │  │ MCP Tools          │
│ (CLI + Chat API)   │  │ (MCP Server)       │
├────────────────────┤  ├────────────────────┤
│ @Tool (LC4j)       │  │ @Tool (MCP)        │
│ - RegistryTools    │  │ - McpRegistryTools │
│ - QueryTools       │  │ - McpQueryTools    │
│ - TableTools       │  │ - McpTableTools    │
│ - etc.             │  │ - etc.             │
└────────────────────┘  └────────────────────┘

Design principle: Business logic in Layer 1, framework integration in Layer 2.

4. RAG Knowledge Base (ADR-021)

Multi-source knowledge ingestion:

Source Type Content Dimension Model
iDempiere Wiki External docs Development guides, features 1024 mxbai-embed-large
K_Entry Internal KB CloudEmpiere support articles 1024 mxbai-embed-large
AD Metadata Schema metadata Tables, windows, processes, fields 1024 mxbai-embed-large

Storage: PostgreSQL + pgvector extension

Commands:

idempiere-cli knowledge init              # Create vector DB
idempiere-cli knowledge ingest --source all
idempiere-cli knowledge search "create callout"
idempiere-cli knowledge status

5. Guardrails & Security

Three-layer security:

@ApplicationScoped
public class InputGuard {
    // PII detection, SQL injection prevention
    GuardResult validate(String input);
}

@ApplicationScoped
public class OutputGuard {
    // Response filtering, sensitive data masking
    GuardResult validate(String output);
}

@ApplicationScoped
public class ExecutionGuard {
    // Tool permission checks, boundary validation
    GuardResult validateToolExecution(String toolName, Map<String, Object> params);
}

Agent Boundaries (ADR-043):

public enum AgentBoundary {
    READ_ONLY("default"),     // Query-only, no mutations
    FULL_ACCESS("admin");     // All operations
}

6. Observability

Three pillars:

A. Audit Logging

@ApplicationScoped
public class AuditService {
    void logChatCompletion(SatelliteRequest request, SatelliteResponse response);
    void logToolExecution(String toolName, Map<String, Object> params, ToolResult result);
}

B. Metrics (Micrometer)

@ApplicationScoped
public class MetricsCollector {
    Counter chatCompletions;
    Timer chatLatency;
    Counter tokensUsed;
}

C. Cost Tracking

@ApplicationScoped
public class CostGuard {
    // Model pricing configuration
    BigDecimal calculateCost(String model, int inputTokens, int outputTokens);
    boolean checkBudget(String userId, BigDecimal estimatedCost);
}

7. Multi-Provider Support

Supported LLM providers:

Provider Chat Model Embedding Model Use Case
Ollama llama3.2 mxbai-embed-large Local development, cost-free
Anthropic claude-sonnet-4 - Production, high quality
OpenAI gpt-4o text-embedding-3-large Production, general purpose
AWS Bedrock claude-3-sonnet titan-embed-v2 Enterprise, compliance

Configuration:

# Default provider
quarkus.langchain4j.chat-model.provider=ollama
quarkus.langchain4j.embedding-model.provider=ollama

# Model-specific settings
quarkus.langchain4j.ollama.chat-model.model-id=llama3.2
quarkus.langchain4j.anthropic.chat-model.model-id=claude-sonnet-4-20250514
quarkus.langchain4j.openai.chat-model.model-name=gpt-4o

8. Chat API Backend (OpenAI-Compatible)

REST Endpoints:

POST /v1/chat/completions
Content-Type: application/json

{
  "model": "llama3.2",
  "messages": [
    {"role": "user", "content": "list C_ tables"}
  ],
  "stream": false,
  "temperature": 0.7,
  "max_tokens": 4096
}

Response:

{
  "id": "chatcmpl-123",
  "object": "chat.completion",
  "created": 1234567890,
  "model": "llama3.2",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "Here are the C_ tables..."
    },
    "finish_reason": "stop"
  }],
  "usage": {
    "prompt_tokens": 15,
    "completion_tokens": 120,
    "total_tokens": 135
  }
}

Streaming (SSE):

POST /v1/chat/completions
Content-Type: application/json

{
  "model": "llama3.2",
  "messages": [...],
  "stream": true
}

# Response (Server-Sent Events):
data: {"id":"chatcmpl-123","choices":[{"delta":{"content":"Here"}}]}
data: {"id":"chatcmpl-123","choices":[{"delta":{"content":" are"}}]}
data: [DONE]

Model listing:

GET /v1/models

# Response:
{
  "object": "list",
  "data": [
    {
      "id": "llama3.2",
      "object": "model",
      "created": 1234567890,
      "owned_by": "ollama"
    },
    {
      "id": "claude-sonnet-4",
      "object": "model",
      "created": 1234567890,
      "owned_by": "anthropic"
    }
  ]
}

9. iDempiere Integration

Chat API mode provides backend for iDempiere UI:

┌──────────────────────────────────────────────────┐
│  iDempiere Server (Java 11)                      │
│  ┌────────────────────────────────────────────┐  │
│  │  ZK UI (Window/Tab/Field)                  │  │
│  │  ┌──────────────────────────────────────┐  │  │
│  │  │  AI Chat Panel                       │  │  │
│  │  │  (HTTP REST Client)                  │  │  │
│  │  └──────────────┬───────────────────────┘  │  │
│  └─────────────────┼──────────────────────────┘  │
└────────────────────┼─────────────────────────────┘
                     │
                     │ POST /v1/chat/completions
                     │ Headers:
                     │   X-iDempiere-Client-ID
                     │   X-iDempiere-User-ID
                     │   X-iDempiere-Role-ID
                     ▼
┌──────────────────────────────────────────────────┐
│  AI Hub (Satellite API Mode, Java 17+)          │
│  ┌────────────────────────────────────────────┐  │
│  │  SatelliteApiResource                     │  │
│  │  ├─ Extract context from headers          │  │
│  │  ├─ Apply guardrails                      │  │
│  │  ├─ Route to ChatAgent               │  │
│  │  └─ Record audit/metrics                  │  │
│  └────────────────────────────────────────────┘  │
│                     │                            │
│  ┌──────────────────▼──────────────────────────┐ │
│  │  ChatAgent                            │ │
│  │  (with DatabaseQueryTool, RestDataTools)  │ │
│  └────────────────┬────────────────────────────┘ │
└────────────────────┼─────────────────────────────┘
                     │
      ┌──────────────┼──────────────┐
      ▼              ▼              ▼
┌──────────┐  ┌──────────┐  ┌──────────┐
│ Direct   │  │iDempiere │  │ Vector   │
│ SQL      │  │ REST API │  │ DB (RAG) │
│(read-only)│  │         │  │          │
└──────────┘  └──────────┘  └──────────┘

Context Propagation:

@ApplicationScoped
public class ContextExtractor {
    ChatContext extract(MultivaluedMap<String, String> headers) {
        return ChatContext.builder()
            .clientId(headers.getFirst("X-iDempiere-Client-ID"))
            .userId(headers.getFirst("X-iDempiere-User-ID"))
            .roleId(headers.getFirst("X-iDempiere-Role-ID"))
            .language(headers.getFirst("X-iDempiere-Language"))
            .build();
    }
}

Implementation Status

✅ Completed

⚠️ Needs Review/Refactoring

Next Steps

Phase 1: Review & Refactor (This Sprint)

  1. Code Review

    • [ ] Review all Satellite API components
    • [ ] Validate tool implementations
    • [ ] Check error handling patterns
    • [ ] Review security guardrails
  2. Refactor

    • [ ] Consolidate duplicate code (if any)
    • [ ] Standardize error responses
    • [ ] Improve logging consistency
    • [ ] Add missing Javadoc
  3. Complete Missing Features

    • [ ] Implement embeddings endpoint (if needed)
    • [ ] Add JWT authentication (if needed)
    • [ ] Implement conversation persistence
    • [ ] Add health checks

Phase 2: Testing (Next Sprint)

  1. Unit Tests

    • [ ] Tool logic tests
    • [ ] Guardrails tests
    • [ ] Context extraction tests
  2. Integration Tests

    • [ ] Chat API endpoint tests
    • [ ] Streaming tests
    • [ ] Multi-provider tests
    • [ ] RAG integration tests
  3. End-to-End Tests

    • [ ] ZK client integration test
    • [ ] Angular client integration test
    • [ ] MCP server test with Claude Code
    • [ ] CLI command tests
  4. Performance Tests

    • [ ] Latency benchmarks
    • [ ] Concurrent request handling
    • [ ] Memory usage profiling
    • [ ] Token usage validation

Phase 3: Deployment (Future)

  1. Documentation

    • [ ] API reference (OpenAPI spec)
    • [ ] Deployment guide
    • [ ] Configuration reference
    • [ ] Client integration examples
  2. DevOps

    • [ ] Docker image
    • [ ] Kubernetes manifests
    • [ ] Monitoring setup (Prometheus/Grafana)
    • [ ] CI/CD pipeline

Consequences

Positive

Negative

Risks

Risk Mitigation
Performance degradation Benchmark before deployment, optimize hot paths
Cost overruns CostGuard enforces budgets, default to Ollama
Security vulnerabilities Guardrails validate all inputs/outputs, read-only SQL user
Integration issues Comprehensive integration tests, staging environment

References


ADR-048 | Version 1.0 | 2025-12-11 Status: ACCEPTED - Final Architecture Next Action: Review → Refactor → Test

Path: /docs/developers/architecture/idempiere-hub/048-idempiere-ai-hub-architecture