spatialreal 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,52 @@
1
+ # Copyright 2026 SpatialReal.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """SpatialReal Python SDK — realtime avatar sessions.
16
+
17
+ Successor to the retired ``avatarkit`` package: same wire protocol and session
18
+ semantics, one import swap for existing callers, plus structured playback
19
+ events (``on_playback``) and the WebSocket close-code contract (``CloseCode``,
20
+ ``AvatarSDKError.retryable``).
21
+ """
22
+
23
+ from .config import (
24
+ AgoraEgressConfig,
25
+ AudioFormat,
26
+ LiveKitEgressConfig,
27
+ SessionConfig,
28
+ )
29
+ from .errors import AvatarSDKError, AvatarSDKErrorCode, CloseCode, SessionTokenError
30
+ from .events import InterruptReason, PlaybackSignal, PlaybackState, PlaybackStateEvent
31
+ from .logid import generate_log_id
32
+ from .session import AvatarSession, new_avatar_session
33
+ from .version import __version__
34
+
35
+ __all__ = [
36
+ "AgoraEgressConfig",
37
+ "AudioFormat",
38
+ "AvatarSDKError",
39
+ "AvatarSDKErrorCode",
40
+ "AvatarSession",
41
+ "CloseCode",
42
+ "LiveKitEgressConfig",
43
+ "PlaybackSignal",
44
+ "PlaybackStateEvent",
45
+ "PlaybackState",
46
+ "InterruptReason",
47
+ "SessionConfig",
48
+ "SessionTokenError",
49
+ "__version__",
50
+ "generate_log_id",
51
+ "new_avatar_session",
52
+ ]
spatialreal/config.py ADDED
@@ -0,0 +1,101 @@
1
+ """Session configuration types."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass, field
7
+ from datetime import datetime
8
+ from enum import Enum
9
+
10
+ from .events import PlaybackSignal, PlaybackStateEvent
11
+
12
+
13
+ class AudioFormat(str, Enum):
14
+ """Audio input encoding negotiated for a session.
15
+
16
+ ``PCM_S16LE``: raw PCM16 mono at ``sample_rate``.
17
+ ``OGG_OPUS``: one continuous, pre-encoded Ogg Opus stream per request id
18
+ (this SDK does not encode client-side; send encoded bytes).
19
+ """
20
+
21
+ PCM_S16LE = "pcm_s16le"
22
+ OGG_OPUS = "ogg_opus"
23
+
24
+
25
+ @dataclass
26
+ class LiveKitEgressConfig:
27
+ """Stream the avatar's audio/video into a LiveKit room via the egress service.
28
+
29
+ When set on a session, animation is NOT returned over the WebSocket; the
30
+ egress worker joins the room and publishes synchronized tracks. The WebSocket
31
+ carries only control/lifecycle messages.
32
+ """
33
+
34
+ url: str = ""
35
+ api_token: str = field(default="", repr=False)
36
+ room_name: str = ""
37
+ publisher_id: str = ""
38
+ extra_attributes: dict[str, str] = field(default_factory=dict)
39
+ # 0 = server default. Server closes the egress connection when completely silent.
40
+ idle_timeout: int = 0
41
+ # Deprecated pair, kept for wire compatibility; prefer api_token.
42
+ api_key: str = field(default="", repr=False)
43
+ api_secret: str = field(default="", repr=False)
44
+
45
+
46
+ @dataclass
47
+ class AgoraEgressConfig:
48
+ """Stream the avatar's audio/video into an Agora channel via the egress service."""
49
+
50
+ channel_name: str = ""
51
+ token: str = field(default="", repr=False)
52
+ uid: int = 0
53
+ publisher_id: str = ""
54
+
55
+
56
+ @dataclass
57
+ class SessionConfig:
58
+ """Configuration for an :class:`~spatialreal.AvatarSession`.
59
+
60
+ Callback contract (all callbacks run on the session's event loop and must
61
+ not block):
62
+
63
+ - ``on_playback(signal)``: structured playback lifecycle parsed from
64
+ ``ServerResponseAnimation`` — preferred over ``transport_frames``.
65
+ - ``on_playback_state(event)``: structured ``ServerPlaybackState`` for
66
+ egress-mode playback control (pause/resume/interrupt position); only
67
+ arrives when the server declares the ``playback_state`` capability.
68
+ - ``transport_frames(frame_bytes, is_last)``: the raw serialized ``Message``
69
+ envelope, for callers that decode the protobuf themselves.
70
+ - ``on_error(error)``: transport/server errors outside a call site (read
71
+ loop). Errors raised by ``send_audio``/``interrupt`` are raised, not
72
+ routed here.
73
+ - ``on_close()``: fired exactly once when the session ends, whether by
74
+ ``close()`` or by the connection dropping.
75
+ """
76
+
77
+ avatar_id: str = ""
78
+ api_key: str = field(default="", repr=False)
79
+ app_id: str = ""
80
+ # v2 auth style: False = headers (mobile-style), True = URL query params (web-style).
81
+ use_query_auth: bool = False
82
+ expire_at: datetime | None = None
83
+ sample_rate: int = 16000
84
+ bitrate: int = 0
85
+ audio_format: AudioFormat = AudioFormat.PCM_S16LE
86
+ console_endpoint_url: str = ""
87
+ ingress_endpoint_url: str = ""
88
+ livekit_egress: LiveKitEgressConfig | None = None
89
+ agora_egress: AgoraEgressConfig | None = None
90
+
91
+ on_playback: Callable[[PlaybackSignal], None] | None = None
92
+ on_playback_state: Callable[[PlaybackStateEvent], None] | None = None
93
+ transport_frames: Callable[[bytes, bool], None] | None = None
94
+ on_error: Callable[[Exception], None] | None = None
95
+ on_close: Callable[[], None] | None = None
96
+
97
+ # Timeout for the console session-token HTTP call.
98
+ token_request_timeout: float = 30.0
99
+
100
+ def __post_init__(self) -> None:
101
+ self.audio_format = AudioFormat(self.audio_format)
spatialreal/errors.py ADDED
@@ -0,0 +1,155 @@
1
+ """Error taxonomy for the SpatialReal SDK.
2
+
3
+ Error codes keep the names the retired avatarkit SDK used, so migrating callers
4
+ can keep their logging/branching. New in this SDK: ``CloseCode`` mirrors the
5
+ server's WebSocket close-code contract (40xx = the request is at fault, retrying
6
+ changes nothing; 45xx = the server is briefly unable, retry with backoff), and
7
+ ``AvatarSDKError.retryable`` answers "should I reconnect?" so callers don't have
8
+ to memorize the ranges.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from enum import Enum, IntEnum
14
+
15
+
16
+ class AvatarSDKErrorCode(str, Enum):
17
+ """Stable, log-friendly error codes surfaced by the SDK."""
18
+
19
+ sessionTokenExpired = "sessionTokenExpired"
20
+ sessionTokenInvalid = "sessionTokenInvalid"
21
+ appIDUnrecognized = "appIDUnrecognized"
22
+ appIDMismatch = "appIDMismatch"
23
+ avatarNotFound = "avatarNotFound"
24
+ billingRequired = "billingRequired"
25
+ creditsExhausted = "creditsExhausted"
26
+ sessionDurationExceeded = "sessionDurationExceeded"
27
+ concurrencyLimit = "concurrencyLimit"
28
+ unsupportedSampleRate = "unsupportedSampleRate"
29
+ invalidEgressConfig = "invalidEgressConfig"
30
+ egressUnavailable = "egressUnavailable"
31
+ idleTimeout = "idleTimeout"
32
+ upstreamError = "upstreamError"
33
+ invalidRequest = "invalidRequest"
34
+ connectionFailed = "connectionFailed"
35
+ connectionClosed = "connectionClosed"
36
+ protocolError = "protocolError"
37
+ serverError = "serverError"
38
+ unknown = "unknown"
39
+
40
+
41
+ class CloseCode(IntEnum):
42
+ """WebSocket close codes the server sends when it ends a session.
43
+
44
+ A client contract, not diagnostics (mirrors driveningress/v2 proto):
45
+ 40xx — the request itself is at fault; do not reconnect.
46
+ 45xx — transient server-side trouble; reconnect with backoff.
47
+ """
48
+
49
+ CREDITS_EXHAUSTED = 4001
50
+ SESSION_TIMEOUT = 4002
51
+ CONCURRENCY_LIMIT = 4003
52
+ AUTH_FAILED = 4010
53
+ UPSTREAM_UNAVAILABLE = 4503
54
+
55
+
56
+ # Close codes for which reconnecting is expected to work.
57
+ _RETRYABLE_CLOSE_CODES = frozenset(
58
+ {
59
+ int(CloseCode.SESSION_TIMEOUT), # proto: "Reconnect is allowed and expected"
60
+ int(CloseCode.UPSTREAM_UNAVAILABLE),
61
+ }
62
+ )
63
+
64
+ _CLOSE_CODE_TO_ERROR = {
65
+ int(CloseCode.CREDITS_EXHAUSTED): AvatarSDKErrorCode.creditsExhausted,
66
+ int(CloseCode.SESSION_TIMEOUT): AvatarSDKErrorCode.sessionDurationExceeded,
67
+ int(CloseCode.CONCURRENCY_LIMIT): AvatarSDKErrorCode.concurrencyLimit,
68
+ int(CloseCode.AUTH_FAILED): AvatarSDKErrorCode.sessionTokenInvalid,
69
+ int(CloseCode.UPSTREAM_UNAVAILABLE): AvatarSDKErrorCode.upstreamError,
70
+ }
71
+
72
+
73
+ class AvatarSDKError(Exception):
74
+ """SDK exception with a stable error code and structured context."""
75
+
76
+ def __init__(
77
+ self,
78
+ code: AvatarSDKErrorCode,
79
+ message: str,
80
+ *,
81
+ phase: str = "unknown",
82
+ http_status: int | None = None,
83
+ server_code: str | None = None,
84
+ server_detail: str | None = None,
85
+ connection_id: str | None = None,
86
+ req_id: str | None = None,
87
+ raw_body: str | None = None,
88
+ close_code: int | None = None,
89
+ close_reason: str | None = None,
90
+ ):
91
+ super().__init__(message)
92
+ self.code = code
93
+ self.message = message
94
+ self.phase = phase
95
+ self.http_status = http_status
96
+ self.server_code = server_code
97
+ self.server_detail = server_detail
98
+ self.connection_id = connection_id
99
+ self.req_id = req_id
100
+ self.raw_body = raw_body
101
+ self.close_code = close_code
102
+ self.close_reason = close_reason
103
+
104
+ @property
105
+ def retryable(self) -> bool:
106
+ """Whether reconnecting can plausibly succeed, per the close-code contract.
107
+
108
+ Unknown/absent close codes count as retryable: transport-level failures
109
+ (network blips, process restarts) close without our 40xx codes, and for
110
+ those, giving up for good is the wrong default.
111
+ """
112
+ if self.close_code is None:
113
+ return self.code in (
114
+ AvatarSDKErrorCode.connectionFailed,
115
+ AvatarSDKErrorCode.connectionClosed,
116
+ AvatarSDKErrorCode.upstreamError,
117
+ )
118
+ if self.close_code in _RETRYABLE_CLOSE_CODES:
119
+ return True
120
+ if 4000 <= self.close_code < 4500:
121
+ return False
122
+ return True
123
+
124
+ def __str__(self) -> str: # pragma: no cover
125
+ return f"{self.code.value}: {self.message}"
126
+
127
+
128
+ class SessionTokenError(AvatarSDKError):
129
+ """Raised when the session-token exchange with the console API fails."""
130
+
131
+ def __init__(
132
+ self,
133
+ message: str,
134
+ *,
135
+ code: AvatarSDKErrorCode = AvatarSDKErrorCode.invalidRequest,
136
+ http_status: int | None = None,
137
+ server_code: str | None = None,
138
+ server_detail: str | None = None,
139
+ raw_body: str | None = None,
140
+ ):
141
+ super().__init__(
142
+ code=code,
143
+ message=message,
144
+ phase="session_token",
145
+ http_status=http_status,
146
+ server_code=server_code,
147
+ server_detail=server_detail,
148
+ raw_body=raw_body,
149
+ )
150
+
151
+
152
+ def error_code_for_close_code(close_code: int | None) -> AvatarSDKErrorCode:
153
+ if close_code is None:
154
+ return AvatarSDKErrorCode.connectionClosed
155
+ return _CLOSE_CODE_TO_ERROR.get(close_code, AvatarSDKErrorCode.connectionClosed)
spatialreal/events.py ADDED
@@ -0,0 +1,66 @@
1
+ """Structured events the SDK surfaces to callers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class PlaybackSignal:
11
+ """Playback lifecycle parsed from a ``ServerResponseAnimation`` message.
12
+
13
+ Semantics depend on the delivery mode:
14
+
15
+ - WebSocket direct return: frames carry animation data; ``end=True`` means
16
+ generation for the request is complete.
17
+ - LiveKit/Agora egress: frames are empty control signals; a non-end frame
18
+ marks playback progress/start and ``end=True`` means the egress playout
19
+ for the request has fully drained. Egress-path retransmission can deliver
20
+ ``end=True`` more than once per ``req_id`` — dedupe on the consumer side.
21
+ """
22
+
23
+ req_id: str
24
+ end: bool
25
+ connection_id: str = ""
26
+ avatar_id: str = ""
27
+ has_animation: bool = False
28
+
29
+
30
+ class PlaybackState(str, Enum):
31
+ """State reported by a ``ServerPlaybackState`` message (egress mode)."""
32
+
33
+ UNSPECIFIED = "unspecified"
34
+ PLAYING = "playing"
35
+ PAUSED = "paused"
36
+ ENDED = "ended"
37
+ INTERRUPTED = "interrupted"
38
+
39
+
40
+ class InterruptReason(str, Enum):
41
+ """``reason`` on an INTERRUPTED playback state."""
42
+
43
+ EXPLICIT = "explicit" # an explicit interrupt() / client interrupt
44
+ PREEMPTED = "preempted" # a newer request superseded this one
45
+ PAUSE_TIMEOUT = "pause_timeout" # a held pause exceeded the server limit
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class PlaybackStateEvent:
50
+ """Structured ``ServerPlaybackState`` (egress-mode playback control, #51).
51
+
52
+ Emitted for every ``pause()``/``resume()`` (the state *after* the operation)
53
+ and spontaneously by the server on playback start/end/interrupt. Only sent
54
+ when the server declared the ``playback_state`` capability; older servers
55
+ never send it. ``played_ms`` is content playout position (excludes
56
+ transitions, does not advance while paused). ``reason`` is meaningful only
57
+ for ``INTERRUPTED`` — notably ``pause_timeout`` means a held pause hit the
58
+ server limit and the caller should treat the segment as interrupted (drop
59
+ retained audio, do not resume).
60
+ """
61
+
62
+ req_id: str
63
+ state: PlaybackState
64
+ played_ms: int = 0
65
+ reason: str = ""
66
+ connection_id: str = ""
spatialreal/logid.py ADDED
@@ -0,0 +1,19 @@
1
+ """Request/log id generation.
2
+
3
+ Format is shared with the Go backend and the retired avatarkit SDK:
4
+ "YYYYMMDDHHMMSS_<nanoid12>" (UTC). Every request gets a fresh id; ids are
5
+ never reused within a connection — the plugin's interrupt bookkeeping and the
6
+ backend's session mapping both rely on uniqueness.
7
+ """
8
+
9
+ from datetime import datetime, timezone
10
+
11
+ from nanoid import generate
12
+
13
+ LOG_ID_TIME_FORMAT = "%Y%m%d%H%M%S"
14
+ LOG_ID_NANOID_LENGTH = 12
15
+
16
+
17
+ def generate_log_id() -> str:
18
+ timestamp = datetime.now(timezone.utc).strftime(LOG_ID_TIME_FORMAT)
19
+ return f"{timestamp}_{generate(size=LOG_ID_NANOID_LENGTH)}"
File without changes
File without changes
@@ -0,0 +1,96 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: driveningress/v2/driveningress.proto
5
+ # Protobuf Python Version: 6.31.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 31,
16
+ 1,
17
+ '',
18
+ 'driveningress/v2/driveningress.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2
26
+
27
+
28
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$driveningress/v2/driveningress.proto\x12\x10\x64riveningress.v2\x1a\x1cgoogle/protobuf/struct.proto\"\xa7\x02\n\x13LiveKitEgressConfig\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0f\n\x07\x61pi_key\x18\x02 \x01(\t\x12\x12\n\napi_secret\x18\x03 \x01(\t\x12\x11\n\troom_name\x18\x04 \x01(\t\x12\x14\n\x0cpublisher_id\x18\x05 \x01(\t\x12T\n\x10\x65xtra_attributes\x18\x06 \x03(\x0b\x32:.driveningress.v2.LiveKitEgressConfig.ExtraAttributesEntry\x12\x14\n\x0cidle_timeout\x18\x07 \x01(\x05\x12\x11\n\tapi_token\x18\x08 \x01(\t\x1a\x36\n\x14\x45xtraAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"[\n\x11\x41goraEgressConfig\x12\x14\n\x0c\x63hannel_name\x18\x01 \x01(\t\x12\r\n\x05token\x18\x02 \x01(\t\x12\x0b\n\x03uid\x18\x03 \x01(\r\x12\x14\n\x0cpublisher_id\x18\x04 \x01(\t\"\xa4\x01\n\x13\x44rivenIngressConfig\x12\x11\n\tshape_npy\x18\x01 \x01(\x0c\x12\x11\n\tstyle_npy\x18\x02 \x01(\x0c\x12\x19\n\x11\x64riven_server_url\x18\x03 \x01(\t\x12/\n\x0emodel_settings\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x1b\n\x13\x65ncoder_start_frame\x18\x05 \x01(\x05\"\xe7\x02\n\x16\x43lientConfigureSession\x12\x13\n\x0bsample_rate\x18\x01 \x01(\x05\x12\x0f\n\x07\x62itrate\x18\x02 \x01(\x05\x12\x33\n\x0c\x61udio_format\x18\x03 \x01(\x0e\x32\x1d.driveningress.v2.AudioFormat\x12\x45\n\x15transport_compression\x18\x04 \x01(\x0e\x32&.driveningress.v2.TransportCompression\x12\x31\n\x0b\x65gress_type\x18\x05 \x01(\x0e\x32\x1c.driveningress.v2.EgressType\x12=\n\x0elivekit_egress\x18\x06 \x01(\x0b\x32%.driveningress.v2.LiveKitEgressConfig\x12\x39\n\x0c\x61gora_egress\x18\x07 \x01(\x0b\x32#.driveningress.v2.AgoraEgressConfig\"C\n\x14ServerConfirmSession\x12\x15\n\rconnection_id\x18\x01 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x02 \x03(\t\">\n\x10\x43lientAudioInput\x12\x0e\n\x06req_id\x18\x01 \x01(\t\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x08\x12\r\n\x05\x61udio\x18\x03 \x01(\x0c\"S\n\x0bServerError\x12\x15\n\rconnection_id\x18\x01 \x01(\t\x12\x0e\n\x06req_id\x18\x02 \x01(\t\x12\x0c\n\x04\x63ode\x18\x03 \x01(\x05\x12\x0f\n\x07message\x18\x04 \x01(\t\"\xa6\x01\n\x05\x46lame\x12\x17\n\x0btranslation\x18\x01 \x03(\x02\x42\x02\x10\x01\x12\x14\n\x08rotation\x18\x02 \x03(\x02\x42\x02\x10\x01\x12\x15\n\tneck_pose\x18\x03 \x03(\x02\x42\x02\x10\x01\x12\x14\n\x08jaw_pose\x18\x04 \x03(\x02\x42\x02\x10\x01\x12\x14\n\x08\x65ye_pose\x18\x05 \x03(\x02\x42\x02\x10\x01\x12\x13\n\x07\x65ye_lid\x18\x06 \x03(\x02\x42\x02\x10\x01\x12\x16\n\nexpression\x18\x07 \x03(\x02\x42\x02\x10\x01\"<\n\x0e\x46lameAnimation\x12*\n\tkeyframes\x18\x01 \x03(\x0b\x32\x17.driveningress.v2.Flame\"\x95\x01\n\x17ServerResponseAnimation\x12\x15\n\rconnection_id\x18\x01 \x01(\t\x12\x0e\n\x06req_id\x18\x02 \x01(\t\x12\x0b\n\x03\x65nd\x18\x03 \x01(\x08\x12\x33\n\tanimation\x18\x04 \x01(\x0b\x32 .driveningress.v2.FlameAnimation\x12\x11\n\tavatar_id\x18\x05 \x01(\t\"!\n\x0f\x43lientInterrupt\x12\x0e\n\x06req_id\x18\x01 \x01(\t\"\x1d\n\x0b\x43lientPause\x12\x0e\n\x06req_id\x18\x01 \x01(\t\"\x1e\n\x0c\x43lientResume\x12\x0e\n\x06req_id\x18\x01 \x01(\t\"\xf0\x01\n\x13ServerPlaybackState\x12\x15\n\rconnection_id\x18\x01 \x01(\t\x12\x0e\n\x06req_id\x18\x02 \x01(\t\x12:\n\x05state\x18\x03 \x01(\x0e\x32+.driveningress.v2.ServerPlaybackState.State\x12\x11\n\tplayed_ms\x18\x04 \x01(\x03\x12\x0e\n\x06reason\x18\x05 \x01(\t\"S\n\x05State\x12\x15\n\x11STATE_UNSPECIFIED\x10\x00\x12\x0b\n\x07PLAYING\x10\x01\x12\n\n\x06PAUSED\x10\x02\x12\t\n\x05\x45NDED\x10\x03\x12\x0f\n\x0bINTERRUPTED\x10\x04\"\xd6\x05\n\x07Message\x12+\n\x04type\x18\x01 \x01(\x0e\x32\x1d.driveningress.v2.MessageType\x12L\n\x18\x63lient_configure_session\x18\x02 \x01(\x0b\x32(.driveningress.v2.ClientConfigureSessionH\x00\x12H\n\x16server_confirm_session\x18\x03 \x01(\x0b\x32&.driveningress.v2.ServerConfirmSessionH\x00\x12@\n\x12\x63lient_audio_input\x18\x04 \x01(\x0b\x32\".driveningress.v2.ClientAudioInputH\x00\x12\x35\n\x0cserver_error\x18\x05 \x01(\x0b\x32\x1d.driveningress.v2.ServerErrorH\x00\x12N\n\x19server_response_animation\x18\x06 \x01(\x0b\x32).driveningress.v2.ServerResponseAnimationH\x00\x12>\n\rdriven_config\x18\x07 \x01(\x0b\x32%.driveningress.v2.DrivenIngressConfigH\x00\x12=\n\x10\x63lient_interrupt\x18\x08 \x01(\x0b\x32!.driveningress.v2.ClientInterruptH\x00\x12\x35\n\x0c\x63lient_pause\x18\t \x01(\x0b\x32\x1d.driveningress.v2.ClientPauseH\x00\x12\x37\n\rclient_resume\x18\n \x01(\x0b\x32\x1e.driveningress.v2.ClientResumeH\x00\x12\x46\n\x15server_playback_state\x18\x0b \x01(\x0b\x32%.driveningress.v2.ServerPlaybackStateH\x00\x42\x06\n\x04\x64\x61ta*\xe9\x02\n\x0bMessageType\x12\x17\n\x13MESSAGE_UNSPECIFIED\x10\x00\x12$\n MESSAGE_CLIENT_CONFIGURE_SESSION\x10\x01\x12\"\n\x1eMESSAGE_SERVER_CONFIRM_SESSION\x10\x02\x12\x1e\n\x1aMESSAGE_CLIENT_AUDIO_INPUT\x10\x03\x12\x18\n\x14MESSAGE_SERVER_ERROR\x10\x04\x12%\n!MESSAGE_SERVER_RESPONSE_ANIMATION\x10\x05\x12 \n\x1cMESSAGE_CLIENT_DRIVEN_CONFIG\x10\x06\x12\x1c\n\x18MESSAGE_CLIENT_INTERRUPT\x10\x07\x12\x18\n\x14MESSAGE_CLIENT_PAUSE\x10\x08\x12\x19\n\x15MESSAGE_CLIENT_RESUME\x10\t\x12!\n\x1dMESSAGE_SERVER_PLAYBACK_STATE\x10\n*D\n\x0b\x41udioFormat\x12\x1a\n\x16\x41UDIO_FORMAT_PCM_S16LE\x10\x00\x12\x19\n\x15\x41UDIO_FORMAT_OGG_OPUS\x10\x01*6\n\x14TransportCompression\x12\x1e\n\x1aTRANSPORT_COMPRESSION_NONE\x10\x00*Y\n\nEgressType\x12\x1b\n\x17\x45GRESS_TYPE_UNSPECIFIED\x10\x00\x12\x17\n\x13\x45GRESS_TYPE_LIVEKIT\x10\x01\x12\x15\n\x11\x45GRESS_TYPE_AGORA\x10\x02*\xd1\x01\n\tCloseCode\x12\x1a\n\x16\x43LOSE_CODE_UNSPECIFIED\x10\x00\x12!\n\x1c\x43LOSE_CODE_CREDITS_EXHAUSTED\x10\xa1\x1f\x12\x1f\n\x1a\x43LOSE_CODE_SESSION_TIMEOUT\x10\xa2\x1f\x12!\n\x1c\x43LOSE_CODE_CONCURRENCY_LIMIT\x10\xa3\x1f\x12\x1b\n\x16\x43LOSE_CODE_AUTH_FAILED\x10\xaa\x1f\x12$\n\x1f\x43LOSE_CODE_UPSTREAM_UNAVAILABLE\x10\x97#BEZCgithub.com/SpatialReal-ai/backend-ng/api/generated/driveningress/v2b\x06proto3')
29
+
30
+ _globals = globals()
31
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
32
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'driveningress.v2.driveningress_pb2', _globals)
33
+ if not _descriptor._USE_C_DESCRIPTORS:
34
+ _globals['DESCRIPTOR']._loaded_options = None
35
+ _globals['DESCRIPTOR']._serialized_options = b'ZCgithub.com/SpatialReal-ai/backend-ng/api/generated/driveningress/v2'
36
+ _globals['_LIVEKITEGRESSCONFIG_EXTRAATTRIBUTESENTRY']._loaded_options = None
37
+ _globals['_LIVEKITEGRESSCONFIG_EXTRAATTRIBUTESENTRY']._serialized_options = b'8\001'
38
+ _globals['_FLAME'].fields_by_name['translation']._loaded_options = None
39
+ _globals['_FLAME'].fields_by_name['translation']._serialized_options = b'\020\001'
40
+ _globals['_FLAME'].fields_by_name['rotation']._loaded_options = None
41
+ _globals['_FLAME'].fields_by_name['rotation']._serialized_options = b'\020\001'
42
+ _globals['_FLAME'].fields_by_name['neck_pose']._loaded_options = None
43
+ _globals['_FLAME'].fields_by_name['neck_pose']._serialized_options = b'\020\001'
44
+ _globals['_FLAME'].fields_by_name['jaw_pose']._loaded_options = None
45
+ _globals['_FLAME'].fields_by_name['jaw_pose']._serialized_options = b'\020\001'
46
+ _globals['_FLAME'].fields_by_name['eye_pose']._loaded_options = None
47
+ _globals['_FLAME'].fields_by_name['eye_pose']._serialized_options = b'\020\001'
48
+ _globals['_FLAME'].fields_by_name['eye_lid']._loaded_options = None
49
+ _globals['_FLAME'].fields_by_name['eye_lid']._serialized_options = b'\020\001'
50
+ _globals['_FLAME'].fields_by_name['expression']._loaded_options = None
51
+ _globals['_FLAME'].fields_by_name['expression']._serialized_options = b'\020\001'
52
+ _globals['_MESSAGETYPE']._serialized_start=2680
53
+ _globals['_MESSAGETYPE']._serialized_end=3041
54
+ _globals['_AUDIOFORMAT']._serialized_start=3043
55
+ _globals['_AUDIOFORMAT']._serialized_end=3111
56
+ _globals['_TRANSPORTCOMPRESSION']._serialized_start=3113
57
+ _globals['_TRANSPORTCOMPRESSION']._serialized_end=3167
58
+ _globals['_EGRESSTYPE']._serialized_start=3169
59
+ _globals['_EGRESSTYPE']._serialized_end=3258
60
+ _globals['_CLOSECODE']._serialized_start=3261
61
+ _globals['_CLOSECODE']._serialized_end=3470
62
+ _globals['_LIVEKITEGRESSCONFIG']._serialized_start=89
63
+ _globals['_LIVEKITEGRESSCONFIG']._serialized_end=384
64
+ _globals['_LIVEKITEGRESSCONFIG_EXTRAATTRIBUTESENTRY']._serialized_start=330
65
+ _globals['_LIVEKITEGRESSCONFIG_EXTRAATTRIBUTESENTRY']._serialized_end=384
66
+ _globals['_AGORAEGRESSCONFIG']._serialized_start=386
67
+ _globals['_AGORAEGRESSCONFIG']._serialized_end=477
68
+ _globals['_DRIVENINGRESSCONFIG']._serialized_start=480
69
+ _globals['_DRIVENINGRESSCONFIG']._serialized_end=644
70
+ _globals['_CLIENTCONFIGURESESSION']._serialized_start=647
71
+ _globals['_CLIENTCONFIGURESESSION']._serialized_end=1006
72
+ _globals['_SERVERCONFIRMSESSION']._serialized_start=1008
73
+ _globals['_SERVERCONFIRMSESSION']._serialized_end=1075
74
+ _globals['_CLIENTAUDIOINPUT']._serialized_start=1077
75
+ _globals['_CLIENTAUDIOINPUT']._serialized_end=1139
76
+ _globals['_SERVERERROR']._serialized_start=1141
77
+ _globals['_SERVERERROR']._serialized_end=1224
78
+ _globals['_FLAME']._serialized_start=1227
79
+ _globals['_FLAME']._serialized_end=1393
80
+ _globals['_FLAMEANIMATION']._serialized_start=1395
81
+ _globals['_FLAMEANIMATION']._serialized_end=1455
82
+ _globals['_SERVERRESPONSEANIMATION']._serialized_start=1458
83
+ _globals['_SERVERRESPONSEANIMATION']._serialized_end=1607
84
+ _globals['_CLIENTINTERRUPT']._serialized_start=1609
85
+ _globals['_CLIENTINTERRUPT']._serialized_end=1642
86
+ _globals['_CLIENTPAUSE']._serialized_start=1644
87
+ _globals['_CLIENTPAUSE']._serialized_end=1673
88
+ _globals['_CLIENTRESUME']._serialized_start=1675
89
+ _globals['_CLIENTRESUME']._serialized_end=1705
90
+ _globals['_SERVERPLAYBACKSTATE']._serialized_start=1708
91
+ _globals['_SERVERPLAYBACKSTATE']._serialized_end=1948
92
+ _globals['_SERVERPLAYBACKSTATE_STATE']._serialized_start=1865
93
+ _globals['_SERVERPLAYBACKSTATE_STATE']._serialized_end=1948
94
+ _globals['_MESSAGE']._serialized_start=1951
95
+ _globals['_MESSAGE']._serialized_end=2677
96
+ # @@protoc_insertion_point(module_scope)