Skip to content

API Reference

SentinelClient

Thymia Sentinel client for streaming audio to the Lyra server.

Streams both user and agent audio, plus transcripts, to enable multimodal safety analysis combining speech biomarkers with conversation content.

Example using decorators
from thymia_sentinel import SentinelClient

sentinel = SentinelClient(
    user_label="user-123",
    policies=["demo_wellbeing_awareness"],
)

@sentinel.on_policy_result
async def handle_policy(result):
    level = result["result"]["classification"]["level"]
    if level >= 2:
        print(f"Elevated risk: {result['result']['concerns']}")

@sentinel.on_progress
async def handle_progress(result):
    for name, status in result["biomarkers"].items():
        print(f"{name}: {status['speech_seconds']:.1f}s")

await sentinel.connect()

# In your audio loop:
await sentinel.send_user_audio(audio_bytes)
await sentinel.send_agent_audio(audio_bytes)
await sentinel.send_user_transcript("Hello")
await sentinel.send_agent_transcript("Hi there!")

await sentinel.close()
Example using callbacks
sentinel = SentinelClient(
    user_label="user-123",
    on_policy_result=handle_policy_result,
    on_progress_result=handle_progress,
)

Attributes:

Name Type Description
user_label

Optional unique identifier for the user being monitored

date_of_birth

Optional user's date of birth (YYYY-MM-DD format, improves accuracy)

birth_sex

Optional user's birth sex ("MALE" or "FEMALE", improves accuracy)

language

Language code (default: "en-GB")

sample_rate

Audio sample rate in Hz (default: 16000)

connected property

Whether the client is currently connected.

__init__(user_label=None, date_of_birth=None, birth_sex=None, language='en-GB', policies=None, biomarkers=None, on_policy_result=None, on_progress_result=None, progress_updates_frequency=1.0, custom_policies=None, sample_rate=DEFAULT_SAMPLE_RATE, server_url=None, api_key=None)

Initialize the Sentinel client.

Parameters:

Name Type Description Default
user_label Optional[str]

Optional unique identifier for the user (UUID format recommended)

None
date_of_birth Optional[str]

Optional date of birth in YYYY-MM-DD format (improves accuracy, imputed from voice if omitted)

None
birth_sex Optional[str]

Optional, either "MALE" or "FEMALE" (improves accuracy, imputed from voice if omitted)

None
language str

Language code (default: "en-GB")

'en-GB'
policies Optional[list[str]]

List of policies to execute (e.g., ["demo_wellbeing_awareness"])

None
biomarkers Optional[list[str]]

List of biomarkers to extract (default: ["helios"])

None
on_policy_result Optional[Union[Callable[[PolicyResult], None], Callable[[PolicyResult], Awaitable[None]]]]

Callback for policy results (sync or async)

None
on_progress_result Optional[Union[Callable[[ProgressResult], None], Callable[[ProgressResult], Awaitable[None]]]]

Callback for progress updates (sync or async)

None
progress_updates_frequency float

How often to receive progress updates in seconds

1.0
custom_policies Optional[list[dict]]

Optional inline policy definitions (requires feature flag on API key)

None
sample_rate int

Audio sample rate in Hz (default: 16000)

DEFAULT_SAMPLE_RATE
server_url Optional[str]

