Domain · Specifications

Psyche Domain — Technical Specifications

Psyche is implemented as a polyglot domain:

20sections35 minread

On this page

Technical specification for the Psyche Hyper-Realistic AI Virtual Assistant Platform. This document describes what is implemented in code: the API Gateway request/response models, enums, the session-orchestration state machine, WebSocket protocol, service topology, configuration, and persistence bootstrap.

Grounding scope. Every schema, field, enum value, endpoint, and event below is taken from source under services/psyche/, apps/psyche/admin/, and libs/contracts/psyche/. Where a concept is named in code only as a directory or interface without a concrete persisted schema, it is described at that level and not embellished. The API Gateway currently keeps all resources in per-process in-memory dictionaries — there is no relational persistence of personas/sessions/knowledge/tools/webhooks yet (see §10).

This document is the authoritative reference for engineers working on Psyche services. It maps exactly to what exists in source: model fields, enum values, endpoint paths, and configuration keys. Where the current implementation diverges from the intended design (for example, in-memory storage instead of a real database), those gaps are called out explicitly in notes. Use this alongside architecture.md (system topology and ML pipelines) and features.md (user-facing capabilities).


1. Implementation Status#

Psyche is implemented as a polyglot domain:

  • Python services — 16 services under services/psyche/, each a Poetry package with pyproject.toml, project.json, src/, and tests/. The workspace root services/psyche/pyproject.toml declares every service as a path dependency. The framework is FastAPI on Python 3.11+.
  • TypeScript libraries — 135 library directories under libs/psyche/ (predominantly TypeScript: ~1,690 .ts files vs ~130 .py files), each with package.json, project.json, tsconfig.json, tsup.config.ts, and vitest.config.ts.
  • Admin web appapps/psyche/admin, a Next.js application.
  • Contractslibs/contracts/psyche (@psyche/contracts), a Zod-based shared-types package. Only the common module is implemented; per-domain contract modules are declared in package.json exports but their files do not yet exist (see §18).

The branding Serwaa is the former codename and still appears in API titles, default config values, MinIO/storage prefixes, and webhook examples.


2. Code Layout#

The source tree below shows every deployable service and the admin application. Note that two service directories (conferencing/, persona-service/) ship Python package names that differ from their directory names — this is called out explicitly below the tree.

text
services/psyche/
  api-gateway/        FastAPI REST + WebSocket gateway (psyche-api-gateway)
  orchestrator/       Session lifecycle, pipeline routing, resources (psyche-orchestrator)
  persona-service/    Persona identity/personality/expertise/memory (psyche-persona-service)
  avatar-engine/      3DGS / NeRF rendering, FLAME, training (psyche-avatar-engine)
  voice-engine/       TTS, STT, diarization, visemes, turn-taking (psyche-voice-engine)
  behavior-engine/    (psyche-behavior-engine) — service shell, src/ empty
  perception-engine/  Face/emotion/intent/screen perception (psyche-perception-engine)
  computer-use/       Browser/desktop automation agent (psyche-computer-use)
  conferencing/       Zoom/Teams/Meet/Webex/WebRTC adapters (psyche-conferencing)
  video-conferencing/ (psyche-video-conferencing)
  knowledge-base/     Ingestion, embedding, RAG, retrieval (psyche-knowledge-base)
  learning-system/    (psyche-learning-system)
  tool-framework/     Tool registry, router, MCP server (psyche-tool-framework)
  tavus-integration/  (psyche-tavus-integration)
  security/           (psyche-security)
  error-handling/     (psyche-error-handling)

