Skip to content

ARIA Dialogue

Status: 🚧 Partial β€” ARIA chat is reachable, sanitized, and per-player-isolated, but its replies are deterministic intent-templates with NO LLM call β€” the documented provider chain, encrypted-memory writes … Β· ⚠︎ contains code↔spec divergence (impl audit 2026-06-16)

Purpose

ARIA is the per-player AI assistant. The dialogue system is the pipeline that turns a player message into a sanitized, language-aware, context-rich prompt; routes it through a fallback-equipped multi-provider LLM stack; sanitizes the response; logs the exchange; and pushes the assistant reply back over the realtime bus. Every step is auditable; every step has a deterministic fallback so the assistant is never fully unavailable.

Inputs

The pipeline reads: - The raw player message body. - The player's User/Player row (locale, aria_consciousness_level, aria_relationship_score, blocked status). - The player's ARIAPersonalMemory, ARIAExplorationMap, ARIAMarketIntelligence, and recent ARIATradingObservation rows (top-K by recency or aggregate-relevance). - Current session context: sector, ship, recent dialogue exchanges (last N turns). - AI provider config (env): AI_PROVIDER_PRIMARY, AI_PROVIDER_SECONDARY, AI_PROVIDER_FALLBACK. - Security config: rate limits, max chars, cost cap, blocked patterns.

The system fires on: - POST /api/v1/ai/chat β€” direct chat send. - POST /api/v1/ai/recommendations β€” request a recommendation (system-style query). - WebSocket aria:send command on the realtime bus. - First-login dialogue (specialized variant β€” see first-login.md). - Tool-result callbacks (when the model invokes a tool and the tool returns).

Process

Player message
   β”‚
   β–Ό
[1] Client-side sanitize (DOMPurify, length cap)
   β”‚  Server-side: build dialogue context with auth_snapshot
   β”‚  β€’ subscription_tier        (User.subscription_tier at this instant)
   β”‚  β€’ aria_consciousness_level (Player.aria_consciousness_level)
   β”‚  β€’ aria_bonus_multiplier    (Player.aria_bonus_multiplier)
   β”‚  Per ADR-0058 A-F1, all subsequent steps read these from the
   β”‚  snapshot, never from the live row. A subscription upgrade or
   β”‚  consciousness level-up landing mid-exchange takes effect on
   β”‚  the NEXT exchange β€” the in-flight one completes consistently.
   β”‚
   β–Ό
[2] Server input gate (ai_security_service)
   β”‚  β€’ length / word cap
   β”‚  β€’ injection patterns (script, sql, dunder, eval)
   β”‚  β€’ prompt-injection patterns (25+ regexes)
   β”‚  β€’ jailbreak patterns (β‰₯2 indicators)
   β”‚  β€’ token-burning (>30% repetition)
   β”‚  β€’ rate limit (req/min, /day β€” per-hour retired per AU2-18)
   β”‚  β€’ cost cap (USD/day, USD/request)
   β”‚  β†’ on hit: log SecurityViolation, penalize trust, possibly block
   β”‚
   β–Ό
[3] Intent classification
   β”‚  β€’ lightweight classifier (rule + small model)
   β”‚  β€’ routes: chat | trade-advice | combat-advice | explore | market-query
   β”‚           | colony-advice | meta (memory recall)
   β”‚
   β–Ό
[4] Multilingual context build (multilingual_ai_service)
   β”‚  β€’ translation_service.get_user_language_preference(user_id)
   β”‚  β€’ cultural-context tag (western/hispanic/chinese/...)
   β”‚  β€’ cultural guidelines (tone, formality, humor, authority)
   β”‚  β†’ response_language fixed; instruction-stream tagged
   β”‚
   β–Ό