WebSocket server URL (default: from THYMIA_SERVER_URL env or wss://ws.thymia.ai)

None
api_key Optional[str]

Thymia API key (default: from THYMIA_API_KEY env)

None

Raises:

Type Description
ValueError

If THYMIA_API_KEY is not provided and not in environment

close() async

Close the connection to the Lyra server.

Cancels the receive task and closes the WebSocket connection.

connect() async

Connect to the Lyra server and start receiving events.

Establishes WebSocket connection, sends configuration, and starts the background task to receive server events.

on_policy_result(func)

Decorator to register a policy result handler.

The handler will be called whenever a policy result is received from the server. Multiple handlers can be registered.

Example
@sentinel.on_policy_result
async def handle_policy(result: PolicyResult):
    level = result["result"]["classification"]["level"]
    if level >= 2:
        await apply_safety_action(result)

Parameters:

Name Type Description Default
func F

Async or sync function that receives a PolicyResult

required

Returns:

Type Description
F

The original function (unchanged)

on_progress(func)

Decorator to register a progress handler.

The handler will be called periodically with biomarker extraction progress updates. Multiple handlers can be registered.

Note: Registering a progress handler automatically enables progress updates from the server.

Example
@sentinel.on_progress
async def handle_progress(result: ProgressResult):
    for name, status in result["biomarkers"].items():
        pct = (status["speech_seconds"] / status["trigger_seconds"]) * 100
        print(f"{name}: {pct:.0f}%")

Parameters:

Name Type Description Default
func F

Async or sync function that receives a ProgressResult

required

Returns:

Type Description
F

The original function (unchanged)

send_agent_audio(audio_data) async

Send agent audio to the Lyra server.

Parameters:

Name Type Description Default
audio_data bytes

PCM16 audio bytes at the configured sample rate

required

send_agent_transcript(text, is_final=True) async

Send agent transcript to the Lyra server.

Parameters:

Name Type Description Default
text str

The text that the agent is speaking

required
is_final bool

Whether this is a final transcript (default: True)

True

send_user_audio(audio_data) async

Send user audio to the Lyra server.

Parameters:

Name Type Description Default
audio_data bytes

PCM16 audio bytes at the configured sample rate

required

send_user_transcript(text, is_final=True) async

Send user transcript to the Lyra server.

Parameters:

Name Type Description Default
text str

The transcribed text from the user

required
is_final bool

Whether this is a final transcript (default: True)

True

Event Handlers

Register handlers using decorators or constructor callbacks:

sentinel = SentinelClient(
    user_label="user-123",
    policies=["demo_wellbeing_awareness"],
)

# Decorator pattern (recommended)
@sentinel.on_policy_result
async def handle_policy(result: PolicyResult):
    level = result["result"]["classification"]["level"]
    if level >= 2:
        await take_action(result)

@sentinel.on_progress
async def handle_progress(result: ProgressResult):
    for name, status in result["biomarkers"].items():
        print(f"{name}: {status['speech_seconds']:.1f}s")

# Alternative: constructor callbacks
sentinel = SentinelClient(
    on_policy_result=handle_policy,
    on_progress_result=handle_progress,
    # ...
)

Multiple handlers can be registered for each event type. Both sync and async handlers are supported.


Type Definitions

PolicyResult

The main result type received from the Lyra server.

class PolicyResult(TypedDict, total=False):
    type: Literal["POLICY_RESULT"]
    policy: str                    # Executor type (e.g., "safety_analysis", "passthrough")
    policy_name: str               # Policy name (e.g., "demo_wellbeing_awareness", "demo_field_extraction")
    triggered_at_turn: int         # User turn that triggered this policy
    timestamp: float               # Unix timestamp
    result: dict                   # Policy-specific result data

Note

policy is the executor type (e.g., "safety_analysis"), while policy_name is the name of the specific policy from your org config (e.g., "demo_wellbeing_awareness"). When multiple policies share the same executor, use policy_name to distinguish them.

ProgressResult

Progress update for biomarker collection.

class ProgressResult(TypedDict, total=False):
    type: Literal["PROGRESS"]
    biomarkers: dict[str, BiomarkerProgress]
    timestamp: float

class BiomarkerProgress(TypedDict, total=False):
    speech_seconds: float          # Seconds of speech collected
    trigger_seconds: float         # Seconds required to trigger
    processing: bool               # Whether analysis is in progress

Wellbeing Awareness Analysis Types

class ReasonerClassification(TypedDict):
    level: int                     # Awareness level 0-3
    alert: str
    confidence: Literal["low", "medium", "high"]

class ReasonerRecommendedActions(TypedDict, total=False):
    for_agent: str                 # Guidance for the AI agent
    for_human_reviewer: str | None # Notes for human reviewers
    urgency: Literal["routine", "follow_up", "attentive", "supportive"]

class ReasonerConcordanceAnalysis(TypedDict, total=False):
    scenario: str                  # mood_not_discussed, concordance, minimization, amplification
    agreement_level: str           # high, moderate, low, n/a
    mismatch_type: str | None
    mismatch_severity: str         # none, mild, moderate, severe

class ReasonerFlags(TypedDict, total=False):
    suicidal_content: bool
    severe_mismatch: bool
    mood_not_yet_discussed: bool
    critical_symptoms: bool

Biomarker Summary

class ReasonerBiomarkerSummary(BaseModel):
    # Helios wellness scores (0-1)
    distress: float | None
    stress: float | None
    burnout: float | None
    fatigue: float | None
    low_self_esteem: float | None

    # Psyche scores (0-1)
    neutral: float | None
    happy: float | None
    sad: float | None
    angry: float | None
    fearful: float | None
    disgusted: float | None
    surprised: float | None

    # Apollo disorder probabilities (0-1)
    depression_probability: float | None
    anxiety_probability: float | None

    # Depression symptoms (0-1)
    symptom_anhedonia: float | None
    symptom_low_mood: float | None
    symptom_sleep_issues: float | None
    symptom_low_energy: float | None
    symptom_appetite: float | None
    symptom_worthlessness: float | None
    symptom_concentration: float | None
    symptom_psychomotor: float | None

    # Anxiety symptoms (0-1)
    symptom_nervousness: float | None
    symptom_uncontrollable_worry: float | None
    symptom_excessive_worry: float | None
    symptom_trouble_relaxing: float | None
    symptom_restlessness: float | None
    symptom_irritability: float | None
    symptom_dread: float | None

    # Summary
    interpretation: str | None

Protocol Reference

WebSocket Messages

Configuration (Client → Server)

Sent immediately after connection:

{
    "api_key": "your-api-key",
    "user_label": "user-123",
    "language": "en-GB",
    "biomarkers": ["helios", "apollo"],
    "policies": ["demo_wellbeing_awareness"],
    "audio_config": {
        "sample_rate": 16000,
        "format": "pcm16",
        "channels": 1
    },
    "progress_updates": {
        "enabled": true,
        "interval_seconds": 1.0
    }
}

Audio Header (Client → Server)

Sent before each audio chunk:

{
    "type": "AUDIO_HEADER",
    "track": "user",
    "format": "pcm16",
    "sample_rate": 16000,
    "channels": 1,
    "bytes": 3200
}

Immediately followed by raw audio bytes.

Transcript (Client → Server)

{
    "type": "TRANSCRIPT",
    "speaker": "user",
    "text": "I'm feeling okay today",
    "is_final": true,
    "language": "en-GB",
    "timestamp": 1234567890.123
}

Policy Result (Server → Client)

{
    "type": "POLICY_RESULT",
    "policy": "safety_analysis",
    "policy_name": "demo_wellbeing_awareness",
    "triggered_at_turn": 3,
    "timestamp": 1234567890.456,
    "result": {
        "type": "safety_analysis",
        "classification": { ... },
        "concerns": [ ... ],
        "recommended_actions": { ... }
    }
}

Progress (Server → Client)

{
    "type": "PROGRESS",
    "biomarkers": {
        "helios": {
            "speech_seconds": 8.2,
            "trigger_seconds": 10.0,
            "processing": false
        }
    },
    "timestamp": 1234567890.789
}

Error (Server → Client)

{
    "type": "ERROR",
    "error_code": "INVALID_CONFIG",
    "message": "Invalid date_of_birth format",
    "details": "Expected YYYY-MM-DD"
}