apps/psyche/admin/    Next.js admin dashboard
libs/psyche/*         135 TypeScript libraries
libs/contracts/psyche @psyche/contracts (Zod)

The Python package name inside each service does not always match the directory name: conferencing/ ships the video_conferencing package, and persona-service/ ships both a persona_service package (FastAPI app) and a persona_system package (the domain logic).


3. API Gateway Data Models#

All API Gateway request/response models are Pydantic v2 models. They live in services/psyche/api-gateway/src/api_gateway/models/. Every model inherits from BaseSchema, which sets strict=True, from_attributes=True, validate_assignment=True, use_enum_values=True, populate_by_name=True, and extra="forbid". Response models additionally mix in TimestampMixin (created_at, updated_at).

3.1 Persona Models (models/personas.py)#

PersonalityConfig#

PersonalityConfig is the API Gateway's flat personality representation. It is not the OCEAN Big Five model — that richer model lives in the Persona Service (see §9). The API uses a simpler six-trait flat set for request/response serialization.

Field Type Default Range Meaning
formality float 0.5 0.0–1.0 0 = casual, 1 = formal
friendliness float 0.7 0.0–1.0 0 = reserved, 1 = warm
verbosity float 0.5 0.0–1.0 0 = concise, 1 = detailed
humor float 0.3 0.0–1.0 0 = serious, 1 = playful
empathy float 0.7 0.0–1.0 0 = neutral, 1 = empathetic
assertiveness float 0.5 0.0–1.0 0 = passive, 1 = assertive

VoiceConfig#

VoiceConfig specifies the TTS provider and voice parameters attached to a persona.

Field Type Default Constraints Meaning
voice_id str "default" maxlen 100 TTS voice identifier
provider str "elevenlabs" TTS provider name
speed float 1.0 0.5–2.0 Speech-speed multiplier
pitch float 1.0 0.5–2.0 Pitch adjustment
custom_voice_enabled bool false Whether voice cloning is on

AvatarConfig#

AvatarConfig links a persona to a trained avatar model and controls the visual presentation.

Field Type Default Meaning
model_id str | null null Avatar model identifier
style str "realistic" Avatar style
background str "default" Background configuration
camera_position str "center" Camera position preset

PersonaCreate (request)#

PersonaCreate is the body for POST /api/v1/personas. Every field except name has a default.

Field Type Constraints Notes
name str minlen 1, maxlen 100 Required
description str maxlen 1000, default ""
system_prompt str maxlen 10000, default "" LLM system prompt text
personality PersonalityConfig default factory
voice VoiceConfig default factory
avatar AvatarConfig default factory
expertise list[str] maxlen 20 Lower-cased and trimmed by a field validator
languages list[str] maxlen 10, default ["en"] Each must be a 2-letter ISO 639-1 code
metadata dict[str, Any] default {}

PersonaUpdate is the same field set with every field optional, plus is_active: bool | null. There is no personality/voice/avatar-equivalent of the OCEAN model, no jobTitle, company, shortBio, topicBoundaries, tools, or profilePhotoUrl field on these API models.

PersonaResponse#

PersonaResponse is the shape returned by all persona read endpoints. It adds identity and lifecycle fields on top of the PersonaCreate fields.

Adds id (string, format persona_NNNNNNNN), organization_id, is_active: bool, training_status: str (default "not_started"), plus created_at/updated_at. The full PersonalityConfig, VoiceConfig, and AvatarConfig are embedded.

3.2 Session Models (models/sessions.py)#

SessionCreate (request)#

SessionCreate is the body for POST /api/v1/sessions. The platform field can be omitted — it is auto-detected from the meeting URL when possible.

Field Type Constraints Notes
persona_id str minlen 1, maxlen 50 Required
meeting_url str | null maxlen 500 Validated to start with http:///https://
platform MeetingPlatform | null Auto-detected from meeting_url if omitted
config dict[str, Any] default {}
metadata dict[str, Any] default {}

JoinMeetingRequest carries a required meeting_url, optional platform, and optional display_name (maxlen 100).

SessionResponse#

SessionResponse is the shape returned for all session read operations. The duration_seconds field is computed when the session ends.

Field Type Notes
id str Format session_NNNNNNNN
persona_id str
status SessionStatus
meeting_url str | null
platform MeetingPlatform | null
started_at datetime | null
ended_at datetime | null
duration_seconds int | null >= 0; computed at end
message_count int >= 0, default 0
config dict[str, Any]
metadata dict[str, Any]
created_at / updated_at datetime

SessionMetrics aggregates total_sessions, active_sessions, avg_duration_seconds, total_messages. SessionListFilters defines query filters by status, persona_id, platform, started_after, started_before.

3.3 Knowledge Models (models/knowledge.py)#

KnowledgeDocumentCreate represents a document being added to the knowledge base — either with inline content or a source_url to fetch from.

KnowledgeDocumentCreate: title (1–200), content (str | null, maxlen 1,000,000), source_url (str | null, maxlen 500, HTTP(S)-validated), document_type (DocumentType, default text), persona_ids: list[str], tags: list[str] (maxlen 20, normalized), metadata.

KnowledgeDocumentResponse: adds id (doc_NNNNNNNN), organization_id, status (DocumentStatus), chunk_count, token_count, size_bytes, error_message, plus timestamps.

KnowledgeSearchRequest: query (1–1000), optional persona_id, optional tags, top_k (1–20, default 5). KnowledgeSearchResult: document_id, document_title, chunk_id, content, score (0–1), metadata. KnowledgeSearchResponse: query, results, total_results.

The gateway's search handler currently performs case-insensitive title matching with a fixed score of 0.85 and does not query a vector store. The Qdrant-backed semantic path is implemented in services/psyche/knowledge-base (see §15) but is not wired into the gateway endpoint.

3.4 Tool Models (models/tools.py)#

Tools define callable integrations that personas can invoke mid-conversation. The ToolParameter model describes a single typed input parameter; ToolCreate assembles those parameters into a registered tool definition.

ToolParameter: name (1–100), type (ParameterType), description (maxlen 500), required: bool, default: Any, enum: list[str] | null, items: dict | null, properties: dict | null.

ToolCreate: name (1–100, must be snake_case, not digit-leading, lower-cased), description (1–1000), tool_type (ToolType, default function), parameters: list[ToolParameter], endpoint_url / mcp_server_url (str | null, maxlen 500, HTTP(S)-validated), auth_type (str | null), persona_ids, timeout_seconds (1–300, default 30), retry_config: dict, metadata.

ToolResponse: adds id (tool_NNNNNNNN), organization_id, is_active: bool, execution_count, success_rate (0–1, default 1.0), avg_latency_ms, plus timestamps.

ToolTestRequest/ToolTestResponse model the /test endpoint: success: bool, result: Any, error: str | null, latency_ms.

3.5 Webhook Models (models/webhooks.py)#

Webhooks enable external systems to receive HTTP callbacks when Psyche events occur. The WebhookCreate model enforces HTTPS for security. A signing secret is auto-generated if not provided; only the first 10 characters are returned in responses to avoid exposing the full secret.

WebhookCreate: url (maxlen 500, must be HTTPS), events: list[WebhookEvent] (minlen 1), description (maxlen 500), secret: str | null (maxlen 200, auto-generated as whsec_<token> if absent), headers: dict[str, str], enabled: bool, metadata.

WebhookResponse: id (webhook_NNNNNNNN), organization_id, url, events, description, status (WebhookStatus), headers, secret_prefix (first 10 chars of the signing secret), delivery_stats: WebhookDeliveryStats, metadata, timestamps.

WebhookDeliveryStats: total_deliveries, successful_deliveries, failed_deliveries, success_rate (0–1), avg_latency_ms, last_delivery_at, last_success_at, last_failure_at.

WebhookDelivery: id, webhook_id, event_type, status_code: int | null, success: bool, attempts (>= 1), latency_ms, error: str | null, request_headers, response_body: str | null, created_at.

3.6 Base / Envelope Models (models/base.py)#

These generic envelope models wrap all API responses for consistency. Every success response is either SuccessResponse[T] (single item) or PaginatedResponse[T] (list); errors always follow ErrorResponse.

  • SuccessResponse[T]{ data: T, meta: dict }.
  • PaginatedResponse[T]{ data: list[T], meta: PaginationMeta }.
  • PaginationMetatotal, page, per_page (1–100), total_pages, has_next, has_prev.
  • ErrorResponse{ error: ErrorDetail } where ErrorDetail is { code, message, details }.
  • DeleteResponse{ deleted: bool, id: str }.

4. Enumerations#

All enums are string enums unless noted. Sources are cited per enum. A key thing to be aware of: the SessionStatus enum is defined independently in three places — the API Gateway, the Orchestrator, and the shared Contracts package — and the three sets of values do not match. This is an intentional layering decision (each service models its own lifecycle state), but it means you must be careful which type you are using in any given context.

API Gateway#

These enums are defined in the API Gateway models and control the values accepted in REST request bodies and returned in responses.

Enum Values Source
SessionStatus pending, connecting, active, paused, ended, error models/sessions.py
MeetingPlatform zoom, meet, teams, webex, custom models/sessions.py
DocumentStatus pending, processing, ready, error models/knowledge.py
DocumentType text, pdf, word, html, markdown, csv, json models/knowledge.py
ToolType function, api, mcp, webhook models/tools.py
ParameterType string, number, integer, boolean, array, object models/tools.py
WebhookEvent see below models/webhooks.py
WebhookStatus active, paused, disabled models/webhooks.py

WebhookEvent values: session.started, session.ended, session.error, conversation.message, conversation.turn_started, conversation.turn_ended, meeting.joined, meeting.left, meeting.participant_joined, meeting.participant_left, tool.executed, tool.error, error.occurred, persona.updated.

WebSocket (api_gateway/websocket/models.py)#

These enums govern the real-time WebSocket protocol. WSMessageType separates the client-to-server and server-to-client message shapes; WSEventType is the set of session-level events clients can subscribe to; CommandType is the set of control commands clients can issue.

Enum Values
WSMessageType subscribe, unsubscribe, command, ping (client→server); event, transcript, metrics, error, ack, pong (server→client)
WSEventType session.started/ended/paused/resumed/error/state_changed; meeting.joining/joined/left/participant_joined/participant_left/audio_started/video_started; conversation.turn_started/turn_ended/message; ai.thinking/speaking/listening/idle; tool.executing/completed/error; emotion.detected; sentiment.changed; error.occurred
CommandType session.start/pause/resume/end; audio.mute/unmute; video.enable/disable; screen.start/stop; ai.interrupt/set_mode; tool.execute/cancel; persona.switch; events.subscribe/unsubscribe

Orchestrator (orchestrator/session/types.py)#

The Orchestrator has its own richer SessionStatus enum that covers internal lifecycle states (initializing, resuming, terminating) not exposed at the API layer. The ConversationPhase enum tracks where in the conversation arc the session currently sits.

Enum Values
SessionStatus initializing, active, paused, resuming, terminating, terminated, failed, timed_out
SessionType video_call, voice_call, screen_share, chat, hybrid
SessionPriority LOW=1, NORMAL=2, HIGH=3, CRITICAL=4 (int enum)
StateType conversation, emotion, tool, screen_share, participant, context
ConversationPhase greeting, discovery, main, resolution, closing
EmotionState neutral, happy, sad, angry, confused, frustrated, engaged, distracted

The orchestrator's SessionStatus is a distinct enum from the API Gateway's. The two services model session lifecycle independently and are not currently wired together.

Persona Service (persona-service/src/persona_system/)#

The Persona Service has the richest set of enums in the domain, covering every dimension of persona identity, behavior, memory, and asset lifecycle. They are grouped below by their source module within persona_system.

Enum Values Module
PersonaStatus draft, active, inactive, archived, training identity
PersonaRole assistant, sales_rep, support_agent, receptionist, instructor, consultant, interviewer, custom identity
GenderPresentation masculine, feminine, neutral, custom identity
AgeRange young_adult, adult, mature, senior identity
CommunicationStyle formal, professional, casual, friendly, technical, empathetic personality
EmotionalExpressiveness reserved, moderate, expressive personality
ResponseLength concise, moderate, detailed, comprehensive personality
ExpertiseLevel novice, beginner, intermediate, advanced, expert expertise
KnowledgeBoundaryAction acknowledge, defer, escalate, redirect, attempt expertise
EscalationTrigger low_confidence, out_of_domain, user_request, repeated_failure, sensitive_topic, high_stakes, emotional_distress expertise
MemoryType fact, opinion, story, interaction, anchor memory
FactCategory identity, capability, limitation, preference, knowledge, relationship memory
OpinionStrength weak, moderate, strong, immutable memory
StoryEventType first_contact, preference_learned, promise_made, topic_discussed, problem_solved, feedback_received, milestone memory
ConstraintType transparency, vocabulary, topic, credential, response_style, safety constraints
ConstraintSeverity warning, block, modify, escalate constraints
TransparencyRule always_disclose, disclose_on_ask, context_dependent, never_deny constraints
AssetType avatar_model, voice_profile, profile_image, background_video, animation_set assets
AssetStatus draft, training, validating, ready, failed, deprecated assets
TrainingStatus pending, queued, preprocessing, training, postprocessing, validating, completed, failed, cancelled assets
QualityLevel low, medium, high, premium assets

Voice Engine (voice-engine/src/voice_engine/)#

The Voice Engine defines separate ProviderType enums for TTS and STT — they are defined in different modules and have different values.

Enum Values Module
ProviderType (TTS) ELEVENLABS, OPENAI, CARTESIA, DEEPGRAM, LOCAL tts/types
AudioFormat pcm_16, mp3, opus, aac, flac, wav tts/types
VoiceEmotion neutral, happy, sad, excited, calm, serious, friendly, professional tts/types
ProviderType (STT) DEEPGRAM, WHISPER, ASSEMBLYAI stt/types

Perception Engine (perception-engine/src/perception_engine/emotion/types.py)#

These enums model the emotional and attentional signals extracted from participant video and audio. EmotionArousal and EmotionValence provide a two-axis model (positive/negative, high/low activation) that complements the discrete EmotionType classification.

Enum Values
EmotionType neutral, happy, sad, angry, fearful, disgusted, surprised, contempt
EmotionValence positive, negative, neutral
EmotionArousal high, medium, low
DetectionSource video, audio, fused

Avatar Engine (avatar-engine/src/avatar_engine/config.py)#

Enum Values
RenderQuality low, medium, high, ultra
DeviceType cpu, cuda, mps

Computer-Use (computer-use/src/computer_use/actions/types.py)#

ActionType is an auto-valued enum covering every action the computer-use agent can take. It is organized into logical groups: mouse, keyboard, browser, file, and utility operations. ScrollDirection and MouseButton are small enums used as parameters on mouse actions.

ActionType (auto-valued enum) covers mouse actions (SCREENSHOT, LEFT_CLICK, RIGHT_CLICK, MIDDLE_CLICK, DOUBLE_CLICK, TRIPLE_CLICK, MOUSE_MOVE, LEFT_CLICK_DRAG, LEFT_MOUSE_DOWN, LEFT_MOUSE_UP, SCROLL), keyboard actions (TYPE, KEY, HOLD_KEY, CLIPBOARD_SET, CLIPBOARD_GET, CLIPBOARD_CLEAR), WAIT, browser actions (NAVIGATE, NEW_TAB, CLOSE_TAB, SWITCH_TAB, REFRESH, BACK, FORWARD), COOKIE_MANAGEMENT, EXTENSION_INTERACTION, file actions (UPLOAD_FILE, DOWNLOAD_FILE, LIST_DIRECTORY, CHANGE_DIRECTORY, READ_FILE, WRITE_FILE, CONVERT_FILE), and BASH_COMMAND. ScrollDirection is up/down/left/right; MouseButton is left/right/middle.


5. REST API Surface#

The API Gateway (api_gateway/app.py) mounts routers under the configurable prefix /api/v1 (settings.api_prefix). Health routes are unprefixed. Responses use ORJSONResponse. GZip compression is applied to bodies over 1 KB. Middleware order (outermost first): RequestIDMiddleware, LoggingMiddleware, TimingMiddleware, CORS, GZip. No authentication middleware is currently installed — endpoints are open; the WebSocket token query parameter is accepted but not validated.

Health (unprefixed) — routers/health.py#

Health endpoints are unprefixed and available at the root of the service. The /ready endpoint aggregates dependency checks, though both the database and Redis checks are currently placeholders.

Method Path Response Notes
GET /health HealthResponse Always returns healthy
GET /ready ReadinessResponse Aggregates DB + Redis checks (both are placeholders)
GET /live LivenessResponse Returns alive plus uptime seconds

Features and Experiments#

These endpoints expose runtime feature flags and A/B experiment configuration. The /experiments/{name}/assign endpoint returns a deterministic variant assignment for a given identifier, using consistent hashing.

Method Path Response Source
GET /api/v1/features feature-flag map routers/features.py
GET /api/v1/experiments configured experiments routers/experiments.py
GET /api/v1/experiments/{name}/assign deterministic variant assignment routers/experiments.py

Personas — routers/personas.py#

The personas collection supports full CRUD plus lifecycle actions (activate, deactivate) and a clone operation. The 409 Conflict response prevents duplicate persona names within an organization.

Method Path Description
POST /api/v1/personas Create persona (409 on duplicate name)
GET /api/v1/personas List (filters: is_active, search, language)
GET /api/v1/personas/{persona_id} Get persona
PATCH /api/v1/personas/{persona_id} Partial update
DELETE /api/v1/personas/{persona_id} Delete
POST /api/v1/personas/{persona_id}/activate Set is_active=true
POST /api/v1/personas/{persona_id}/deactivate Set is_active=false
POST /api/v1/personas/{persona_id}/clone Clone (requires new_name query)

Sessions — routers/sessions.py#

Session management endpoints follow a lifecycle: create → start → join-meeting → pause/resume → end. The /metrics endpoint returns aggregate counts across all sessions without pagination.

Method Path Description
POST /api/v1/sessions Create session (status pending)
GET /api/v1/sessions List (filters: status, persona_id, platform)
GET /api/v1/sessions/metrics Aggregate SessionMetrics
GET /api/v1/sessions/{session_id} Get session
POST /api/v1/sessions/{session_id}/start pendingactive
POST /api/v1/sessions/{session_id}/join-meeting Set meeting URL/platform, status connecting
POST /api/v1/sessions/{session_id}/pause paused
POST /api/v1/sessions/{session_id}/resume active
POST /api/v1/sessions/{session_id}/end ended, computes duration_seconds
DELETE /api/v1/sessions/{session_id} Delete session record

Knowledge — routers/knowledge.py#

The knowledge endpoints cover document lifecycle (create, upload, list, get, update, delete, reprocess) and search. The upload endpoint accepts multipart form data; the document type is inferred from the file extension.

Method Path Description
POST /api/v1/knowledge/documents Create document (inline content or source_url)
POST /api/v1/knowledge/documents/upload Multipart file upload; type inferred from extension
GET /api/v1/knowledge/documents List (filters: status, document_type, persona_id, tag, search)
GET /api/v1/knowledge/documents/{document_id} Get document
PATCH /api/v1/knowledge/documents/{document_id} Update metadata
DELETE /api/v1/knowledge/documents/{document_id} Delete document
POST /api/v1/knowledge/documents/{document_id}/reprocess Reset document to pending
POST /api/v1/knowledge/search Title-match search (see §3.3)

Tools — routers/tools.py#

The tools endpoints manage registrations of callable integrations. The name and tool_type fields are immutable after creation. The /schema endpoint emits the tool's OpenAI-function-style JSON schema, suitable for passing to LLM APIs that accept function definitions.

Method Path Description
POST /api/v1/tools Register tool (409 on duplicate name)
GET /api/v1/tools List (filters: tool_type, is_active, persona_id, search)
GET /api/v1/tools/{tool_id} Get tool
PATCH /api/v1/tools/{tool_id} Partial update (name and type are immutable)
DELETE /api/v1/tools/{tool_id} Delete tool
POST /api/v1/tools/{tool_id}/test Test execution (validates endpoint/MCP URL presence)
POST /api/v1/tools/{tool_id}/activate Set is_active=true
POST /api/v1/tools/{tool_id}/deactivate Set is_active=false
GET /api/v1/tools/{tool_id}/schema Emit OpenAI-function-style JSON schema

Webhooks — routers/webhooks.py#

Webhook management includes delivery history, a test-fire endpoint, and secret rotation. The /rotate-secret endpoint returns the new secret once and never again — callers must store it immediately.

Method Path Description
POST /api/v1/webhooks Create webhook
GET /api/v1/webhooks List (filters: status, event)
GET /api/v1/webhooks/events List subscribable events + descriptions
GET /api/v1/webhooks/{webhook_id} Get webhook
PATCH /api/v1/webhooks/{webhook_id} Update webhook
DELETE /api/v1/webhooks/{webhook_id} Delete webhook
POST /api/v1/webhooks/{webhook_id}/test Send test event
POST /api/v1/webhooks/{webhook_id}/enable active
POST /api/v1/webhooks/{webhook_id}/disable disabled
POST /api/v1/webhooks/{webhook_id}/rotate-secret Rotate signing secret (returned once)
GET /api/v1/webhooks/{webhook_id}/deliveries Paginated delivery history

WebSocket management — routers/websocket.py#

These REST endpoints provide visibility into active WebSocket connections — useful for debugging and monitoring live session traffic.

Method Path Description
GET /api/v1/ws/connections List active connections
GET /api/v1/ws/connections/{connection_id} Get one connection
GET /api/v1/ws/sessions/{session_id}/subscribers Subscriber count for session
GET /api/v1/ws/stats Connection-count statistics

The persona-service (persona-service/src/persona_service/main.py) is a separate FastAPI app exposing its own routes — see §9. It is unprefixed and not mounted by the gateway.


6. WebSocket Protocol#

api_gateway/routers/websocket.py exposes two WebSocket endpoints under the API prefix. The session-specific endpoint automatically subscribes the connection to all events for that session, making it the primary entry point for clients building real-time session integrations.

  • GET /api/v1/ws — general connection. Accepts an optional token query parameter (not validated).
  • GET /api/v1/ws/sessions/{session_id} — session connection; auto-subscribes to session_id on connect.

Message Envelope#

All messages are JSON objects with a type field (WSMessageType). Client→ server messages dispatched by MessageHandler.handle_message:

type Model Effect
subscribe SubscribeMessage Subscribe a connection to a session's events; optional events filter
unsubscribe UnsubscribeMessage Drop a session subscription
command ControlCommand Run a CommandType against a session
ping Server replies with pong and round-trip latency

Server→client message types: event (SessionEvent), transcript (TranscriptEvent carrying a TranscriptSegment), metrics (MetricsEvent), error (ErrorMessage), ack (AckMessage), pong (PongMessage).

Control Commands#

MessageHandler registers handlers for all 15 non-subscription CommandType values: session start/pause/resume/end, audio mute/unmute, video enable/disable, screen share start/stop, AI interrupt/set-mode, tool execute/cancel, and persona switch. Session lifecycle commands broadcast a corresponding WSEventType to all session subscribers; tool.execute broadcasts tool.executing then tool.completed. Handlers log and return acknowledgement payloads — they do not yet invoke the orchestrator or tool-framework services.

TranscriptSegment fields#

TranscriptSegment is the payload type for transcript server→client messages. It carries timing metadata so clients can synchronize caption display with the avatar's speech output.

speaker_id, speaker_name, text, start_time, end_time (seconds, >= 0), confidence (0–1, default 1.0), is_final: bool, language (default en). emit_transcript() and emit_session_event() are exported helpers other services can call to broadcast over the connection manager.


7. Webhook Subsystem#

The delivery engine lives in api_gateway/services/webhooks.py. It handles signing, retry logic, and delivery records for all outbound webhook calls. The retry policy uses exponential backoff with jitter to avoid thundering-herd retries.

  • Signing — HMAC-SHA256 over the JSON payload.
  • PayloadWebhookPayload is { id, event_type, timestamp, api_version, data } with api_version defaulting to "2024-01-01".
  • RetryRetryConfig: max_attempts=5, initial_delay_seconds=1.0, max_delay_seconds=300.0, backoff_multiplier=2.0, jitter_factor=0.1, retryable status codes {408, 429, 500, 502, 503, 504}.
  • Delivery statusDeliveryStatus: pending, delivering, success, failed, retrying, dead_letter.
  • RecordsDeliveryAttempt (per-attempt) and DeliveryRecord (all attempts) track outcomes.

The gateway's /webhooks/{id}/test endpoint currently simulates a successful 200 delivery rather than issuing a real HTTP request.


8. Session Orchestration Model#

The orchestrator service (services/psyche/orchestrator) owns the full session-lifecycle state machine and the real-time pipeline. Types are in orchestrator/session/types.py; logic in orchestrator/session/lifecycle.py. The orchestrator's session model is richer and more fine-grained than the API Gateway's — it tracks internal transition states (resuming, terminating) that the API does not expose to clients.

Session aggregate#

The Session is the central aggregate of the orchestrator. It carries all state needed to resume, pause, time out, or hand off a session.

Field Type Notes
session_id str (UUID)
status SessionStatus Default initializing
config SessionConfig
state SessionState
participants list[Participant]
host_id str
allocated_resources dict
created_at / started_at / paused_at / terminated_at datetime / nullable
total_duration_seconds / active_duration_seconds float
error_count / last_error int / str|null

SessionConfig carries session_type, priority, timeouts (idle_timeout_seconds=300, max_duration_seconds=3600, connection_timeout_seconds=30), feature toggles (enable_recording=False, enable_transcription=True, enable_screen_share=True, enable_tool_use=True), quality (video_quality/audio_quality default "high"), limits (max_participants=10, max_concurrent_tools=3), and persona linkage (persona_id, avatar_id, voice_id).

SessionState#

SessionState is a composite object capturing the evolving state of every dimension of the session. It is checkpointed periodically for recovery purposes.

Composed of ConversationState (phase, turn/message counts, topics, intents, short-term memory, key facts), EmotionalState (current_emotion, engagement_level, satisfaction_score, frustration_level, history), ToolState (active/pending/completed tool calls, computer-use and screen-share flags), and ScreenShareState. State carries a state_version and checkpoint metadata; StateCheckpoint snapshots SessionState with optional compression.

Lifecycle transitions (SessionLifecycleManager)#

The diagram below shows valid state transitions. Attempting an invalid transition (e.g. pausing a session that is not ACTIVE) raises a ValueError. The TimeoutManager polls every 10 seconds and fires warning and timeout callbacks.

text
                create_session()           start_session()
   (none) ──────────────────────▶ INITIALIZING ──────────────▶ ACTIVE
                                                                  │
                            pause_session()  ┌────────────────────┤
                                              ▼                    │
                                           PAUSED                  │
                                              │ resume_session()    │
                                              ▼                    │
                                         RESUMING ─────────────────┤
                                                                   │
                          terminate_session() / timeout            │
                                              ▼                    │
                          TERMINATING ──▶ TERMINATED ◀─────────────┘

pause requires status ACTIVE; resume requires PAUSED; start requires INITIALIZING — violations raise ValueError. TimeoutManager polls every 10s, firing a warning callback inside warning_before_timeout (60s) and a timeout callback (idle or max_duration). SessionHandoffManager models agent handoff with statuses pending/accepted/rejected/completed. The manager emits in-process events: session_created, session_started, session_terminated, session_paused, session_resumed, handoff_requested, handoff_completed.

Pipeline (orchestrator/pipeline/types.py)#

These enums describe the data flowing through the real-time processing pipeline and the services that consume it. They are used to label pipeline messages and route them to the correct destination service.

StreamType (audio_input/output, video_input/output, screen_share, data, control, events), PipelineStage (input, preprocessing, processing, postprocessing, output), MessageType (data, audio_frame, video_frame, text, command, event, response, error, heartbeat, ack), and ServiceType (asr, tts, llm, nlu, vision, avatar, emotion, knowledge, tools). The orchestrator also ships batching/, caching/ (semantic cache + embedding service), multiparty/ (participants, turns, sync), and resources/ (allocator, monitor, pool) submodules.


9. Persona Service Model#

services/psyche/persona-service is a standalone FastAPI app (persona_service/main.py, title "Psyche Persona Service") backed by the persona_system package. It is richer than the API Gateway's persona model and stores personas in an in-process PersonaSystem instance. The reason for this separation is that the Persona Service implements the full domain logic including OCEAN personality, expertise evaluation, memory storage, and constraint checking — capabilities that don't belong in the API Gateway's thin CRUD layer.

Endpoints (selected)#

  • Templates: GET /templates/personas|personalities|domains|constraints.
  • CRUD: POST/GET/PUT/DELETE /personas, GET /personas (filters organization_id, role, status, limit, offset), POST /personas/{id}/clone, POST /personas/from-template/{template_id}.
  • Personality: GET/POST /personas/{id}/personality.
  • Expertise: GET/POST /personas/{id}/expertise, POST /personas/{id}/expertise/domains, POST /personas/{id}/expertise/evaluate.
  • Memory: POST /personas/{id}/memories/facts|opinions|interactions|retrieve.
  • Constraints: GET/POST /personas/{id}/constraints, POST /personas/{id}/constraints/check-response, POST /personas/{id}/constraints/check-drift.
  • Assets: POST /personas/{id}/avatars|voices, POST /avatars/{id}/train, POST /voices/{id}/train, POST /personas/{id}/assets/link, GET /personas/{id}/assets.
  • Prompt: POST /personas/{id}/system-prompt.

Domain types#

These are the core domain types managed by the Persona Service. Unlike the API Gateway's flat models, these types carry the full behavioral configuration used by the LLM, voice, and avatar systems at session time.

  • PersonaIdentityid (UUID), name, display_name, role (PersonaRole), custom_role, gender_presentation, age_range, background_story, company_affiliation, job_title, status (PersonaStatus), created_at/updated_at, created_by, organization_id, tags.
  • BigFivePersonality — the OCEAN model does exist here: openness, conscientiousness, extraversion, agreeableness, neuroticism, each a float defaulting to 0.5 with a validate() bounds check. CommunicationParameters adds style, formality_level, humor_level, expressiveness, response_length, verbosity, technical_level, and behavior flags. PersonalityConfig ties BigFivePersonality + CommunicationParameters + BehaviorMapping to a persona_id.
  • DomainExpertisedomain_id, domain_name, level (ExpertiseLevel), coverage (0–1), confidence_threshold, topics, excluded_topics, knowledge_snippets, related_domains.
  • MemoryPersonaFact (category, key, value, importance, is_public, use_count), opinion anchors, and per-user story events.
  • PresetsPERSONALITY_PRESETS ships five presets (warm_professional, empathetic_helper, efficient_expert, enthusiastic_guide, calm_advisor). SYSTEM_TEMPLATES ships four persona templates (professional_assistant, friendly_support, sales_professional, technical_instructor). DOMAIN_TEMPLATES and CONSTRAINT_PRESETS are also exported.

10. Persistence#

Database bootstrap#

services/psyche/api-gateway/src/api_gateway/db/migrate.py is the only database-DDL code in the domain. It is an idempotent bootstrap script (not Alembic migrations) intended to run as a Kubernetes Job. Its scope is narrow: it prepares the PostgreSQL extensions and creates the audit schema, but does not create any domain tables. Specifically, it:

  • Enables extensions: uuid-ossp, pgcrypto, vector (pgvector), pg_trgm, btree_gin, btree_gist.
  • Creates schemas: persona, knowledge, sessions, analytics, audit.
  • Creates one table — audit.change_log (id, table_name, operation, old_data JSONB, new_data JSONB, changed_by, changed_at, session_id, ip_address INET, user_agent) — with four indexes.
  • Defines two PL/pgSQL functions: update_updated_at_column() and audit_changes().

No personas, sessions, knowledge_documents, tools, or webhooks tables are created. The module notes that Alembic migrations are not yet introduced.

Runtime storage#

The API Gateway routers store every resource in module-level Python dicts (_personas, _sessions, _documents, _tools, _webhooks, _deliveries) with integer counters generating IDs. Each router file states "In-memory storage for demo purposes." app.py's lifespan handler leaves database and Redis initialization commented out. The persona-service likewise keeps state inside an in-process PersonaSystem.

Configured stores#

The API Gateway Settings declares a PostgreSQL URL (asyncpg driver) and a Redis URL with pool sizes, and pyproject.toml includes sqlalchemy[asyncio] and asyncpg. These dependencies are present but the gateway does not yet open connections at runtime.


11. Avatar Engine Configuration#

services/psyche/avatar-engine/src/avatar_engine/config.py defines a nested pydantic-settings configuration. The package implements 3DGS and NeRF rendering (rendering/, nerf/), FLAME fitting (preprocessing/, models/flame.py), training (training/), quantization, lip sync (lipsync/), expression/eye/body systems, and cultural overlays.

RenderConfig (env prefix AVATAR_RENDER_)#

RenderConfig controls real-time rendering parameters. Note that the configuration defaults to internal gRPC ports (50051/50052), while the Dockerfile overrides the HTTP service port to 8001 — these are different protocol endpoints on the same service.

Field Default Constraints
width / height 1024 256–2048
quality high RenderQuality
target_fps 30 15–120
enable_temporal_aa true
taa_history_weight 0.9 0.0–1.0
max_gaussians 100000 10000–500000
sh_degree 3 0–4
tile_size 16 8–32
enable_lod / enable_depth_sorting / enable_frustum_culling true

Additional groups cover ambient occlusion, dynamic resolution, and multi-resolution rendering.

TrainingConfig (env prefix AVATAR_TRAIN_)#

TrainingConfig governs the offline 3DGS avatar training pipeline. The densification settings control when the system adds or prunes Gaussian splats to match the training video.

iterations=30000 (1000–100000), batch_size=1 (1–8), learning_rate=1e-4, per-attribute Gaussian learning rates, densification settings (densify_from_iter=500, densify_until_iter=15000, densify_grad_threshold=0.0002), loss weights (lambda_l1=0.8, lambda_ssim=0.2, lambda_lpips=0.0, lambda_reg=0.01), checkpointing, and early stopping.

FLAMEConfig (env prefix AVATAR_FLAME_)#

FLAMEConfig controls the FLAME parametric face model that provides the expression-control skeleton underlying the 3DGS render.

model_path/landmark_path, n_shape=100 (10–300), n_expr=50 (10–100), n_tex=50 (10–100), and shape/expression/pose regularization weights.

PreprocessingConfig (env prefix AVATAR_PREPROCESS_)#

PreprocessingConfig controls the face detection and video preprocessing step that prepares training data for 3DGS optimization.

face_detector="mediapipe" (also mtcnn, retinaface), num_landmarks=68, min_video_duration=5.0, max_video_duration=300.0 (cap 600.0), background_model="robust_video_matting", quality thresholds.

AvatarConfig (root, env prefix AVATAR_)#

The root avatar service configuration, which composes the four configs above.

service_name="avatar-engine", service_port=50051, grpc_port=50052, device=cuda (DeviceType), gpu_id=0, enable_mixed_precision=true, and the four nested sub-configs above. Note: the avatar-engine config defaults to ports 50051/50052, while its Dockerfile sets SERVICE_PORT=8001 (see §16).


12. Voice Engine Model#

services/psyche/voice-engine implements TTS, STT, diarization, phoneme/viseme generation, turn-taking, captions, transcript processing, and audio handling. The service is organized into subpackages, each responsible for a distinct stage of the voice pipeline.

TTS providers (tts/)#

Each TTS provider is a separate module implementing the TTSProvider abstract interface. A provider_chain.py implements ordered failover across providers. Supporting modules handle advanced synthesis features.

Provider modules: elevenlabs.py, openai_tts.py, cartesia.py, deepgram_aura.py, orpheus.py, f5_tts.py, piper.py. A provider_chain.py implements failover. Supporting modules: voice_cloning.py, style_transfer.py, emotional_synthesis.py, emotion_intensity.py, personality_variation.py, voice_quality.py, voice_similarity.py, watermarking.py, phrase_cache.py, usage_audit.py, streaming.py.

SynthesisConfig (tts/types.py) is the synthesis request: voice_id / voice_profile, output_format (AudioFormat, default pcm_16), sample_rate (default 24000), channels, speaking_rate (0.5–2.0), pitch_shift (semitones), volume_gain_db, emotion (VoiceEmotion), style_intensity, optional dynamic-emotion-intensity, optional VoicePersonalityTraits (formality, friendliness, verbosity, humor, empathy, assertiveness — modeled after the API persona traits), SSML fields, voice-cloning reference audio, style-transfer reference audio, watermark_payload, audit_metadata, streaming (chunk_size_ms=100), timeout_seconds=30.0, and quality (standard/high).

AudioChunk carries data: bytes, format, sample_rate, channels, duration_ms, sequence_num, is_last, with numpy conversion helpers. SynthesisResult adds timing (processing_time_ms, first_byte_latency_ms), provider, voice_id, text_length, cost_usd. TTSProvider is the abstract provider interface (synthesize_stream, synthesize_streaming_text, synthesize_batch, health_check, get_voices, close).

STT providers (stt/)#

The STT subsystem mirrors the TTS structure: individual provider modules, a provider_chain.py for failover, and supporting modules for accuracy improvements.

Provider modules: deepgram.py, whisper.py, local_whisper.py, assemblyai.py, google_speech.py, plus provider_chain.py, punctuation_restoration.py, and fine_tuning.py. ProviderType (STT) is DEEPGRAM, WHISPER, ASSEMBLYAI.

Other subsystems#

The voice engine is a full-featured pipeline with dedicated subpackages for each processing stage. Each subpackage covers a distinct concern.

conversation/ (LLM, RAG, context, prompts, long-term memory, style), diarization/, phoneme/ (g2p, IPA, alignment), viseme/, turn_taking/ (endpointing, interruption, timing), captions/, transcript/ (confidence, correction, entities, intent, sentiment, streaming, timestamps), sync/ (AV sync), failover/ (circuit breaker, health), commands/, and audio/ (VAD, echo cancellation, noise suppression, gain control, resampling, spatial audio).


13. Perception Engine Model#

services/psyche/perception-engine implements emotion, intent, and screen perception (emotion/, intent/, screen/, context/). The emotion types are the most detailed — the perception engine uses a richer set than the simple emotion states in the orchestrator, because it models the full affective state of participants including valence (positive/negative) and arousal (high/low).

Emotion types (emotion/types.py)#

  • EmotionType — 7 emotions plus neutral (see §4). EmotionProbabilities holds a dict[EmotionType, float] distribution with dominant_emotion, confidence, and entropy properties.
  • BoundingBoxx, y, width, height, confidence, with center, area, IoU helpers.
  • FacialLandmarks — a 68-point landmark array with named regions and derived metrics (eye-aspect-ratio, mouth-aspect-ratio, eyebrow-height-ratio).
  • VideoEmotionResult, AudioEmotionResult, FusedEmotionResult — modality results; AudioFeatures carries prosodic/energy/spectral/voice-quality features (pitch, energy, MFCCs, jitter, shimmer, HNR).
  • EngagementMetricsengagement_score, attention_score, confusion_score, interest_score, looking_at_camera, gaze_deviation, head_nods, head_shakes, blink_rate.
  • EmotionDetectorConfigface_detector_backend="retinaface", landmark_model="dlib_68", emotion_model="fer2013", temporal smoothing, and modality-fusion weights (video_weight=0.6, audio_weight=0.4).

14. Computer-Use Action Model#

services/psyche/computer-use implements an agentic browser/desktop automation loop. The service is organized into subpackages covering the Observe-Reason-Act loop, vision capabilities, and safety guardrails.

Subpackages: actions/, agent/ (loop, planning, recovery, undo, dead-end, explain), vision/ (OCR, screenshot, UI detection, semantics, accessibility), environment/ (container, display, Windows VM), screen_sharing/, demonstration/, and benchmark/.

Action (actions/types.py) is the core type: every browser or desktop operation is represented as an Action instance. Factory constructors exist for each common action type, avoiding the need to manually set all optional fields.

Action (actions/types.py) carries an action_type (ActionType, see §4) plus optional mouse coordinates (Coordinate/end_coordinate), keyboard parameters (text, key), clipboard_selection, scroll parameters (scroll_direction, scroll_amount=3), duration_ms, browser parameters (url, tab_index), file_path, and command. Factory constructors exist for each common action. ActionResult records success, action_type, screenshot_base64, output, error, duration_ms, cursor_position, and converts to Claude tool-result format. ActionHistory keeps a bounded history (max_size=100). SPECIAL_KEYS and MODIFIER_KEYS map names to X11 keysyms.


15. Knowledge Base Model#

services/psyche/knowledge-base implements ingestion, embedding, RAG, and retrieval — distinct from the gateway's in-memory document store. This service is the backend that the gateway search endpoint will eventually call; currently they operate independently.

  • ingestion/DocumentType (pdf, docx, doc, html, text, markdown, excel, csv, powerpoint, json, audio, video, image, unknown), ProcessingStatus (pending, processing, chunking, embedding, indexing, completed, failed), ChunkingStrategy (fixed_size, semantic, sentence, paragraph, hierarchical, document_specific), plus parsers, cleaning, and chunking modules.
  • embedding/ — embedding generators, vector stores, and a manager.
  • rag/ — pipeline, generator, evaluator, prompts.
  • retrieval/ — query, search, reranking, context, pipeline.
  • management/ — analytics, lifecycle, quality, updates.

The knowledge-base service's ProcessingStatus (7 stages) differs from the API Gateway's DocumentStatus (4 states) and from DocumentType in models/knowledge.py (7 types). They are separate type sets.


16. Service Ports#

Ports below are taken from each service's Dockerfile (SERVICE_PORT). These are the ports services listen on within the Kubernetes cluster. External traffic enters exclusively through the API Gateway on port 8000.

Service Project name Port
API Gateway psyche-api-gateway 8000
Avatar Engine psyche-avatar-engine 8001
Voice Engine psyche-voice-engine 8002
Behavior Engine psyche-behavior-engine 8003
Perception Engine psyche-perception-engine 8004
Computer-Use psyche-computer-use 8005
Conferencing psyche-conferencing 8006
Orchestrator psyche-orchestrator 8007
Knowledge Base psyche-knowledge-base 8008
Persona Service psyche-persona-service 8009
Learning System psyche-learning-system 8010
Tool Framework psyche-tool-framework 8011 (MCP server on 8012)

The avatar-engine application config (AvatarConfig) defaults to 50051/50052 for direct/gRPC use; the Dockerfile overrides the HTTP service port to 8001. Services psyche-tavus-integration, psyche-video-conferencing, psyche-security, and psyche-error-handling do not declare a Dockerfile port.


17. API Gateway Configuration#

Settings (api_gateway/config.py) uses pydantic-settings, reading a .env file (case-insensitive, extra keys ignored). The table below lists key fields and their defaults. Note that the configured internal URLs for orchestrator (:8003) and knowledge_base (:8004) do not match those services' Dockerfile ports (:8007 and :8008) — this discrepancy must be resolved when wiring the gateway to the backend services.

Field Default
app_name Serwaa API Gateway
environment development (development/staging/production)
host / port 0.0.0.0 / 8000
api_prefix /api/v1
database_url postgresql+asyncpg://serwaa:serwaa_dev@localhost:5432/serwaa
database_pool_size / max_overflow 5 / 10
redis_url redis://localhost:6379/0
secret_key SecretStr placeholder (must be overridden)
jwt_algorithm HS256
access_token_expire_minutes 30
refresh_token_expire_days 7
cors_origins localhost:3000, localhost:3001, dashboard.serwaa.localhost:8443
rate_limit_requests / window_seconds 100 / 60
metrics_enabled / tracing_enabled true / true
jaeger_endpoint http://localhost:14268/api/traces
avatar_engine_url http://localhost:8001
voice_engine_url http://localhost:8002
orchestrator_url http://localhost:8003
knowledge_base_url http://localhost:8004

The configured internal-service URLs for orchestrator (:8003) and knowledge_base (:8004) do not match those services' Dockerfile ports (:8007 and :8008).

Feature flags#

Feature flags control which optional capabilities are enabled at runtime. They are exposed as environment variables and served to clients via GET /api/v1/features.

feature_computer_use (default true), feature_voice_cloning (true), feature_multi_party (false), feature_screen_share (true). Exposed via GET /api/v1/features as FEATURE_COMPUTER_USE, FEATURE_VOICE_CLONING, FEATURE_MULTI_PARTY, FEATURE_SCREEN_SHARE.

Experiments#

The experiments system supports A/B testing via deterministic variant assignment. Each experiment defines a set of named variants with integer weights that must sum to 100.

experiments is a dict[str, dict[str, int]] of variant→weight maps; a validator enforces that each experiment's weights are non-negative integers summing to 100. experiment_salt (default serwaa) feeds deterministic bucketing (assign_variant in api_gateway/experiments.py), producing a bucket 0–99.

The avatar engine, perception engine, and other services each define their own pydantic-settings configuration with their own env prefixes (e.g. AVATAR_*). There is no single shared environment-variable contract across services.


18. Shared Contracts Package#

libs/contracts/psyche (@psyche/contracts) is a Zod-based TypeScript contracts package intended to be consumed by any TypeScript client or library that needs type-safe access to Psyche data types. Only the common module is currently implemented; per-domain contract modules (avatar, voice, behavior, etc.) are declared in package.json exports but their source files do not yet exist.

libs/contracts/psyche (@psyche/contracts) is a Zod-based TypeScript contracts package. Its package.json declares exports for ./avatar, ./voice, ./behavior, ./perception, ./conferencing, ./knowledge, and ./persona, but only src/common/index.ts and src/index.ts existsrc/index.ts re-exports common and keeps the per-domain exports commented out pending migration.

The implemented common module exports these Zod schemas and inferred types:

Schema Shape / values
IdSchema / SessionIdSchema UUID string
TimestampSchema ISO 8601 datetime string
QualityPresetSchema LOW | MEDIUM | HIGH | ULTRA
QualityConfigSchema { preset, resolution: {width 256–2048, height 256–2048}, targetFps 15–120 }
HealthStatusSchema healthy | degraded | unhealthy
HealthCheckResponseSchema { status, version, uptime, timestamp, checks }
ErrorCodeSchema 25 codes: general (INTERNAL_ERROR, VALIDATION_ERROR, NOT_FOUND, UNAUTHORIZED, FORBIDDEN, RATE_LIMITED, SERVICE_UNAVAILABLE), avatar, voice, conferencing, knowledge, and persona error codes
ApiErrorSchema { code, message, details?, requestId?, timestamp }
PaginationRequestSchema { page>=1, limit 1–100, sortBy?, sortOrder }
PaginationResponseSchema { page, limit, total, totalPages, hasMore }
SessionStatusSchema initializing | active | paused | ended | error
SessionInfoSchema { sessionId, status, personaId?, avatarId?, startedAt, lastActivityAt, metadata? }
GpuDeviceSchema { id, name, memory: {total,used,free}, utilization 0–100, temperature? }
ResourceRequestSchema { gpuMemoryMb?, cpuCores?, memoryMb?, priority }
PerformanceMetricsSchema { latencyMs, throughput?, errorRate?, gpuUtilization?, memoryUsageMb? }
LatencyBreakdownSchema { total, components: Record<string, number> }

The contracts package's SessionStatusSchema (initializing/active/paused/ ended/error) is yet another distinct session-status value set, matching neither the API Gateway's nor the orchestrator's enum.


19. V2 Fighting-Game Cross-Domain Contracts#

The V2 fighting-game project (V2/) consumes three distinct Psyche capability clusters through dedicated @v2/* adapter packages. Each adapter wraps a Psyche library, runs entirely on V2's client/services tier, and is bound by the same hard safety property: none of these outputs may ever feed the deterministic, rollback-networked combat simulation. They are presentation, accessibility, and compliance surfaces only. The TypeScript contracts encode this as a literal mayInfluenceRollback: false field, and V2's V2Netcode modules are statically forbidden from referencing any of these packages.

These sections describe what is implemented in source under apps/v2/psyche-ai-director-hints/, apps/v2/psyche-caption-streaming/, apps/v2/eu-ai-act-surface/, and the underlying Psyche libraries libs/psyche/behavior-prediction, libs/psyche/caption-streaming, and libs/psyche/action-safety.

19.1 V2 Adaptive AI Director Hints Contract#

@v2/psyche-ai-director-hints is the V2 adapter between the V2AdaptiveAI Unreal plugin and @psyche/behavior-prediction. It is implemented in apps/v2/psyche-ai-director-hints/src/psyche-ai-director-hints.ts and depends on @psyche/behavior-prediction (workspace:*), instantiating the predictor through createBehaviorPredictionEngine. The package exports the constants V2_ADAPTIVE_AI_PLUGIN_NAME ('V2AdaptiveAI') and V2_PSYCHE_BEHAVIOR_PREDICTION_PACKAGE ('@psyche/behavior-prediction'), and the plan builders buildV2AdaptiveAILiveHintPlan, buildV2AdaptiveAIMatchStartSnapshotPlan, and queueV2AdaptiveAIMidMatchPsycheUpdateForNextMatch.

The behavior-prediction engine produces per-player tendency hints — a five-axis V2AdaptiveAITendencyVector (pressure, defense, spacing, throwGame, resourceUse) plus a next-action prediction and confidence. How those hints reach the AI Director depends on the match mode, captured by the V2AdaptiveAIHintConsumptionMode union:

  1. non-rollback-live. In modes that do not use rollback networking (training, single-player, lobby practice), V2AdaptiveAI consumes live Psyche hints each tick. The hint is advisory CPU-behavior shaping; it is not a simulation input shared between peers, so live RPCs are permitted.
  2. rollback-match-start-snapshot. In rollback-with-CPU modes, the adapter samples Psyche behavior-prediction once at match load and bakes the result — the tendency-vector + seed — into the deterministic match-start input vector. After that snapshot, the CPU runs from the seeded vector with no further Psyche RPCs, so every peer re-simulates identically during a rollback. The source marks this path rollback-match-start-snapshot and tags the serialized payload v2NetcodeTreatsAsOrdinaryDeterministicInput.

The contract enforces the rollback boundary with two complementary guards: non-rollback-live plans are rejected when the match context reports calledFromRollbackFrame, and the spec asserts that the adapter rejects live Psyche RPCs from rollback frames ("Psyche RPC is rejected inside rollback frames"). Mid-match Psyche updates that arrive during a rollback match are not applied to the running sim; they are deferred by queueV2AdaptiveAIMidMatchPsycheUpdateForNextMatch and folded into the next match-start snapshot instead. The rollback policy string is live-off-rollback-or-match-start-snapshot-only, and every hint and plan carries mayInfluenceRollback: false.

This contract is registered in V2's services layer as the psyche-behavior-prediction adapter under EV2OshunDomain::Psyche, mirrored in the V2AdaptiveAI plugin types (FV2AdaptiveAITendencyHint, FV2AdaptiveAIMatchStartInputVector), and validated by V2/ue/Tools/check-v2-adaptive-ai-psyche.py together with the repository's rollback-determinism self-test.

19.2 V2 Caption Streaming Contract#

@v2/psyche-caption-streaming wires real-time V2 match captions through @psyche/caption-streaming. It is implemented in apps/v2/psyche-caption-streaming/src/psyche-caption-streaming.ts, depends on @psyche/caption-streaming (workspace:*), and composes the Psyche accessibility caption engine and live orchestrator via createAccessibilityCaptionEngine and createLiveMeetingOrchestrator. The exported constants name both ends of the bridge: V2_PSYCHE_CAPTION_STREAMING_SOURCE_PACKAGE ('@psyche/caption-streaming'), V2_PSYCHE_CAPTION_STREAMING_PACKAGE_NAME ('@v2/psyche-caption-streaming'), its binding id V2_PSYCHE_CAPTION_STREAMING_BINDING_ID, and the accessibility owner V2_IRIS_ACCESSIBILITY_BRIDGE_PACKAGE ('@v2/iris-accessibility').

buildV2PsycheCaptionStreamingSurface builds a per-match plan across five caption surfaces (in-match-hud, spectator-overlay, replay-viewer, companion-second-screen, broadcast-observer). The surface flags advertise its scope: controlsRealTimeMatchCaptions: true for in-match HUD captions, controlsAccessibilityCaptions for the accessibility caption channel, and controlsSpectatorModeCaptions for the spectator mode overlay that broadcasts and tournament observers consume. Replay support is exposed through supportsReplayCaptionTranscript, and live channel changes are published on the v2.caption-stream.spectator.channel.updated event topic.

Captions are strictly a UI presentation layer. The rollback policy is off-rollback-caption-ui-only, the surface sets mayInfluenceRollback: false, and the bridge never writes deterministic match-simulation state. Iris remains the accessibility-semantics owner (the caption channel is reused by @v2/iris-accessibility as its real-time caption provider); Psyche provides the caption-engine implementation until Iris owns live captions end to end. The contract is validated by V2/ue/Tools/check-v2-psyche-caption-streaming.py.

19.3 EU AI Act Transparency Surface (@psyche/action-safety)#

Psyche owns the player-facing classifier transparency and opt-out half of V2's EU AI Act compliance surface. The implementation lives in libs/psyche/action-safety/src/v2-ai-act-transparency.ts (package @psyche/action-safety) and exports buildV2AdaptiveAIClassifierTransparency and evaluateV2AdaptiveAIOptOut. The transparency record classifies the Adaptive AI Director as a limited-risk AI system, carries the human-readable disclosure text, names the classifier purpose and signals, and links the hosted Model Card slug adaptive-ai-director. Opt-out is always available (optOutAvailable: true): when a player opts out, evaluateV2AdaptiveAIOptOut returns a decision that disables adaptive behavior and falls back to a static-cpu-profile (fallbackMode: 'static-cpu-profile'). Like every other V2 surface, the transparency record and the opt-out decision are off-rollback (offRollback: true, mayInfluenceRollback: false).

This Psyche transparency contract is one of three composed by the @v2/eu-ai-act-surface service package. That package depends on @psyche/action-safety for transparency/opt-out, on @nous/safety for Model Card hosting (buildV2AdaptiveAIModelCard and the AI-commentary / anti-cheat / generation-pipeline Model Cards), and on @themis/accountability for the AI-system-of-record registration and the regulator-ready conformity export. The composed surface marks AI commentary and generation outputs as off-rollback presentation content and applies the player opt-out before any adaptive classifier runs. The cross-domain surface is validated by V2/ue/Tools/check-v2-eu-ai-act-surface.py; the Nous and Themis halves of the contract are documented in DOMAINS/nous/specifications.md and DOMAINS/themis/specifications.md.


Acceptance Criteria#

A change to a Psyche Python service is acceptable when:

  1. Type safety — every API model derives from BaseSchema (gateway) or is a typed dataclass / pydantic model; extra="forbid" is preserved on gateway models.
  2. Enum fidelity — new states or types extend the relevant enum in §4 rather than introducing free-form strings.
  3. Lint and typesruff and mypy pass for the touched service.
  4. Testspytest passes; each service has a tests/ directory.
  5. Build — TypeScript libraries build with tsup and pass vitest.
  6. No fabricated capability — documentation and OpenAPI metadata describe only behavior that the code performs (e.g. the gateway's knowledge search is title-matching, not vector search).