ADR-046: Satellite-iDempiere REST API Integration

Status

Proposed

Date

2025-12-10

Deciders

Context and Problem Statement

ADR-043 through ADR-045 define the Chat API Service architecture. A key decision remains: How should the Satellite Service persist data (metrics, audit, usage) to iDempiere?

Options Considered

  1. Direct Database Access - Satellite connects directly to iDempiere PostgreSQL
  2. REST API Integration - Satellite calls iDempiere REST API endpoints
  3. Message Queue - Satellite publishes to queue, iDempiere consumes

Current Assumption (ADR-044)

ADR-044 assumed direct database access via Panache entities, which has issues:

Decision Drivers

Decision Outcome

Use iDempiere REST API as primary integration, with optional message queue for high-volume async operations.

Architecture

┌─────────────────────────────────────────────────────────────────────────────────────┐
│                         CHAT API SERVICE                                         │
│                                                                                      │
│  ┌────────────────────────────────────────────────────────────────────────────────┐ │
│  │                           AI Processing Layer                                   │ │
│  │  • Chat completions                                                            │ │
│  │  • Embeddings                                                                  │ │
│  │  • Tool execution                                                              │ │
│  └──────────────────────────────────┬─────────────────────────────────────────────┘ │
│                                     │                                               │
│                    Generates: metrics, audit, usage data                            │
│                                     │                                               │
│  ┌──────────────────────────────────▼─────────────────────────────────────────────┐ │
│  │                        Integration Layer                                        │ │
│  │                                                                                 │ │
│  │  ┌─────────────────────┐  ┌─────────────────────┐  ┌───────────────────────┐   │ │
│  │  │  Sync REST Client   │  │  Async Queue Client │  │  Local Buffer         │   │ │
│  │  │                     │  │                     │  │                       │   │ │
│  │  │  • Provider config  │  │  • Metrics batch    │  │  • Offline cache      │   │ │
│  │  │  • Budget check     │  │  • Audit events     │  │  • Retry queue        │   │ │
│  │  │  • Auth validation  │  │  • Usage streaming  │  │  • Circuit breaker    │   │ │
│  │  └──────────┬──────────┘  └──────────┬──────────┘  └───────────┬───────────┘   │ │
│  │             │                        │                         │               │ │
│  └─────────────┼────────────────────────┼─────────────────────────┼───────────────┘ │
│                │                        │                         │                 │
└────────────────┼────────────────────────┼─────────────────────────┼─────────────────┘
                 │                        │                         │
                 │ HTTP/REST              │ AMQP/SQS                │ (local)
                 │                        │                         │
┌────────────────▼────────────────────────▼─────────────────────────┘
│
│  ┌─────────────────────────────────────────────────────────────────────────────────┐
│  │                         iDEMPIERE SERVER                                         │
│  │                                                                                  │
│  │  ┌────────────────────────────────────────────────────────────────────────────┐ │
│  │  │                      REST API Layer                                         │ │
│  │  │                                                                             │ │
│  │  │  Existing Endpoints:                    New Satellite Endpoints:            │ │
│  │  │  ┌─────────────────────────┐           ┌─────────────────────────────────┐ │ │
│  │  │  │ GET  /api/v1/models     │           │ POST /api/v1/ai/metrics         │ │ │
│  │  │  │ POST /api/v1/models     │           │ POST /api/v1/ai/audit           │ │ │
│  │  │  │ GET  /api/v1/windows    │           │ POST /api/v1/ai/usage           │ │ │
│  │  │  │ POST /api/v1/processes  │           │ GET  /api/v1/ai/providers       │ │ │
│  │  │  └─────────────────────────┘           │ GET  /api/v1/ai/budgets         │ │ │
│  │  │                                        │ POST /api/v1/ai/conversations   │ │ │
│  │  │                                        └─────────────────────────────────┘ │ │
│  │  └────────────────────────────────────────────────────────────────────────────┘ │
│  │                                     │                                           │
│  │  ┌──────────────────────────────────▼──────────────────────────────────────────┐│
│  │  │                      Queue Consumer (Optional)                               ││
│  │  │                                                                              ││
│  │  │  • Listens to satellite.metrics queue                                       ││
│  │  │  • Batch inserts to AIG_UsageMetrics                                        ││
│  │  │  • Handles backpressure                                                     ││
│  │  └──────────────────────────────────────────────────────────────────────────────┘│
│  │                                     │                                           │
│  │  ┌──────────────────────────────────▼──────────────────────────────────────────┐│
│  │  │                      Business Logic Layer                                    ││
│  │  │                                                                              ││
│  │  │  • MAIProvider - Provider configuration                                     ││
│  │  │  • MAIUsageMetrics - Usage recording                                        ││
│  │  │  • MAIBudget - Cost limits                                                  ││
│  │  │  • MAIQueryAudit - Query audit                                              ││
│  │  └──────────────────────────────────────────────────────────────────────────────┘│
│  │                                     │                                           │
│  │  ┌──────────────────────────────────▼──────────────────────────────────────────┐│
│  │  │                      Database Layer                                          ││
│  │  │                                                                              ││
│  │  │  AIG_Provider │ AIG_UsageMetrics │ AIG_Budget │ AIG_QueryAudit              ││
│  │  └──────────────────────────────────────────────────────────────────────────────┘│
│  │                                                                                  │
│  └──────────────────────────────────────────────────────────────────────────────────┘
│
└─────────────────────────────────────────────────────────────────────────────────────

