ADR-054: AI Tool Architecture - Purpose and Evolution
Status: Active Documentation Date: 2025-12-12 Context: Clarifying the purpose and relationships between AI tool classes
Problem Statement
Multiple tool classes exist in the AI/Chat API architecture with overlapping names but distinct purposes:
RegistryToolLogic(src/main/java/org/idempiere/cli/ai/shared/)RestDataToolLogic(src/main/java/org/idempiere/cli/ai/shared/)ChatToolProvider(src/main/java/org/idempiere/cli/chatapi/tool/impl/)
Question: What is the purpose of each, were they created early or recently, and are they self-explanatory?
Tool Evolution Timeline
1. RegistryToolLogic (December 6, 2025 - EARLY)
Created: Commit cdf0ce9 - "feat(ai): add LangChain4j integration for natural language CLI (ADR-013)"
Purpose: Application Dictionary registry operations via direct JDBC
ADR: ADR-008 (Application Dictionary Registry)
Version: v1.23.0 (implemented), v1.30.0 (LangChain4j integration)
Evolution:
cdf0ce9 (Dec 6) - Initial creation with LangChain4j integration
9eb3836 (Dec 7) - Enhanced describeTable with window/tab context
ae54559 (Dec 7) - Added hybrid RAG-augmented semantic search
cb494c7 (Dec 11) - Refactored config, renamed env vars
Core Methods:
listTables()- Query AD_Table via JDBClistColumns()- Query AD_Column via JDBCdescribeTable()- Full table metadata + relationships + windows/tabslistWindows(),listProcesses(),listReferences()- AD queriessemanticSearch()- RAG-powered vector searchsmartSearch()- Intelligent strategy selection (SQL vs RAG)
Architecture:
RegistryToolLogic
│
├─ Direct JDBC → PostgreSQL (AD_Table, AD_Column, AD_Window, etc.)
├─ RAG Integration → PGVector (semantic search)
└─ Pattern Detection (document, master-data, transaction)
Purpose: > Direct database access for comprehensive Application Dictionary metadata with semantic search capabilities.
2. RestDataToolLogic (December 6, 2025 - EARLY)
Created: Commit 772968c - "feat(ai): add REST data tools facade for hybrid backend (ADR-015)"
Purpose: REST API facade for hybrid backend operations
ADR: ADR-015 (REST Data Tools Facade)
Version: v1.30.0
Core Methods:
listModels()- REST API: GET /modelslistProcessesRest()- REST API: GET /processeslistServerJobs()- REST API: GET /scheduler/jobsgetServerJob()- REST API: GET /scheduler/jobs/{id}toggleServerJobState()- REST API: PUT /scheduler/jobs/{id}checkHealth()- REST API: GET /healthgetBackendStatus()- Backend availability check
Architecture:
RestDataToolLogic
│
└─ GeneratedOpenApiFactory → iDempiere REST API
│
└─ HTTP/JSON → iDempiere Server (running instance required)
Purpose: > REST API operations that require a running iDempiere server. Focus on server-side operations (scheduler, health, processes).
3. ChatToolProvider (December 11, 2025 - RECENT)
Created: Commit 55f04b3 - "feat(satellite): add SatelliteToolProvider with LangChain4j tools"
Original Name: SatelliteToolProvider
Renamed: Commit 2e38e38 - "refactor!: rename Satellite to Chat API across codebase (ADR-049)"
Fixed: Commit e5e034c - "fix(chat-api): fix searchRecords hallucination bug and add getTableColumns tool"
Purpose: LangChain4j @Tool methods for Chat API
ADR: ADR-048 (iDempiere AI Hub Integration Architecture)
Version: v1.59.0 (created), v1.60.0 (renamed), v1.65.0 (fixed)
Core Methods:
searchRecords()- Search table records via REST APIgetTableColumns()- Get column schema (uses RegistryToolLogic!)getRecord()- Get single record by IDlistProcesses()- List processes (delegates to RestDataToolLogic)executeProcess()- Execute process via REST APIlistServerJobs()- List scheduler jobs (delegates to RestDataToolLogic)getServerJob()- Get job details (delegates to RestDataToolLogic)toggleServerJob()- Enable/disable job (delegates to RestDataToolLogic)searchKnowledge()- Search RAG knowledge base (uses RagService)executeQuery()- Execute read-only SQL (uses DatabaseQueryTool)checkHealth()- Health check (delegates to RestDataToolLogic)getBackendStatus()- Backend status (delegates to RestDataToolLogic)
Architecture:
ChatToolProvider (@ApplicationScoped)
│
├─ @Inject RestDataToolLogic → REST API operations
├─ @Inject RegistryToolLogic → Direct JDBC metadata queries
├─ @Inject GeneratedOpenApiFactory → REST API client
├─ @Inject RagService → Vector search
└─ @Inject DatabaseQueryTool → Direct SQL queries
↓ Exposed as LangChain4j @Tool methods ↓
LangChain4j ChatAgent → ChatToolProvider.@Tool methods
Purpose: > Orchestration layer that provides LangChain4j @Tool annotations for Chat API by delegating to specialized services.
Tool Comparison Matrix
| Aspect | RegistryToolLogic | RestDataToolLogic | ChatToolProvider |
|---|---|---|---|
| Created | Dec 6, 2025 (EARLY) | Dec 6, 2025 (EARLY) | Dec 11, 2025 (RECENT) |
| ADR | ADR-008, ADR-013 | ADR-015 | ADR-048, ADR-049 |
| Layer | Business Logic | Business Logic | LangChain4j Wrapper |
| Data Source | Direct JDBC | REST API | Delegates to others |
| Protocol | SQL | HTTP/JSON | LangChain4j @Tool |
| Requires | PostgreSQL | iDempiere Server | Multiple services |
| Primary Use | AD metadata | Server operations | AI agent tools |
| Semantic Search | ✅ Yes (RAG) | ❌ No | ✅ Via RagService |
| Pattern Detection | ✅ Yes | ❌ No | ❌ No |
| Translations | ✅ Yes (_trl tables) | ❌ No | ❌ No |
| Relationships | ✅ Window/Tab context | ❌ Limited | ❌ No |
| CDI Scope | @ApplicationScoped | @ApplicationScoped | @ApplicationScoped |
| Injection | None | GeneratedOpenApiFactory, IdempiereConfig | All tools (4+) |
Purpose Boundaries (Self-Explanatory)
RegistryToolLogic
"I query Application Dictionary tables directly via JDBC for comprehensive metadata including patterns, relationships, translations, and semantic search."
When to use:
- Need AD metadata (tables, columns, windows, processes, references)
- Want semantic/RAG search over AD elements
- Need pattern detection (document, master-data, transaction)
- Need translations (_trl tables)
- Need relationship traversal (Window→Tab→Field→Column)
- Want fast, comprehensive AD queries
When NOT to use:
- Need to execute processes (use RestDataToolLogic)
- Need scheduler operations (use RestDataToolLogic)
- Need record CRUD (use GeneratedOpenApiFactory directly)
- Building LangChain4j tools (use ChatToolProvider instead)
RestDataToolLogic
"I provide REST API operations for server-side features like scheduler jobs, health checks, and process listings."
When to use:
- Need scheduler job operations (list, get, toggle, run)
- Need health/availability checks
- Need process listings via REST API
- Need model listings via REST API
- Want hybrid backend approach (REST or SQL)
When NOT to use:
- Need AD metadata (use RegistryToolLogic)
- Need semantic search (use RegistryToolLogic)
- Building LangChain4j tools (use ChatToolProvider instead)
- Need direct SQL (use RegistryToolLogic)
ChatToolProvider
"I orchestrate LangChain4j @Tool methods for the Chat API by delegating to specialized services."
When to use:
- Building LangChain4j AI agent
- Exposing tools to LLM
- Need @Tool annotations
- Want unified AI tool interface
- Building Chat API endpoints
When NOT to use:
- Direct business logic (use RegistryToolLogic or RestDataToolLogic)
- Reusable service logic (create in shared package)
- MCP server tools (use MCP-specific wrappers)
- CLI commands (use service classes directly)
Delegation Pattern (How ChatToolProvider Works)
@ApplicationScoped
public class ChatToolProvider {
@Inject RestDataToolLogic restLogic; // REST API operations
@Inject RegistryToolLogic registryLogic; // JDBC AD queries
@Inject GeneratedOpenApiFactory apiFactory; // REST client
@Inject RagService ragService; // Vector search
@Inject DatabaseQueryTool databaseQueryTool;// Direct SQL
// ========== Model/Record Operations (REST API) ==========
@Tool("Search for records in an iDempiere table...")
public String searchRecords(String tableName, String filter) {
// Uses GeneratedOpenApiFactory → REST API
apiFactory.models().modelsTableNameGet(tableName, ...);
}
@Tool("Get column names for a table...")
public String getTableColumns(String tableName) {
// Delegates to RegistryToolLogic → JDBC
return registryLogic.listColumns(tableName).toJson();
}
// ========== Process Operations (REST API) ==========
@Tool("List available processes...")
public String listProcesses(String filter) {
// Delegates to RestDataToolLogic → REST API
return restLogic.listProcessesRest(filter).toJson();
}
@Tool("Execute a process...")
public String executeProcess(String processId, String params) {
// Uses GeneratedOpenApiFactory → REST API
apiFactory.process().processesProcessSlugPost(processId, params);
}
// ========== Server Operations (REST API) ==========
@Tool("List background server jobs...")
public String listServerJobs() {
// Delegates to RestDataToolLogic → REST API
return restLogic.listServerJobs().toJson();
}
@Tool("Enable or disable a server job...")
public String toggleServerJob(String jobId, boolean enabled) {
// Delegates to RestDataToolLogic → REST API
return restLogic.toggleServerJobState(jobId).toJson();
}
// ========== Knowledge & RAG (Vector Search) ==========
@Tool("Search iDempiere knowledge base...")
public String searchKnowledge(String query, Integer limit) {
// Delegates to RagService → PGVector
return ragService.search(query, null).toJson();
}
// ========== Direct SQL (JDBC) ==========
@Tool("Execute read-only SQL query...")
public String executeQuery(String sql, Integer maxRows) {
// Delegates to DatabaseQueryTool → JDBC
return databaseQueryTool.execute(args, null).toJson();
}
// ========== Health & Status (REST API) ==========
@Tool("Check iDempiere API health...")
public String checkHealth() {
// Delegates to RestDataToolLogic → REST API
return restLogic.checkHealth().toJson();
}
}
Pattern: ChatToolProvider is a thin orchestration layer that:
- Adds LangChain4j
@Toolannotations - Delegates to specialized services
- Converts results to JSON for LLM consumption
- Does NOT contain business logic
Evolution Insights
Why Three Classes?
-
Separation of Concerns
- RegistryToolLogic: JDBC expertise, AD domain knowledge
- RestDataToolLogic: REST API expertise, server operations
- ChatToolProvider: LangChain4j integration, tool orchestration
-
Reusability
- RegistryToolLogic used by: MCP tools, CLI commands, ChatToolProvider
- RestDataToolLogic used by: MCP tools, ChatToolProvider
- ChatToolProvider used by: Chat API only
-
Historical Reasons
- RegistryToolLogic created first for LangChain4j CLI (ADR-013)
- RestDataToolLogic added for hybrid backend support (ADR-015)
- ChatToolProvider created for Satellite/Chat API (ADR-048)
- All created within 5 days (Dec 6-11, 2025)
Recent Fix: searchRecords() Bug (Dec 12, 2025)
Problem: searchRecords() was calling wrong API endpoint causing LLM hallucinations.
Before (BUGGY):
@Tool("Search for records...")
public String searchRecords(String tableName, String filter) {
// WRONG: Lists TABLES, not records IN a table!
return restLogic.listModels(filter).toJson();
}
After (FIXED - commit e5e034c):
@Tool("Search for records... IMPORTANT: Use actual column names...")
public String searchRecords(String tableName, String filter) {
// CORRECT: Queries records IN the specified table
apiFactory.models().modelsTableNameGet(tableName, ..., filter, ...);
}
@Tool("Get column names for a table...")
public String getTableColumns(String tableName) {
// NEW: Help LLM discover actual column names
return registryLogic.listColumns(tableName).toJson();
}
This fix demonstrates:
- ChatToolProvider orchestrates (calls apiFactory and registryLogic)
- RegistryToolLogic provides metadata (column names)
- Business logic stays in service classes
Naming Self-Explanatory Assessment
Current Names
| Class | Self-Explanatory? | Improvement Suggestions |
|---|---|---|
RegistryToolLogic |
⚠️ Partial | Good: "Registry" implies AD lookup<br>Unclear: "Tool" + "Logic" is redundant<br>Better: ApplicationDictionaryRegistry or ADMetadataService |
RestDataToolLogic |
⚠️ Partial | Good: "Rest" implies HTTP API<br>Unclear: "Data" is vague, "Tool" + "Logic" redundant<br>Better: IdempiereRestFacade or ServerOperationsService |
ChatToolProvider |
✅ Good | Clear: "Chat" = Chat API, "Tool" = LangChain4j tools, "Provider" = supplies tools<br>Self-explanatory for LangChain4j context |
Recommended Naming (If Refactoring)
src/main/java/org/idempiere/cli/ai/shared/
├── ADMetadataService.java (was RegistryToolLogic)
├── ServerOperationsService.java (was RestDataToolLogic)
└── ToolResult.java
src/main/java/org/idempiere/cli/chatapi/tool/impl/
└── ChatToolProvider.java (good as-is)
Rationale:
ADMetadataService- Clear it's about Application Dictionary metadataServerOperationsService- Clear it's server-side operations (scheduler, health)ChatToolProvider- Already clear for LangChain4j context
Decision: Keep current names but add comprehensive JavaDoc (done in classes).
Documentation Improvements
Added to Each Class
-
RegistryToolLogic (src/main/java/org/idempiere/cli/ai/shared/RegistryToolLogic.java)
- ✅ Comprehensive JavaDoc with architecture diagram
- ✅ Query strategy explanation (exact, pattern, semantic)
- ✅ Usage examples
-
RestDataToolLogic (src/main/java/org/idempiere/cli/ai/shared/RestDataToolLogic.java)
- ✅ Hybrid backend architecture diagram
- ✅ Backend selection guidance
- ✅ ADR-015 reference
-
ChatToolProvider (src/main/java/org/idempiere/cli/chatapi/tool/impl/ChatToolProvider.java)
- ✅ Architecture diagram showing delegation
- ✅ "Why Not Wrappers?" explanation
- ✅ ADR-048 reference
Conclusion
Are They Self-Explanatory?
Timeline:
- ✅ Early: RegistryToolLogic, RestDataToolLogic (Dec 6, 2025)
- ✅ Recent: ChatToolProvider (Dec 11, 2025)
Purpose Clarity:
- ⚠️ Partial - Class names could be clearer
- ✅ Good - JavaDoc now comprehensive
- ✅ Good - Architecture diagrams added
- ✅ Good - This ADR documents purposes and relationships
Key Takeaways
-
RegistryToolLogic = JDBC + AD metadata + RAG search
-
RestDataToolLogic = REST API + server operations
-
ChatToolProvider = LangChain4j orchestration (delegates to #1 and #2)
-
All created within 5 days during AI integration sprint
-
ChatToolProvider is the newest - still evolving (just fixed Dec 12)
-
RegistryToolLogic is the foundation - most powerful, already used in fix
Recommendations
- Keep current names - renaming would break too much
- Rely on JavaDoc - now comprehensive in each class
- Use this ADR - as reference for understanding relationships
- Future tools - follow ChatToolProvider pattern (thin wrapper, delegate to services)
References
- ADR-008: Application Dictionary Registry
- ADR-013: LangChain4j Natural Language CLI
- ADR-015: REST Data Tools Facade
- ADR-048: iDempiere AI Hub Integration Architecture
- ADR-053: Build-Time Model Discovery
- ADR-053 Comparison: Integration with ADR-008
Approval Status: Documentation (No approval needed) Version: v1.65.0+