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:
- Developer CLI - Terminal-based natural language interface for development tasks
- AI IDE Integration - Claude Code/Desktop integration via Model Context Protocol (MCP)
- ERP Backend API - REST API for ZK/Angular clients to provide chat-driven UI
Current situation:
- Duplicated AI logic between
com.cloudempiere.ai(Java 11 OSGi plugin) andidempiere-cli(Java 17+ Quarkus) - Fragmented tool implementations
- No unified architecture for AI capabilities
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?
- Different lifecycle: CLI is per-request, API maintains conversation state
- Different memory: API needs persistent conversation history
- Different context: API extracts iDempiere tenant context from headers
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
- [x] Quarkus 3.27 + LangChain4j 1.3.1 migration
- [x] Three execution modes (CLI, MCP, Chat API)
- [x] Tool framework with shared logic layer
- [x] RAG knowledge base (Wiki, K_Entry, AD metadata)
- [x] MCP Server (HTTP/SSE, 40+ tools)
- [x] Chat API endpoints (
/v1/chat/completions,/v1/models) - [x] Streaming support (SSE)
- [x] Guardrails (Input/Output/Execution)
- [x] Observability (Audit, Metrics, Cost tracking)
- [x] Multi-provider configuration
- [x] Context extraction for tenant isolation
- [x] Agent boundaries
⚠️ Needs Review/Refactoring
- [ ] Satellite API integration testing - Verify with real clients
- [ ] JWT authentication - Add if needed for production
- [ ] Conversation persistence - Memory provider needs storage backend
- [ ] Embeddings endpoint - Currently placeholder
- [ ] Error handling standardization - Ensure consistent error responses
- [ ] Documentation - API documentation, deployment guide
- [ ] Performance testing - Benchmark chat latency, streaming performance
- [ ] Cost tracking validation - Verify pricing calculations
Next Steps
Phase 1: Review & Refactor (This Sprint)
-
Code Review
- [ ] Review all Satellite API components
- [ ] Validate tool implementations
- [ ] Check error handling patterns
- [ ] Review security guardrails
-
Refactor
- [ ] Consolidate duplicate code (if any)
- [ ] Standardize error responses
- [ ] Improve logging consistency
- [ ] Add missing Javadoc
-
Complete Missing Features
- [ ] Implement embeddings endpoint (if needed)
- [ ] Add JWT authentication (if needed)
- [ ] Implement conversation persistence
- [ ] Add health checks
Phase 2: Testing (Next Sprint)
-
Unit Tests
- [ ] Tool logic tests
- [ ] Guardrails tests
- [ ] Context extraction tests
-
Integration Tests
- [ ] Chat API endpoint tests
- [ ] Streaming tests
- [ ] Multi-provider tests
- [ ] RAG integration tests
-
End-to-End Tests
- [ ] ZK client integration test
- [ ] Angular client integration test
- [ ] MCP server test with Claude Code
- [ ] CLI command tests
-
Performance Tests
- [ ] Latency benchmarks
- [ ] Concurrent request handling
- [ ] Memory usage profiling
- [ ] Token usage validation
Phase 3: Deployment (Future)
-
Documentation
- [ ] API reference (OpenAPI spec)
- [ ] Deployment guide
- [ ] Configuration reference
- [ ] Client integration examples
-
DevOps
- [ ] Docker image
- [ ] Kubernetes manifests
- [ ] Monitoring setup (Prometheus/Grafana)
- [ ] CI/CD pipeline
Consequences
Positive
- ✅ Single codebase for all AI capabilities (CLI, MCP, API)
- ✅ Shared business logic eliminates duplication
- ✅ Modern stack (Java 17+, Quarkus, LangChain4j 1.x)
- ✅ Production-ready features (guardrails, observability, cost tracking)
- ✅ OpenAI-compatible API for easy client integration
- ✅ Multi-provider support for flexibility and cost optimization
- ✅ Comprehensive knowledge base for accurate responses
Negative
- ⚠️ Complexity - Three execution modes increase testing surface
- ⚠️ Migration effort - OSGi plugin clients need to migrate to Chat API
- ⚠️ Maintenance - Must keep up with LangChain4j, Quarkus updates
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 |
Related ADRs
- ADR-001: iDempiere Version Compatibility
- ADR-010: MCP Server Architecture
- ADR-021: RAG Architecture
- ADR-023: Quarkus LangChain4j Migration
- ADR-043: Satellite Service Evolution (superseded by this ADR)
- ADR-047: Satellite-iDempiere Integration (superseded by this ADR)
References
- Quarkus LangChain4j Documentation
- Model Context Protocol Specification
- OpenAI API Reference
- LangChain4j Documentation
ADR-048 | Version 1.0 | 2025-12-11 Status: ACCEPTED - Final Architecture Next Action: Review → Refactor → Test