Integration Patterns

Pattern 1: Synchronous REST (Real-time, Low Volume)

Use for: Provider config, budget checks, authentication validation

Satellite                           iDempiere REST API
    │                                      │
    │  GET /api/v1/ai/providers/{id}       │
    │ ────────────────────────────────────>│
    │                                      │
    │  <────────────────────────────────────
    │  { "provider": {...}, "budget": {...}}
    │                                      │
    │  GET /api/v1/ai/budgets/check        │
    │  ?client_id=1000000&amount=0.05      │
    │ ────────────────────────────────────>│
    │                                      │
    │  <────────────────────────────────────
    │  { "allowed": true, "remaining": 95.50 }
    │                                      │

Pattern 2: Async REST (Batched, Medium Volume)

Use for: Metrics, audit logs (batched every N seconds or N records)

Satellite                           iDempiere REST API
    │                                      │
    │  [Accumulate metrics locally]        │
    │                                      │
    │  POST /api/v1/ai/metrics/batch       │
    │  [                                   │
    │    { "model": "claude-sonnet-4",     │
    │      "input_tokens": 150,            │
    │      "output_tokens": 89,            │
    │      "cost_usd": 0.0012,             │
    │      "timestamp": "..." },           │
    │    { ... },                          │
    │    { ... }                           │
    │  ]                                   │
    │ ────────────────────────────────────>│
    │                                      │
    │  <────────────────────────────────────
    │  { "accepted": 50, "failed": 0 }     │
    │                                      │

Pattern 3: Message Queue (High Volume, Async)

Use for: Real-time streaming metrics, high-throughput scenarios

Satellite                    Message Queue              iDempiere Consumer
    │                             │                            │
    │  Publish metrics            │                            │
    │ ───────────────────────────>│                            │
    │                             │  ─────────────────────────>│
    │  Publish metrics            │                            │  Batch insert
    │ ───────────────────────────>│                            │  to DB
    │                             │  ─────────────────────────>│
    │  Publish metrics            │                            │
    │ ───────────────────────────>│                            │
    │                             │  ─────────────────────────>│
    │                             │                            │

Queue Options:

Pattern 4: GraphQL (Future Option)

Use for: Flexible queries, reduced over-fetching, subscriptions

Satellite                           iDempiere GraphQL API
    │                                      │
    │  POST /graphql                       │
    │  query {                             │
    │    provider(id: 1) {                 │
    │      name                            │
    │      endpoint                        │
    │      budget {                        │
    │        dailyLimit                    │
    │        dailyUsed                     │
    │      }                               │
    │    }                                 │
    │  }                                   │
    │ ────────────────────────────────────>│
    │                                      │
    │  <────────────────────────────────────
    │  { "data": { "provider": {...} } }   │
    │                                      │

