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:

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:

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:

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:

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:

When NOT to use:


RestDataToolLogic

"I provide REST API operations for server-side features like scheduler jobs, health checks, and process listings."

When to use:

When NOT to use:


ChatToolProvider

"I orchestrate LangChain4j @Tool methods for the Chat API by delegating to specialized services."

When to use:

When NOT to use:


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:

  1. Adds LangChain4j @Tool annotations
  2. Delegates to specialized services
  3. Converts results to JSON for LLM consumption
  4. Does NOT contain business logic

Evolution Insights

Why Three Classes?

  1. Separation of Concerns

    • RegistryToolLogic: JDBC expertise, AD domain knowledge
    • RestDataToolLogic: REST API expertise, server operations
    • ChatToolProvider: LangChain4j integration, tool orchestration
  2. Reusability

    • RegistryToolLogic used by: MCP tools, CLI commands, ChatToolProvider
    • RestDataToolLogic used by: MCP tools, ChatToolProvider
    • ChatToolProvider used by: Chat API only
  3. 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:


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
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:

Decision: Keep current names but add comprehensive JavaDoc (done in classes).


Documentation Improvements

Added to Each Class

  1. RegistryToolLogic (src/main/java/org/idempiere/cli/ai/shared/RegistryToolLogic.java)

    • ✅ Comprehensive JavaDoc with architecture diagram
    • ✅ Query strategy explanation (exact, pattern, semantic)
    • ✅ Usage examples
  2. RestDataToolLogic (src/main/java/org/idempiere/cli/ai/shared/RestDataToolLogic.java)

    • ✅ Hybrid backend architecture diagram
    • ✅ Backend selection guidance
    • ✅ ADR-015 reference
  3. 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:

Purpose Clarity:

Key Takeaways

  1. RegistryToolLogic = JDBC + AD metadata + RAG search

  2. RestDataToolLogic = REST API + server operations

  3. ChatToolProvider = LangChain4j orchestration (delegates to #1 and #2)

  4. All created within 5 days during AI integration sprint

  5. ChatToolProvider is the newest - still evolving (just fixed Dec 12)

  6. RegistryToolLogic is the foundation - most powerful, already used in fix

Recommendations

  1. Keep current names - renaming would break too much
  2. Rely on JavaDoc - now comprehensive in each class
  3. Use this ADR - as reference for understanding relationships
  4. Future tools - follow ChatToolProvider pattern (thin wrapper, delegate to services)

References


Approval Status: Documentation (No approval needed) Version: v1.65.0+

Path: /docs/developers/architecture/idempiere-hub/054-ai-tool-architecture-clarity