[5] Prompt assembly
   β”‚  β€’ system header (role, persona, language directive)
   β”‚  β€’ personal memory snippets (top-K, decay-weighted)
   β”‚  β€’ exploration data slice (sectors visited, ports known)
   β”‚  β€’ market intelligence slice (relevant commodities)
   β”‚  β€’ last N dialogue turns
   β”‚  β€’ the (sanitized) player message embedded in a JSON envelope
   β”‚       so injected text lands inside a data field, not the
   β”‚       instruction stream
   β”‚  β€’ tool definitions (market_lookup, sector_info, route_plan, ...)
   β”‚
   β–Ό
[6] Provider chain (ai_provider_service)
   β”‚   primary β†’ secondary β†’ manual fallback
   β”‚   each step has a hard timeout; chain advances on failure or 5xx
   β”‚
   β–Ό
[7] Tool invocation loop
   β”‚  while response.tool_call:
   β”‚    β€’ validate tool args (schema)
   β”‚    β€’ run tool (read-only handler β€” market query, sector lookup, ...)
   β”‚    β€’ feed result back as a follow-up turn
   β”‚    β€’ cap at 3 tool turns to avoid infinite loops
   β”‚
   β–Ό
[8] Response sanitize
   β”‚  β€’ strip control characters
   β”‚  β€’ cap length
   β”‚  β€’ run output filters (no PII echo, no other-player private data)
   β”‚  β€’ DOMPurify on the client before render
   β”‚
   β–Ό
[9] Persist
   β”‚  β€’ ARIAPersonalMemory row (encrypted) for the exchange
   β”‚  β€’ DialogueExchange row in chat history
   β”‚  β€’ ARIASecurityLog if any anomaly
   β”‚  β€’ increment Player.aria_total_interactions
   β”‚  β€’ check consciousness threshold (50/150/400/1000) and bump
   β”‚    aria_consciousness_level + aria_bonus_multiplier
   β”‚
   β”‚  Multiplier semantics (per ADR-0057 A-D2): the multiplier applies
   β”‚  CONTINUOUSLY on every ARIA interaction β€” read on each call to
   β”‚  scale recommendation strength, narration richness, observation
   β”‚  depth. Level transitions are atomic boundary events; the
   β”‚  multiplier itself is not gated to those transitions.
   β”‚
   β–Ό
[10] Push (realtime-bus)
   β”‚  β€’ aria_message event (personal:{user_id})
   β”‚  β€’ turn_pool_updated if multiplier changed

Provider chain detail

AI_PROVIDER_PRIMARY=openai
AI_PROVIDER_SECONDARY=anthropic
AI_PROVIDER_FALLBACK=manual    # rule-based, always available

Each provider exposes a uniform analyze(...) / generate(...) API. Selection is per-request; a provider failure (timeout, 5xx, content filter) advances the chain. The manual provider (enhanced_manual_provider.py) replicates the response shape with rule-based pattern matching so the assistant is never offline.

Rate / cost controls

requests_per_minute   = 10     (env: ARIA_RPM)         # anti-burst cap
requests_per_day      = 500    (env: ARIA_RPD)         # long-term ceiling
max_cost_per_day_usd  = 2.00   (env: ARIA_DAILY_USD)   # per-player daily spend cap
max_cost_per_request  = 0.05   (env: ARIA_REQ_USD)     # per-request hard ceiling
max_chars_per_request = 500
max_words_per_request = 100

Plus a per-instance circuit breaker: instance_max_cost_per_day_usd = 50.00 (env: ARIA_INSTANCE_DAILY_USD) β€” defense-in-depth backstop on aggregate spend across all players. Per-hour cap retired (AU2-18) β€” was dominated by per-minute math. When daily spend hits 80% of the per-player limit, the player is blocked for the rest of the UTC day with ERR_DAILY_BUDGET_EXHAUSTED.

Trust scoring

Each player starts with trust = 1.0. Penalties: - Injection attempt: βˆ’0.3 - Prompt injection: βˆ’0.2 - Jailbreak: βˆ’0.4 - System command: βˆ’0.5 - Rate-limit hit: βˆ’0.1