Benefits:

Pattern 5: WebSocket (Real-time Bidirectional)

Use for: Real-time streaming metrics, live budget updates, bidirectional communication

Satellite                           iDempiere WebSocket
    │                                      │
    │  WS CONNECT /ws/ai                   │
    │ ════════════════════════════════════>│
    │                                      │
    │  Subscribe: budget.updates           │
    │ ────────────────────────────────────>│
    │                                      │
    │  Stream metrics (continuous)         │
    │ ────────────────────────────────────>│
    │ ────────────────────────────────────>│
    │ ────────────────────────────────────>│
    │                                      │
    │  <────────────────────────────────────
    │  Budget alert: 80% used              │
    │                                      │
    │  <────────────────────────────────────
    │  Provider config changed             │
    │                                      │

Benefits:

Use Cases:


Integration Options Comparison

Option Latency Throughput Complexity Real-time Use Case
REST (sync) Medium Low Low No Config, budget checks
REST (batch) Low Medium Low No Metrics batching
Message Queue Very Low Very High Medium No High-volume metrics
GraphQL Medium Medium Medium Subscriptions Flexible queries
WebSocket Very Low High Medium Yes Real-time streaming
┌─────────────────────────────────────────────────────────────────────────┐
│                    INTEGRATION LAYER STRATEGY                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  Phase 1 (MVP):                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  REST API (Sync + Batch)                                        │    │
│  │  • GET  /api/v1/ai/providers - Config fetch                     │    │
│  │  • GET  /api/v1/ai/budgets/check - Budget validation            │    │
│  │  • POST /api/v1/ai/metrics/batch - Batched metrics              │    │
│  │  • POST /api/v1/ai/audit/batch - Batched audit                  │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  Phase 2 (Scale):                                                       │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  + Message Queue (AWS SQS / RabbitMQ)                           │    │
│  │  • High-volume metrics streaming                                 │    │
│  │  • Decoupled processing                                          │    │
│  │  • Guaranteed delivery                                           │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  Phase 3 (Real-time):                                                   │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  + WebSocket                                                     │    │
│  │  • Real-time budget alerts                                       │    │
│  │  • Live metrics dashboard                                        │    │
│  │  • Push config updates                                           │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  Phase 4 (Flexible):                                                    │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  + GraphQL                                                       │    │
│  │  • Complex queries with nested data                              │    │
│  │  • Schema-driven development                                     │    │
│  │  • Subscriptions for real-time                                   │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

New iDempiere REST API Endpoints

Provider Management

GET /api/v1/ai/providers
  description: List active AI providers for client
  query:
    - client_id: int (required)
    - type: string (optional, e.g., "SAT", "ANT")
  response:
    - providers: array of provider configs

GET /api/v1/ai/providers/{id}
  description: Get provider configuration
  response:
    - provider: { id, type, name, endpoint, model_name, ... }
    - budget: { daily_limit, monthly_limit, current_usage }

POST /api/v1/ai/providers/{id}/health
  description: Report provider health status
  body:
    - status: "UP" | "DOWN" | "DEGRADED"
    - latency_ms: int
    - error_message: string (optional)

Budget Management

GET /api/v1/ai/budgets/check
  description: Check if request is within budget
  query:
    - client_id: int
    - estimated_cost: decimal
  response:
    - allowed: boolean
    - daily_remaining: decimal
    - monthly_remaining: decimal
    - warning: string (optional, e.g., "80% of daily budget used")

GET /api/v1/ai/budgets/{client_id}
  description: Get budget configuration and current usage
  response:
    - daily_limit: decimal
    - monthly_limit: decimal
    - daily_used: decimal
    - monthly_used: decimal
    - alert_threshold: decimal

Metrics & Usage

POST /api/v1/ai/metrics
  description: Record single metric (sync)
  body:
    - client_id: int
    - user_id: int
    - model: string
    - input_tokens: int
    - output_tokens: int
    - cost_usd: decimal
    - latency_ms: int
    - session_id: string
    - conversation_id: string
    - success: boolean
    - error_message: string (optional)

