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/, andlibs/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 withpyproject.toml,project.json,src/, andtests/. The workspace rootservices/psyche/pyproject.tomldeclares 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.tsfiles vs ~130.pyfiles), each withpackage.json,project.json,tsconfig.json,tsup.config.ts, andvitest.config.ts. - Admin web app —
apps/psyche/admin, a Next.js application. - Contracts —
libs/contracts/psyche(@psyche/contracts), a Zod-based shared-types package. Only thecommonmodule is implemented; per-domain contract modules are declared inpackage.jsonexportsbut 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.
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
scoreof0.85and does not query a vector store. The Qdrant-backed semantic path is implemented inservices/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 }.PaginationMeta—total,page,per_page(1–100),total_pages,has_next,has_prev.ErrorResponse—{ error: ErrorDetail }whereErrorDetailis{ 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
SessionStatusis 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 |
pending → active |
| 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 optionaltokenquery parameter (not validated).GET /api/v1/ws/sessions/{session_id}— session connection; auto-subscribes tosession_idon 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.
- Payload —
WebhookPayloadis{ id, event_type, timestamp, api_version, data }withapi_versiondefaulting to"2024-01-01". - Retry —
RetryConfig: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 status —
DeliveryStatus:pending,delivering,success,failed,retrying,dead_letter. - Records —
DeliveryAttempt(per-attempt) andDeliveryRecord(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.
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(filtersorganization_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.
PersonaIdentity—id(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 avalidate()bounds check.CommunicationParametersaddsstyle,formality_level,humor_level,expressiveness,response_length,verbosity,technical_level, and behavior flags.PersonalityConfigtiesBigFivePersonality+CommunicationParameters+BehaviorMappingto apersona_id.DomainExpertise—domain_id,domain_name,level(ExpertiseLevel),coverage(0–1),confidence_threshold,topics,excluded_topics,knowledge_snippets,related_domains.- Memory —
PersonaFact(category,key,value,importance,is_public,use_count), opinion anchors, and per-user story events. - Presets —
PERSONALITY_PRESETSships five presets (warm_professional,empathetic_helper,efficient_expert,enthusiastic_guide,calm_advisor).SYSTEM_TEMPLATESships four persona templates (professional_assistant,friendly_support,sales_professional,technical_instructor).DOMAIN_TEMPLATESandCONSTRAINT_PRESETSare 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_dataJSONB,new_dataJSONB,changed_by,changed_at,session_id,ip_addressINET,user_agent) — with four indexes. - Defines two PL/pgSQL functions:
update_updated_at_column()andaudit_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 plusneutral(see §4).EmotionProbabilitiesholds adict[EmotionType, float]distribution withdominant_emotion,confidence, andentropyproperties.BoundingBox—x,y,width,height,confidence, withcenter,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;AudioFeaturescarries prosodic/energy/spectral/voice-quality features (pitch, energy, MFCCs, jitter, shimmer, HNR).EngagementMetrics—engagement_score,attention_score,confusion_score,interest_score,looking_at_camera,gaze_deviation,head_nods,head_shakes,blink_rate.EmotionDetectorConfig—face_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'sDocumentStatus(4 states) and fromDocumentTypeinmodels/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) andknowledge_base(:8004) do not match those services' Dockerfile ports (:8007and: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 exist —
src/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:
non-rollback-live. In modes that do not use rollback networking (training, single-player, lobby practice),V2AdaptiveAIconsumes 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.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 pathrollback-match-start-snapshotand tags the serialized payloadv2NetcodeTreatsAsOrdinaryDeterministicInput.
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:
- Type safety — every API model derives from
BaseSchema(gateway) or is a typed dataclass / pydantic model;extra="forbid"is preserved on gateway models. - Enum fidelity — new states or types extend the relevant enum in §4 rather than introducing free-form strings.
- Lint and types —
ruffandmypypass for the touched service. - Tests —
pytestpasses; each service has atests/directory. - Build — TypeScript libraries build with
tsupand passvitest. - 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).