livekit-plugins-spatialreal 1.3.12__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.
- livekit/plugins/spatialreal/__init__.py +37 -0
- livekit/plugins/spatialreal/avatar.py +278 -0
- livekit/plugins/spatialreal/log.py +3 -0
- livekit/plugins/spatialreal/py.typed +0 -0
- livekit/plugins/spatialreal/version.py +1 -0
- livekit_plugins_spatialreal-1.3.12.dist-info/METADATA +135 -0
- livekit_plugins_spatialreal-1.3.12.dist-info/RECORD +9 -0
- livekit_plugins_spatialreal-1.3.12.dist-info/WHEEL +4 -0
- livekit_plugins_spatialreal-1.3.12.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""SpatialReal avatar plugin for LiveKit Agents.
|
|
2
|
+
|
|
3
|
+
This plugin provides integration with SpatialReal's avatar service for
|
|
4
|
+
lip-synced avatar rendering in LiveKit voice agents.
|
|
5
|
+
|
|
6
|
+
See https://docs.spatialreal.com for more information.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from livekit.plugins.spatialreal import AvatarSession
|
|
10
|
+
|
|
11
|
+
avatar = AvatarSession()
|
|
12
|
+
await avatar.start(agent_session, room=ctx.room)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from .avatar import AvatarSession, SpatialRealException
|
|
16
|
+
from .version import __version__
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"AvatarSession",
|
|
20
|
+
"SpatialRealException",
|
|
21
|
+
"__version__",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
# Try to register plugin if Plugin class is available (livekit-agents >= 1.3)
|
|
25
|
+
try:
|
|
26
|
+
from livekit.agents import Plugin
|
|
27
|
+
|
|
28
|
+
from .log import logger
|
|
29
|
+
|
|
30
|
+
class SpatialRealPlugin(Plugin):
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
super().__init__(__name__, __version__, __package__, logger)
|
|
33
|
+
|
|
34
|
+
Plugin.register_plugin(SpatialRealPlugin())
|
|
35
|
+
except (ImportError, AttributeError):
|
|
36
|
+
# Plugin registration not available in older versions
|
|
37
|
+
pass
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SpatialReal Avatar integration for LiveKit Agents.
|
|
3
|
+
|
|
4
|
+
This module provides AvatarSession which hooks into an AgentSession
|
|
5
|
+
to route TTS audio to the SpatialReal avatar service.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import os
|
|
12
|
+
from datetime import datetime, timedelta, timezone
|
|
13
|
+
|
|
14
|
+
from avatarkit import (
|
|
15
|
+
AvatarSession as AvatarkitSession,
|
|
16
|
+
)
|
|
17
|
+
from avatarkit import (
|
|
18
|
+
LiveKitEgressConfig,
|
|
19
|
+
new_avatar_session,
|
|
20
|
+
)
|
|
21
|
+
from livekit.agents import AgentSession
|
|
22
|
+
from livekit.agents.voice.avatar import AudioSegmentEnd, QueueAudioOutput
|
|
23
|
+
|
|
24
|
+
from livekit import rtc
|
|
25
|
+
|
|
26
|
+
from .log import logger
|
|
27
|
+
|
|
28
|
+
__all__ = ["AvatarSession", "SpatialRealException"]
|
|
29
|
+
|
|
30
|
+
DEFAULT_AVATAR_PARTICIPANT_IDENTITY = "spatialreal-avatar"
|
|
31
|
+
DEFAULT_SAMPLE_RATE = 24000
|
|
32
|
+
|
|
33
|
+
# Default endpoints (China)
|
|
34
|
+
DEFAULT_CONSOLE_ENDPOINT = "https://console.us-west.spatialwalk.cloud/v1/console"
|
|
35
|
+
DEFAULT_INGRESS_ENDPOINT = "wss://api.us-west.spatialwalk.cloud/v2/driveningress"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SpatialRealException(Exception):
|
|
39
|
+
"""Exception raised for SpatialReal-related errors."""
|
|
40
|
+
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AvatarSession:
|
|
45
|
+
"""
|
|
46
|
+
This connects to SpatialReal's avatar service and routes TTS audio
|
|
47
|
+
from the agent to the avatar for lip-synced rendering. The avatar
|
|
48
|
+
service joins the LiveKit room and publishes synchronized video + audio.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
api_key: SpatialReal API key. Falls back to SPATIALREAL_API_KEY env var.
|
|
52
|
+
app_id: SpatialReal application ID. Falls back to SPATIALREAL_APP_ID env var.
|
|
53
|
+
avatar_id: Avatar ID to use. Falls back to SPATIALREAL_AVATAR_ID env var.
|
|
54
|
+
console_endpoint_url: Console endpoint URL. Falls back to
|
|
55
|
+
SPATIALREAL_CONSOLE_ENDPOINT env var or default.
|
|
56
|
+
ingress_endpoint_url: Ingress endpoint URL. Falls back to
|
|
57
|
+
SPATIALREAL_INGRESS_ENDPOINT env var or default.
|
|
58
|
+
avatar_participant_identity: LiveKit identity for the avatar participant.
|
|
59
|
+
|
|
60
|
+
Usage:
|
|
61
|
+
avatar = AvatarSession()
|
|
62
|
+
await avatar.start(session, room=ctx.room)
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
*,
|
|
68
|
+
api_key: str | None = None,
|
|
69
|
+
app_id: str | None = None,
|
|
70
|
+
avatar_id: str | None = None,
|
|
71
|
+
console_endpoint_url: str | None = None,
|
|
72
|
+
ingress_endpoint_url: str | None = None,
|
|
73
|
+
avatar_participant_identity: str | None = None,
|
|
74
|
+
) -> None:
|
|
75
|
+
# Resolve API key
|
|
76
|
+
self._api_key = api_key or os.getenv("SPATIALREAL_API_KEY")
|
|
77
|
+
if not self._api_key:
|
|
78
|
+
raise SpatialRealException(
|
|
79
|
+
"api_key must be provided or SPATIALREAL_API_KEY environment variable must be set"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Resolve app ID
|
|
83
|
+
self._app_id = app_id or os.getenv("SPATIALREAL_APP_ID")
|
|
84
|
+
if not self._app_id:
|
|
85
|
+
raise SpatialRealException("app_id must be provided or SPATIALREAL_APP_ID environment variable must be set")
|
|
86
|
+
|
|
87
|
+
# Resolve avatar ID
|
|
88
|
+
self._avatar_id = avatar_id or os.getenv("SPATIALREAL_AVATAR_ID")
|
|
89
|
+
if not self._avatar_id:
|
|
90
|
+
raise SpatialRealException(
|
|
91
|
+
"avatar_id must be provided or SPATIALREAL_AVATAR_ID environment variable must be set"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Resolve endpoints
|
|
95
|
+
self._console_endpoint_url = (
|
|
96
|
+
console_endpoint_url or os.getenv("SPATIALREAL_CONSOLE_ENDPOINT") or DEFAULT_CONSOLE_ENDPOINT
|
|
97
|
+
)
|
|
98
|
+
self._ingress_endpoint_url = (
|
|
99
|
+
ingress_endpoint_url or os.getenv("SPATIALREAL_INGRESS_ENDPOINT") or DEFAULT_INGRESS_ENDPOINT
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Avatar participant configuration
|
|
103
|
+
self._avatar_participant_identity = avatar_participant_identity or DEFAULT_AVATAR_PARTICIPANT_IDENTITY
|
|
104
|
+
|
|
105
|
+
# Internal state
|
|
106
|
+
self._avatarkit_session: AvatarkitSession | None = None
|
|
107
|
+
self._agent_session: AgentSession | None = None
|
|
108
|
+
self._audio_buffer: QueueAudioOutput | None = None
|
|
109
|
+
self._main_task: asyncio.Task | None = None
|
|
110
|
+
self._initialized = False
|
|
111
|
+
|
|
112
|
+
async def start(
|
|
113
|
+
self,
|
|
114
|
+
agent_session: AgentSession,
|
|
115
|
+
room: rtc.Room,
|
|
116
|
+
*,
|
|
117
|
+
livekit_url: str | None = None,
|
|
118
|
+
livekit_api_key: str | None = None,
|
|
119
|
+
livekit_api_secret: str | None = None,
|
|
120
|
+
) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Start the avatar session and hook into the agent session.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
agent_session: The AgentSession to hook into for TTS audio.
|
|
126
|
+
room: The LiveKit room for egress configuration.
|
|
127
|
+
livekit_url: LiveKit server URL. Falls back to LIVEKIT_URL env var.
|
|
128
|
+
livekit_api_key: LiveKit API key. Falls back to LIVEKIT_API_KEY env var.
|
|
129
|
+
livekit_api_secret: LiveKit API secret. Falls back to LIVEKIT_API_SECRET env var.
|
|
130
|
+
"""
|
|
131
|
+
if self._initialized:
|
|
132
|
+
logger.warning("Avatar session already initialized")
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
self._agent_session = agent_session
|
|
136
|
+
|
|
137
|
+
# Resolve LiveKit credentials
|
|
138
|
+
lk_url = livekit_url or os.getenv("LIVEKIT_URL")
|
|
139
|
+
lk_api_key = livekit_api_key or os.getenv("LIVEKIT_API_KEY")
|
|
140
|
+
lk_api_secret = livekit_api_secret or os.getenv("LIVEKIT_API_SECRET")
|
|
141
|
+
|
|
142
|
+
if not lk_url or not lk_api_key or not lk_api_secret:
|
|
143
|
+
raise SpatialRealException(
|
|
144
|
+
"livekit_url, livekit_api_key, and livekit_api_secret must be provided "
|
|
145
|
+
"or LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET environment variables must be set"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
room_name = room.name
|
|
149
|
+
logger.info(f"Initializing SpatialReal avatar session for room: {room_name}")
|
|
150
|
+
logger.debug(f"Console endpoint: {self._console_endpoint_url}")
|
|
151
|
+
logger.debug(f"Ingress endpoint: {self._ingress_endpoint_url}")
|
|
152
|
+
|
|
153
|
+
# Create LiveKit egress configuration for the avatar to join the room
|
|
154
|
+
livekit_egress = LiveKitEgressConfig(
|
|
155
|
+
url=lk_url,
|
|
156
|
+
api_key=lk_api_key,
|
|
157
|
+
api_secret=lk_api_secret,
|
|
158
|
+
room_name=room_name,
|
|
159
|
+
publisher_id=self._avatar_participant_identity,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Create avatar session with LiveKit egress mode
|
|
163
|
+
self._avatarkit_session = new_avatar_session(
|
|
164
|
+
api_key=self._api_key,
|
|
165
|
+
app_id=self._app_id,
|
|
166
|
+
avatar_id=self._avatar_id,
|
|
167
|
+
console_endpoint_url=self._console_endpoint_url,
|
|
168
|
+
ingress_endpoint_url=self._ingress_endpoint_url,
|
|
169
|
+
expire_at=datetime.now(timezone.utc) + timedelta(hours=1),
|
|
170
|
+
livekit_egress=livekit_egress,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Initialize and start the avatar session
|
|
174
|
+
await self._avatarkit_session.init()
|
|
175
|
+
await self._avatarkit_session.start()
|
|
176
|
+
logger.info("SpatialReal avatar session connected")
|
|
177
|
+
|
|
178
|
+
# Create audio buffer using livekit-agents' QueueAudioOutput
|
|
179
|
+
sample_rate = agent_session.tts.sample_rate if agent_session.tts else DEFAULT_SAMPLE_RATE
|
|
180
|
+
self._audio_buffer = QueueAudioOutput(sample_rate=sample_rate)
|
|
181
|
+
|
|
182
|
+
# Hook into agent session's audio output
|
|
183
|
+
agent_session.output.audio = self._audio_buffer
|
|
184
|
+
|
|
185
|
+
# Start the audio buffer
|
|
186
|
+
await self._audio_buffer.start()
|
|
187
|
+
|
|
188
|
+
# Register for clear_buffer events (interruptions)
|
|
189
|
+
@self._audio_buffer.on("clear_buffer")
|
|
190
|
+
def on_clear_buffer() -> None:
|
|
191
|
+
asyncio.create_task(self._handle_interrupt())
|
|
192
|
+
|
|
193
|
+
# Start the main task that forwards audio to avatar
|
|
194
|
+
self._main_task = asyncio.create_task(self._run_main_task())
|
|
195
|
+
|
|
196
|
+
self._initialized = True
|
|
197
|
+
logger.info("Avatar audio output attached to agent session")
|
|
198
|
+
|
|
199
|
+
# Register cleanup on session close
|
|
200
|
+
@agent_session.on("close")
|
|
201
|
+
def on_session_close() -> None:
|
|
202
|
+
asyncio.create_task(self.aclose())
|
|
203
|
+
|
|
204
|
+
async def _run_main_task(self) -> None:
|
|
205
|
+
"""Main task that forwards audio from the buffer to the avatar service."""
|
|
206
|
+
if not self._audio_buffer or not self._avatarkit_session:
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
frame_count = 0
|
|
211
|
+
async for item in self._audio_buffer:
|
|
212
|
+
if isinstance(item, rtc.AudioFrame):
|
|
213
|
+
# Convert AudioFrame to bytes and send to avatar
|
|
214
|
+
audio_bytes = bytes(item.data)
|
|
215
|
+
frame_count += 1
|
|
216
|
+
|
|
217
|
+
if frame_count == 1:
|
|
218
|
+
logger.debug("Avatar: First audio frame received")
|
|
219
|
+
|
|
220
|
+
await self._avatarkit_session.send_audio(
|
|
221
|
+
audio=audio_bytes,
|
|
222
|
+
end=False,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
elif isinstance(item, AudioSegmentEnd):
|
|
226
|
+
# End of audio segment - signal completion to avatar
|
|
227
|
+
logger.debug(f"Avatar: Segment end, sent {frame_count} frames")
|
|
228
|
+
await self._avatarkit_session.send_audio(
|
|
229
|
+
audio=b"",
|
|
230
|
+
end=True,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Notify the buffer that playback is finished
|
|
234
|
+
self._audio_buffer.notify_playback_finished(
|
|
235
|
+
playback_position=0.0,
|
|
236
|
+
interrupted=False,
|
|
237
|
+
)
|
|
238
|
+
frame_count = 0
|
|
239
|
+
|
|
240
|
+
except asyncio.CancelledError:
|
|
241
|
+
logger.debug("Avatar main task cancelled")
|
|
242
|
+
except Exception as e:
|
|
243
|
+
logger.error(f"Error in avatar main task: {e}")
|
|
244
|
+
|
|
245
|
+
async def _handle_interrupt(self) -> None:
|
|
246
|
+
"""Handle interruption - stop avatar's current audio processing."""
|
|
247
|
+
if not self._avatarkit_session:
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
interrupted_id = await self._avatarkit_session.interrupt()
|
|
252
|
+
logger.debug(f"Avatar interrupted, request_id={interrupted_id}")
|
|
253
|
+
except Exception as e:
|
|
254
|
+
logger.warning(f"Failed to interrupt avatar: {e}")
|
|
255
|
+
|
|
256
|
+
async def aclose(self) -> None:
|
|
257
|
+
"""Clean up avatar session resources."""
|
|
258
|
+
if self._main_task:
|
|
259
|
+
self._main_task.cancel()
|
|
260
|
+
try:
|
|
261
|
+
await self._main_task
|
|
262
|
+
except asyncio.CancelledError:
|
|
263
|
+
pass
|
|
264
|
+
self._main_task = None
|
|
265
|
+
|
|
266
|
+
if self._audio_buffer:
|
|
267
|
+
await self._audio_buffer.aclose()
|
|
268
|
+
self._audio_buffer = None
|
|
269
|
+
|
|
270
|
+
if self._avatarkit_session:
|
|
271
|
+
try:
|
|
272
|
+
await self._avatarkit_session.close()
|
|
273
|
+
logger.info("Avatar session closed")
|
|
274
|
+
except Exception as e:
|
|
275
|
+
logger.warning(f"Error closing avatar session: {e}")
|
|
276
|
+
finally:
|
|
277
|
+
self._avatarkit_session = None
|
|
278
|
+
self._initialized = False
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.3.12"
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: livekit-plugins-spatialreal
|
|
3
|
+
Version: 1.3.12
|
|
4
|
+
Summary: Agent Framework plugin for SpatialReal Avatar
|
|
5
|
+
Project-URL: Documentation, https://docs.spatialreal.com
|
|
6
|
+
Project-URL: Website, https://spatialreal.com/
|
|
7
|
+
Project-URL: Source, https://github.com/spatialreal/livekit-plugins-spatialreal
|
|
8
|
+
Author-email: 3DRX <3drxkjy@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai,audio,avatar,livekit,realtime,spatialreal,video,voice,webrtc
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
20
|
+
Classifier: Topic :: Multimedia :: Video
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.9.0
|
|
23
|
+
Requires-Dist: avatarkit>=0.1.3
|
|
24
|
+
Requires-Dist: livekit-agents>=1.2.9
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# LiveKit Agents Plugin for SpatialReal Avatar
|
|
28
|
+
|
|
29
|
+
This plugin provides integration with [SpatialReal](https://spatialreal.com)'s avatar service for lip-synced avatar rendering in LiveKit voice agents.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install livekit-plugins-spatialreal
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or install from source:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
Set the following environment variables:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Required
|
|
49
|
+
SPATIALREAL_API_KEY=your-api-key
|
|
50
|
+
SPATIALREAL_APP_ID=your-app-id
|
|
51
|
+
SPATIALREAL_AVATAR_ID=your-avatar-id
|
|
52
|
+
|
|
53
|
+
# Optional
|
|
54
|
+
SPATIALREAL_CONSOLE_ENDPOINT=
|
|
55
|
+
SPATIALREAL_INGRESS_ENDPOINT=
|
|
56
|
+
|
|
57
|
+
# LiveKit credentials
|
|
58
|
+
LIVEKIT_URL=
|
|
59
|
+
LIVEKIT_API_KEY=
|
|
60
|
+
LIVEKIT_API_SECRET=
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from livekit.agents import Agent, AgentSession, JobContext, cli, WorkerOptions
|
|
67
|
+
from livekit.plugins import spatialreal
|
|
68
|
+
|
|
69
|
+
class VoiceAssistant(Agent):
|
|
70
|
+
def __init__(self):
|
|
71
|
+
super().__init__(
|
|
72
|
+
instructions="You are a helpful voice assistant."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
async def entrypoint(ctx: JobContext):
|
|
76
|
+
await ctx.connect()
|
|
77
|
+
|
|
78
|
+
# Configure your pipeline components (VAD, STT, LLM, TTS)
|
|
79
|
+
session = AgentSession(
|
|
80
|
+
vad=vad,
|
|
81
|
+
stt=stt,
|
|
82
|
+
llm=llm,
|
|
83
|
+
tts=tts,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Initialize and start the avatar session
|
|
87
|
+
avatar = spatialreal.AvatarSession()
|
|
88
|
+
await avatar.start(session, room=ctx.room)
|
|
89
|
+
|
|
90
|
+
# Start the agent session
|
|
91
|
+
await session.start(
|
|
92
|
+
agent=VoiceAssistant(),
|
|
93
|
+
room=ctx.room,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## API Reference
|
|
101
|
+
|
|
102
|
+
### `AvatarSession`
|
|
103
|
+
|
|
104
|
+
Main class for integrating SpatialReal avatars with LiveKit agents.
|
|
105
|
+
|
|
106
|
+
#### Constructor Parameters
|
|
107
|
+
|
|
108
|
+
| Parameter | Type | Description |
|
|
109
|
+
|-----------|------|-------------|
|
|
110
|
+
| `api_key` | `str` | SpatialReal API key (or set `SPATIALREAL_API_KEY`) |
|
|
111
|
+
| `app_id` | `str` | SpatialReal application ID (or set `SPATIALREAL_APP_ID`) |
|
|
112
|
+
| `avatar_id` | `str` | Avatar ID to use (or set `SPATIALREAL_AVATAR_ID`) |
|
|
113
|
+
| `console_endpoint_url` | `str` | Custom console endpoint URL |
|
|
114
|
+
| `ingress_endpoint_url` | `str` | Custom ingress endpoint URL |
|
|
115
|
+
| `avatar_participant_identity` | `str` | LiveKit identity for avatar participant |
|
|
116
|
+
|
|
117
|
+
#### Methods
|
|
118
|
+
|
|
119
|
+
- `start(agent_session, room, *, livekit_url, livekit_api_key, livekit_api_secret)`: Start the avatar session and hook into the agent's audio output.
|
|
120
|
+
- `aclose()`: Clean up avatar session resources.
|
|
121
|
+
|
|
122
|
+
### `SpatialRealException`
|
|
123
|
+
|
|
124
|
+
Exception raised for SpatialReal-related errors.
|
|
125
|
+
|
|
126
|
+
## How It Works
|
|
127
|
+
|
|
128
|
+
1. The plugin intercepts TTS audio output from the agent session
|
|
129
|
+
2. Audio frames are forwarded to SpatialReal's avatar service
|
|
130
|
+
3. SpatialReal generates lip-synced video and audio
|
|
131
|
+
4. The avatar joins the LiveKit room and publishes the synchronized streams
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
livekit/plugins/spatialreal/__init__.py,sha256=wp1ICd36yzIT7zK1EVy7NJUfCNC-BsLWmcNK3Ww8Ds0,1008
|
|
2
|
+
livekit/plugins/spatialreal/avatar.py,sha256=GStcE-40MJi1pLRmQhom-cPp-_a0p_8sUtVLWnmTCiM,10510
|
|
3
|
+
livekit/plugins/spatialreal/log.py,sha256=Gi7neqj1wBakfvLRZY98c8D9FL8UJn-BDExJx0hTwwc,74
|
|
4
|
+
livekit/plugins/spatialreal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
livekit/plugins/spatialreal/version.py,sha256=2Rgd7p7gALBiXufv4uSeaC9anR_muEfi4m5NMAgfMHI,23
|
|
6
|
+
livekit_plugins_spatialreal-1.3.12.dist-info/METADATA,sha256=PJs0t02iuBJJni6wW6kfUTFf4SsVsM9fcqbM9erPXMM,3867
|
|
7
|
+
livekit_plugins_spatialreal-1.3.12.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
+
livekit_plugins_spatialreal-1.3.12.dist-info/licenses/LICENSE,sha256=J0AyxvqWXwDXalRKg9CQzVxw0-xT8tu2h-316a-9dY8,1068
|
|
9
|
+
livekit_plugins_spatialreal-1.3.12.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 spatialwalk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|