POST /api/v1/ai/metrics/batch
  description: Record multiple metrics (batch)
  body:
    - metrics: array of metric objects
  response:
    - accepted: int
    - failed: int
    - errors: array (optional)

GET /api/v1/ai/metrics/summary
  description: Get usage summary
  query:
    - client_id: int
    - from: datetime
    - to: datetime
    - group_by: "day" | "model" | "user"
  response:
    - summary: aggregated metrics

Audit Logging

POST /api/v1/ai/audit
  description: Record audit event
  body:
    - client_id: int
    - user_id: int
    - event_type: "CHAT" | "QUERY" | "TOOL" | "ERROR"
    - model: string
    - input_summary: string (truncated/hashed)
    - output_summary: string (truncated/hashed)
    - tokens: { input, output }
    - metadata: object

POST /api/v1/ai/audit/batch
  description: Record multiple audit events
  body:
    - events: array of audit objects

Conversation Management

POST /api/v1/ai/conversations
  description: Create or update conversation
  body:
    - conversation_id: string
    - client_id: int
    - user_id: int
    - title: string (optional, AI-generated)
    - window_id: int (optional)
    - record_id: int (optional)

GET /api/v1/ai/conversations/{id}
  description: Get conversation details
  response:
    - conversation: { id, title, created, updated, message_count }

GET /api/v1/ai/conversations/{id}/messages
  description: Get conversation messages (for context reload)
  query:
    - limit: int (default 50)
  response:
    - messages: array of { role, content, timestamp }

Satellite Integration Layer

REST Client Service

@ApplicationScoped
public class IdempiereRestClient {

    @ConfigProperty(name = "idempiere.api.base-url")
    String baseUrl;

    @ConfigProperty(name = "idempiere.api.token")
    String apiToken;

    @Inject
    @RestClient
    IdempiereAiApi aiApi;

    /**
     * Get provider configuration (cached).
     */
    @CacheResult(cacheName = "providers")
    public ProviderConfig getProvider(int providerId) {
        return aiApi.getProvider(providerId);
    }

    /**
     * Check budget before processing request.
     */
    public BudgetCheckResult checkBudget(int clientId, BigDecimal estimatedCost) {
        return aiApi.checkBudget(clientId, estimatedCost);
    }

    /**
     * Record metrics (async, batched).
     */
    @Asynchronous
    public void recordMetrics(UsageMetrics metrics) {
        metricsBuffer.add(metrics);
        if (metricsBuffer.size() >= BATCH_SIZE || shouldFlush()) {
            flushMetrics();
        }
    }

    /**
     * Flush buffered metrics to iDempiere.
     */
    @Scheduled(every = "10s")
    void flushMetrics() {
        if (metricsBuffer.isEmpty()) return;

        List<UsageMetrics> batch = metricsBuffer.drain();
        try {
            aiApi.recordMetricsBatch(batch);
        } catch (Exception e) {
            // Add to retry queue
            retryQueue.addAll(batch);
            log.warn("Failed to flush metrics, queued for retry", e);
        }
    }
}

REST Client Interface

@Path("/api/v1/ai")
@RegisterRestClient(configKey = "idempiere-api")
public interface IdempiereAiApi {

    @GET
    @Path("/providers/{id}")
    ProviderConfig getProvider(@PathParam("id") int providerId);

    @GET
    @Path("/budgets/check")
    BudgetCheckResult checkBudget(
        @QueryParam("client_id") int clientId,
        @QueryParam("estimated_cost") BigDecimal cost);

    @POST
    @Path("/metrics/batch")
    BatchResult recordMetricsBatch(List<UsageMetrics> metrics);

    @POST
    @Path("/audit/batch")
    BatchResult recordAuditBatch(List<AuditEvent> events);
}

Queue Publisher (Optional)

@ApplicationScoped
public class MetricsQueuePublisher {

    @Inject
    @Channel("chat-api-metrics")
    Emitter<UsageMetrics> metricsEmitter;

    @ConfigProperty(name = "satellite.queue.enabled", defaultValue = "false")
    boolean queueEnabled;