Severe violations auto-block for 24 h (progressive: 1 h β†’ 6 h β†’ 24 h on repeats).

Multilingual routing

The user's language preference (User.locale) drives: - Response language directive in the prompt. - Cultural guideline injection (tone, formality, humor, authority). - Pre-translated UI strings used in tool outputs (see translation_service).

Fallback to en if the user has no preference or the preference is unsupported.

Outputs / state changes

Per dialogue turn: - ARIAPersonalMemory β€” encrypted memory entry (importance score, content hash for dedup). - DialogueExchange (or equivalent) β€” chat-history row. - ARIASecurityLog β€” if any security event fired. - Player.aria_total_interactions β€” incremented. - Player.aria_consciousness_level, Player.aria_bonus_multiplier β€” bumped on threshold crossings. - User.trust_score β€” adjusted per any violation.

Events emitted: - aria_message β€” personal unicast with the assistant's reply. - turn_pool_updated β€” if multiplier changed (downstream of consciousness bump). - aria_security_alert β€” admin feed, on dangerous violations. - notification β€” personal, if a tool surfaced an alert (e.g. price hit).

Downstream: - Memory writes feed future ARIA queries (recall in step 5). - Security log feeds the admin dashboard.

Invariants

  1. Every player input is rate-limited and sanitized before reaching any provider.
  2. The player's input is never inlined directly into the instruction stream β€” it lives in a JSON data field.
  3. Each ARIA instance is per-player; no cross-player data is read or written.
  4. Memories are encrypted at rest (Fernet/AES-256).
  5. Provider failure never produces an unhandled exception to the user β€” the chain falls through to manual fallback.
  6. Tool-call loops cap at 3 iterations; runaway prevented.
  7. Cost caps are enforced before the provider call, not after.
  8. Audit trail (ARIASecurityLog) is written for every detected violation and every blocked request.
  9. Output is sanitized on both server and client (defense in depth: server strip + client DOMPurify).

Failure modes

Mode Target handling
All providers down Manual fallback delivers a rule-based response; user sees a degraded-mode banner.
Provider returns malformed JSON Validator catches; chain advances; fallback if exhausted.
Tool call to unknown tool Reject with explicit error; do not advance chain (model bug, not provider failure).
Player rate-limited Return 429; suggest cooldown; emit aria_rate_limited.
Cost cap reached Block until next UTC day; admin can lift via security action.
Injection / jailbreak Block message, log, penalize trust; reply with a canned safety message.
Translation lookup fails Fall back to English directive; log the locale that missed.
Memory decryption fails (key rotation, corruption) Skip that memory snippet; continue with reduced context; log.
Realtime bus push fails Reply still persisted; client picks up via REST history on next open.
First-login variant during normal chat (mistaken context) First-login flow is a separate handler (see first-login.md); cross-routing rejected at intent classification.

Source map

Concern Path (target)
Dialogue orchestration services/gameserver/src/services/ai_dialogue_service.py
Multi-provider client services/gameserver/src/services/ai_provider_service.py
Manual fallback provider services/gameserver/src/services/enhanced_manual_provider.py
Multilingual routing services/gameserver/src/services/multilingual_ai_service.py
Translation lookups services/gameserver/src/services/translation_service.py
Personal memory + tool data services/gameserver/src/services/aria_personal_intelligence_service.py
Trade-DNA / market intelligence services/gameserver/src/services/ai_trading_service.py
Security gate services/gameserver/src/services/ai_security_service.py
Audit log model services/gameserver/src/models/aria_personal_intelligence.py:ARIASecurityLog
Encrypted memory model services/gameserver/src/models/aria_personal_intelligence.py:ARIAPersonalMemory
Chat / recommendation routes services/gameserver/src/api/routes/ai.py, enhanced_ai.py
Admin security routes services/gameserver/src/api/routes/admin_comprehensive.py
Player-client UI services/player-client/src/components/ai/EnhancedAIAssistant.tsx