    public void publish(UsageMetrics metrics) {
        if (queueEnabled) {
            metricsEmitter.send(metrics);
        }
    }
}

Configuration

Satellite application.properties

# ==================== iDempiere REST API Integration ====================

# Base URL of iDempiere REST API
idempiere.api.base-url=${IDEMPIERE_API_URL:http://localhost:8080}

# API authentication token
idempiere.api.token=${IDEMPIERE_API_TOKEN:}

# REST client configuration
quarkus.rest-client.idempiere-api.url=${idempiere.api.base-url}
quarkus.rest-client.idempiere-api.scope=singleton

# Connection pool
quarkus.rest-client.idempiere-api.connect-timeout=5000
quarkus.rest-client.idempiere-api.read-timeout=30000

# ==================== Metrics Integration ====================

# Integration mode: "rest" | "queue" | "both"
satellite.metrics.integration-mode=rest

# Batch settings (for REST mode)
satellite.metrics.batch-size=50
satellite.metrics.flush-interval=10s

# Retry settings
satellite.metrics.retry.max-attempts=3
satellite.metrics.retry.delay=5s

# Local buffer (for offline resilience)
satellite.metrics.buffer.max-size=10000
satellite.metrics.buffer.persist-on-shutdown=true

# ==================== Queue Integration (Optional) ====================

# Enable queue-based metrics
satellite.queue.enabled=false
satellite.queue.type=sqs

# AWS SQS
%sqs.quarkus.sqs.endpoint-override=${AWS_SQS_ENDPOINT:}
%sqs.quarkus.sqs.aws.region=${AWS_REGION:us-east-1}
satellite.queue.sqs.metrics-queue=${SQS_METRICS_QUEUE:chat-api-metrics}

# RabbitMQ
%rabbitmq.mp.messaging.outgoing.chat-api-metrics.connector=smallrye-rabbitmq
%rabbitmq.mp.messaging.outgoing.chat-api-metrics.queue.name=chat-api-metrics

# ==================== Cache Settings ====================

# Provider cache (from iDempiere)
quarkus.cache.caffeine.providers.expire-after-write=5m
quarkus.cache.caffeine.providers.maximum-size=100

# Budget cache (short TTL for accuracy)
quarkus.cache.caffeine.budgets.expire-after-write=30s
quarkus.cache.caffeine.budgets.maximum-size=50

Sequence Diagrams

Chat Request with REST Integration

┌──────────────┐    ┌─────────────────┐    ┌─────────────────┐    ┌─────────────┐
│  iDempiere   │    │   Satellite     │    │  iDempiere      │    │   Claude    │
│  Plugin      │    │   Service       │    │  REST API       │    │   API       │
└──────┬───────┘    └────────┬────────┘    └────────┬────────┘    └──────┬──────┘
       │                     │                      │                     │
       │  POST /v1/chat/completions                 │                     │
       │  X-iDempiere-Client-ID: 1000000            │                     │
       │ ───────────────────>│                      │                     │
       │                     │                      │                     │
       │                     │  GET /api/v1/ai/budgets/check              │
       │                     │  ?client_id=1000000&estimated_cost=0.05    │
       │                     │ ────────────────────>│                     │
       │                     │                      │                     │
       │                     │  <────────────────────                     │
       │                     │  { "allowed": true }  │                     │
       │                     │                      │                     │
       │                     │  [Process with Claude]│                     │
       │                     │ ─────────────────────────────────────────>│
       │                     │                      │                     │
       │                     │  <─────────────────────────────────────────
       │                     │                      │                     │
       │  <───────────────────                      │                     │
       │  { "choices": [...] }                      │                     │
       │                     │                      │                     │
       │                     │  [Async] POST /api/v1/ai/metrics           │
       │                     │  { tokens, cost, latency }                 │
       │                     │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>│                     │
       │                     │                      │                     │

High-Volume Metrics via Queue

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐    ┌─────────────┐
│   Satellite     │    │  Message Queue  │    │  iDempiere      │    │  Database   │
│   Service       │    │  (SQS/RabbitMQ) │    │  Consumer       │    │             │
└────────┬────────┘    └────────┬────────┘    └────────┬────────┘    └──────┬──────┘
         │                      │                      │                     │
         │  Publish metric 1    │                      │                     │
         │ ────────────────────>│                      │                     │
         │                      │                      │                     │
         │  Publish metric 2    │                      │                     │
         │ ────────────────────>│                      │                     │
         │                      │                      │                     │
         │  Publish metric 3    │  [Batch consume]     │                     │
         │ ────────────────────>│ ────────────────────>│                     │
         │                      │                      │                     │
         │                      │                      │  Batch INSERT       │
         │                      │                      │ ───────────────────>│
         │                      │                      │                     │
         │                      │                      │  <───────────────────
         │                      │                      │  Committed          │
         │                      │                      │                     │
         │                      │  <────────────────────                     │
         │                      │  ACK                 │                     │
         │                      │                      │                     │

Comparison: Direct DB vs REST API

Aspect Direct Database REST API
Coupling Tight (schema dependent) Loose (API contract)
Security DB credentials needed API token only
Business Logic Bypassed Enforced
Schema Changes Breaking Versioned API
Multi-tenant Manual AD_Client_ID Automatic via token
Offline Support None Local buffer + retry
Scalability Limited by DB connections Horizontal (API + queue)
Observability DB metrics only API metrics + tracing
Deployment Same network required Can be remote

Migration Impact

Changes to ADR-044

Remove database entity layer from extraction:

- ### Layer 4: Database Access
-
- **Extraction: Replace iDempiere PO with Quarkus patterns**
-
- | iDempiere (X_/M*) | Quarkus (Panache/JPA) |
- |-------------------|----------------------|
- | `X_AIG_Provider` | `AigProvider` entity |
- | `MAIProvider` | `AigProviderRepository` |

+ ### Layer 4: REST API Integration
+
+ **New: REST client for iDempiere API**
+
+ | Data | REST Endpoint |
+ |------|---------------|
+ | Provider config | GET /api/v1/ai/providers/{id} |
+ | Budget check | GET /api/v1/ai/budgets/check |
+ | Metrics | POST /api/v1/ai/metrics/batch |
+ | Audit | POST /api/v1/ai/audit/batch |

Changes to ADR-045

Update package structure:

  └── satellite/
-     ├── model/
-     │   ├── AigProvider.java             # Provider config entity
-     │   ├── AigUsageMetrics.java         # Usage tracking entity
-     │   └── AigProviderRepository.java   # Panache repository
+     ├── client/
+     │   ├── IdempiereAiApi.java          # REST client interface
+     │   ├── IdempiereRestClient.java     # REST client service
+     │   └── dto/
+     │       ├── ProviderConfig.java      # Provider response DTO
+     │       ├── BudgetCheckResult.java   # Budget check response
+     │       └── BatchResult.java         # Batch operation result
+     │
+     ├── queue/
+     │   ├── MetricsQueuePublisher.java   # Queue publisher
+     │   └── MetricsBuffer.java           # Local buffer

Consequences

Positive

Negative

Risks & Mitigations

Risk Mitigation
iDempiere API unavailable Local buffer + retry queue
High latency for budget checks Aggressive caching (30s TTL)
Queue message loss Persistent queues, acknowledgments
API rate limiting Batching, exponential backoff

Implementation Phases

Phase A: REST Client (Week 3-4)

  1. Create REST client interface
  2. Implement provider config fetching
  3. Implement budget checking
  4. Add caching layer

Phase B: Async Metrics (Week 4-5)

  1. Implement metrics buffering
  2. Add batch REST endpoint calls
  3. Implement retry logic
  4. Add circuit breaker

Phase C: Queue Integration (Optional, Week 6+)

  1. Add queue publisher
  2. Implement iDempiere consumer
  3. Configure queue infrastructure
  4. Performance testing


ADR-046 | Version 1.0 | 2025-12-10 Status: Proposed

Path: /docs/developers/architecture/idempiere-hub/046-chat-api-idempiere-rest-integration