spatius 1.0.0__tar.gz

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.
Files changed (39) hide show
  1. spatius-1.0.0/.github/workflows/publish-testpypi.yml +40 -0
  2. spatius-1.0.0/.github/workflows/publish.yml +39 -0
  3. spatius-1.0.0/.github/workflows/test.yml +35 -0
  4. spatius-1.0.0/.gitignore +216 -0
  5. spatius-1.0.0/.python-version +1 -0
  6. spatius-1.0.0/AGENTS.md +127 -0
  7. spatius-1.0.0/CLAUDE.md +1 -0
  8. spatius-1.0.0/LICENSE +21 -0
  9. spatius-1.0.0/PKG-INFO +86 -0
  10. spatius-1.0.0/README.md +61 -0
  11. spatius-1.0.0/examples/connection_pool/README.md +222 -0
  12. spatius-1.0.0/examples/connection_pool/main.py +695 -0
  13. spatius-1.0.0/examples/http_service/Dockerfile +26 -0
  14. spatius-1.0.0/examples/http_service/main.py +256 -0
  15. spatius-1.0.0/examples/single_audio_clip/main.py +275 -0
  16. spatius-1.0.0/proto/buf.gen.yaml +4 -0
  17. spatius-1.0.0/proto/buf.yaml +11 -0
  18. spatius-1.0.0/proto/message.proto +121 -0
  19. spatius-1.0.0/pyproject.toml +63 -0
  20. spatius-1.0.0/renovate.json +6 -0
  21. spatius-1.0.0/src/spatius/__init__.py +34 -0
  22. spatius-1.0.0/src/spatius/audio_encoder.py +304 -0
  23. spatius-1.0.0/src/spatius/avatar_session.py +1086 -0
  24. spatius-1.0.0/src/spatius/errors.py +130 -0
  25. spatius-1.0.0/src/spatius/logid.py +30 -0
  26. spatius-1.0.0/src/spatius/proto/__init__.py +1 -0
  27. spatius-1.0.0/src/spatius/proto/generated/__init__.py +1 -0
  28. spatius-1.0.0/src/spatius/proto/generated/message_pb2.py +65 -0
  29. spatius-1.0.0/src/spatius/py.typed +0 -0
  30. spatius-1.0.0/src/spatius/session_config.py +274 -0
  31. spatius-1.0.0/test-local.sh +76 -0
  32. spatius-1.0.0/tests/test_audio_encoder.py +41 -0
  33. spatius-1.0.0/tests/test_avatar_session_v2.py +862 -0
  34. spatius-1.0.0/tests/test_connection_pool.py +995 -0
  35. spatius-1.0.0/tests/test_e2e_errors.py +150 -0
  36. spatius-1.0.0/tests/test_e2e_request.py +202 -0
  37. spatius-1.0.0/tests/test_public_package.py +6 -0
  38. spatius-1.0.0/tox.ini +35 -0
  39. spatius-1.0.0/uv.lock +1283 -0
@@ -0,0 +1,40 @@
1
+ name: Publish to TestPyPI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ jobs:
10
+ publish:
11
+ name: Build and publish to TestPyPI
12
+ runs-on: ubuntu-latest
13
+ environment: testpypi
14
+ permissions:
15
+ contents: read
16
+ id-token: write
17
+
18
+ steps:
19
+ - name: Checkout code
20
+ uses: actions/checkout@v6
21
+ with:
22
+ fetch-depth: 0
23
+
24
+ - name: Set up Python
25
+ uses: actions/setup-python@v6
26
+ with:
27
+ python-version: '3.14'
28
+
29
+ - name: Install uv
30
+ uses: astral-sh/setup-uv@v8.1.0
31
+ with:
32
+ version: "latest"
33
+
34
+ - name: Build package
35
+ run: uv build
36
+
37
+ - name: Publish to TestPyPI
38
+ uses: pypa/gh-action-pypi-publish@release/v1
39
+ with:
40
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,39 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ publish:
12
+ name: Build and publish to PyPI
13
+ runs-on: ubuntu-latest
14
+ environment: pypi
15
+ permissions:
16
+ contents: read
17
+ id-token: write
18
+
19
+ steps:
20
+ - name: Checkout code
21
+ uses: actions/checkout@v6
22
+ with:
23
+ fetch-depth: 0
24
+
25
+ - name: Set up Python
26
+ uses: actions/setup-python@v6
27
+ with:
28
+ python-version: '3.14'
29
+
30
+ - name: Install uv
31
+ uses: astral-sh/setup-uv@v8.1.0
32
+ with:
33
+ version: "latest"
34
+
35
+ - name: Build package
36
+ run: uv build
37
+
38
+ - name: Publish to PyPI
39
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,35 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, macos-latest, windows-latest]
16
+ python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
17
+
18
+ steps:
19
+ - uses: actions/checkout@v6
20
+
21
+ - name: Set up Python ${{ matrix.python-version }}
22
+ uses: actions/setup-python@v6
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v8.1.0
28
+ with:
29
+ enable-cache: true
30
+
31
+ - name: Install dependencies
32
+ run: uv sync
33
+
34
+ - name: Run tests
35
+ run: uv run pytest
@@ -0,0 +1,216 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+
204
+ # Ruff stuff:
205
+ .ruff_cache/
206
+
207
+ # PyPI configuration file
208
+ .pypirc
209
+
210
+ # Marimo
211
+ marimo/_static/
212
+ marimo/_lsp/
213
+ __marimo__/
214
+
215
+ # Streamlit
216
+ .streamlit/secrets.toml
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,127 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to coding agents when working with code in this repository.
4
+
5
+ ## Build and Development Commands
6
+
7
+ ```bash
8
+ # Install dependencies
9
+ uv sync
10
+
11
+ # Run tests
12
+ pytest
13
+
14
+ # Run a single test file
15
+ pytest tests/test_avatar_session_v2.py
16
+
17
+ # Run a specific test
18
+ pytest tests/test_avatar_session_v2.py::TestAvatarSessionV2::test_init_success
19
+
20
+ # Test across multiple Python versions locally
21
+ ./test-local.sh all # Test all Python versions (3.9-3.13) with all dependency combinations
22
+ ./test-local.sh py39 # Test Python 3.9 only
23
+ ./test-local.sh min # Test minimum dependency versions on all Python versions
24
+ ./test-local.sh latest # Test latest dependency versions on all Python versions
25
+ ./test-local.sh quick # Quick test on current Python version
26
+
27
+ # Regenerate protobuf code (after modifying proto/message.proto)
28
+ cd proto && buf generate
29
+ ```
30
+
31
+ ## Architecture
32
+
33
+ This is a Python SDK for WebSocket-based avatar services with audio streaming and animation frame reception. Published as `spatius` on PyPI.
34
+
35
+ ### Core Components
36
+
37
+ - **`avatar_session.py`** - Main `AvatarSession` class managing WebSocket connections, audio streaming, and frame reception. Uses v2 protocol with HTTP-based session token acquisition followed by WebSocket handshake. Exports `SessionTokenError` for token acquisition failures.
38
+
39
+ - **`session_config.py`** - `SessionConfig` dataclass, `LiveKitEgressConfig` dataclass, `AgoraEgressConfig` dataclass, and typed `new_avatar_session()` factory for session configuration.
40
+
41
+ - **`errors.py`** - `AvatarSDKError` exception with stable error codes (`AvatarSDKErrorCode` enum). Error codes: `sessionTokenExpired`, `sessionTokenInvalid`, `appIDUnrecognized`, `unknown`.
42
+
43
+ - **`logid.py`** - `generate_log_id()` utility for generating unique log IDs in format "YYYYMMDDHHMMSS_<nanoid>".
44
+
45
+ - **`proto/generated/`** - Auto-generated protobuf code from `proto/message.proto`. Message types: ClientConfigureSession, ServerConfirmSession, ClientAudioInput, ServerError, ServerResponseAnimation, ClientInterrupt.
46
+
47
+ ### Session Flow
48
+
49
+ 1. `new_avatar_session()` creates configuration
50
+ 2. `session.init()` - HTTP POST to console API for session token
51
+ 3. `session.start()` - WebSocket connection + v2 handshake, returns connection_id
52
+ 4. `session.send_audio()` - Send PCM audio via protobuf
53
+ 5. Background read loop delivers animation frames via `transport_frames` callback
54
+ 6. `session.close()` - Cleanup
55
+
56
+ ### Audio Format
57
+
58
+ Mono 16-bit PCM (s16le) only. Supported sample rates: 8000, 16000, 22050, 24000, 32000, 44100, 48000 Hz.
59
+
60
+ ### Authentication
61
+
62
+ Two modes controlled by `use_query_auth`:
63
+ - `False` (default): Headers-based auth (mobile pattern)
64
+ - `True`: Query params-based auth (web pattern)
65
+
66
+ ### LiveKit Egress Mode
67
+
68
+ When configured with `livekit_egress`, audio and animation data are streamed to a LiveKit room via the egress service instead of being returned through the WebSocket connection. The egress configuration is sent via the `ClientConfigureSession` proto message.
69
+
70
+ To use LiveKit egress mode:
71
+ 1. Configure the session with `livekit_egress=LiveKitEgressConfig(...)`
72
+ 2. Provide LiveKit connection details: url, api_key, api_secret, room_name, and publisher_id
73
+ 3. The server will create an egress connection and stream output to the LiveKit room
74
+ 4. The `transport_frames` callback will not be invoked since data goes to LiveKit
75
+
76
+ ```python
77
+ from spatius import new_avatar_session, LiveKitEgressConfig
78
+
79
+ session = new_avatar_session(
80
+ livekit_egress=LiveKitEgressConfig(
81
+ url="wss://livekit.example.com",
82
+ api_key="your-api-key",
83
+ api_secret="your-api-secret",
84
+ room_name="room-name",
85
+ publisher_id="publisher-id",
86
+ ),
87
+ # ... other options
88
+ )
89
+ ```
90
+
91
+ ### Agora Egress Mode
92
+
93
+ When configured with `agora_egress`, audio and animation data are streamed to an Agora channel via the egress service instead of being returned through the WebSocket connection. The egress configuration is sent via the `ClientConfigureSession` proto message.
94
+
95
+ To use Agora egress mode:
96
+ 1. Configure the session with `agora_egress=AgoraEgressConfig(...)`
97
+ 2. Provide Agora connection details: channel_name, token (optional for testing), uid (0 for auto-assign), and publisher_id
98
+ 3. The server will create an egress connection and stream output to the Agora channel
99
+ 4. The `transport_frames` callback will not be invoked since data goes to Agora
100
+
101
+ ```python
102
+ from spatius import new_avatar_session, AgoraEgressConfig
103
+
104
+ session = new_avatar_session(
105
+ agora_egress=AgoraEgressConfig(
106
+ channel_name="channel-name",
107
+ token="your-agora-token", # optional for testing
108
+ uid=0, # 0 for auto-assign
109
+ publisher_id="publisher-id",
110
+ ),
111
+ # ... other options
112
+ )
113
+ ```
114
+
115
+ ### Interrupt Functionality (Egress Mode Only)
116
+
117
+ The `interrupt()` method sends an interrupt signal to stop current audio processing. This is available when using egress mode (LiveKit or Agora).
118
+
119
+ ```python
120
+ # Send some audio
121
+ req_id = await session.send_audio(audio_data, end=True)
122
+
123
+ # Interrupt if needed (e.g., user wants to stop)
124
+ interrupted_id = await session.interrupt()
125
+ ```
126
+
127
+ The interrupt uses `last_req_id` which tracks the most recent request, even after `end=True` was sent. This allows interrupting requests that have finished sending audio but are still being processed.
@@ -0,0 +1 @@
1
+ AGENTS.md
spatius-1.0.0/LICENSE ADDED
@@ -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.
spatius-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: spatius
3
+ Version: 1.0.0
4
+ Summary: Python SDK for avatar WebSocket services with audio streaming and animation frame reception
5
+ Author-email: 3DRX <3drxkjy@gmail.com>
6
+ License-File: LICENSE
7
+ Keywords: animation,audio,avatar,protobuf,sdk,websocket
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Python: >=3.9
18
+ Requires-Dist: aiohttp>=3.9.0
19
+ Requires-Dist: nanoid>=2.0.0
20
+ Requires-Dist: protobuf>=5.28.3
21
+ Requires-Dist: websockets>=10.0
22
+ Provides-Extra: opus
23
+ Requires-Dist: opuslib>=3.0.1; extra == 'opus'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Spatius Python SDK
27
+
28
+ Python server SDK for creating Spatius avatar sessions.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install spatius
34
+ ```
35
+
36
+ Install the optional Ogg Opus encoder support when you want the SDK to encode raw PCM before sending:
37
+
38
+ ```bash
39
+ pip install "spatius[opus]"
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ ```python
45
+ import asyncio
46
+ from datetime import datetime, timedelta, timezone
47
+
48
+ from spatius import new_avatar_session
49
+
50
+
51
+ async def main():
52
+ session = new_avatar_session(
53
+ api_key="your-api-key",
54
+ app_id="your-app-id",
55
+ avatar_id="your-avatar-id",
56
+ expire_at=datetime.now(timezone.utc) + timedelta(minutes=5),
57
+ transport_frames=lambda frame, last: print(
58
+ f"Received frame: {len(frame)} bytes, last={last}"
59
+ ),
60
+ on_error=lambda err: print(f"Session error: {err}"),
61
+ on_close=lambda: print("Session closed"),
62
+ )
63
+
64
+ await session.init()
65
+ connection_id = await session.start()
66
+ print(f"Connected: {connection_id}")
67
+
68
+ audio_data = b"..." # mono PCM s16le audio bytes
69
+ request_id = await session.send_audio(audio_data, end=True)
70
+ print(f"Sent audio request: {request_id}")
71
+
72
+ await asyncio.sleep(10)
73
+ await session.close()
74
+
75
+
76
+ if __name__ == "__main__":
77
+ asyncio.run(main())
78
+ ```
79
+
80
+ ## Documentation
81
+
82
+ See the full Python SDK guide at [docs.spatius.ai/sdk-reference/python-sdk/python-sdk](https://docs.spatius.ai/sdk-reference/python-sdk/python-sdk).
83
+
84
+ ## License
85
+
86
+ MIT
@@ -0,0 +1,61 @@
1
+ # Spatius Python SDK
2
+
3
+ Python server SDK for creating Spatius avatar sessions.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install spatius
9
+ ```
10
+
11
+ Install the optional Ogg Opus encoder support when you want the SDK to encode raw PCM before sending:
12
+
13
+ ```bash
14
+ pip install "spatius[opus]"
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```python
20
+ import asyncio
21
+ from datetime import datetime, timedelta, timezone
22
+
23
+ from spatius import new_avatar_session
24
+
25
+
26
+ async def main():
27
+ session = new_avatar_session(
28
+ api_key="your-api-key",
29
+ app_id="your-app-id",
30
+ avatar_id="your-avatar-id",
31
+ expire_at=datetime.now(timezone.utc) + timedelta(minutes=5),
32
+ transport_frames=lambda frame, last: print(
33
+ f"Received frame: {len(frame)} bytes, last={last}"
34
+ ),
35
+ on_error=lambda err: print(f"Session error: {err}"),
36
+ on_close=lambda: print("Session closed"),
37
+ )
38
+
39
+ await session.init()
40
+ connection_id = await session.start()
41
+ print(f"Connected: {connection_id}")
42
+
43
+ audio_data = b"..." # mono PCM s16le audio bytes
44
+ request_id = await session.send_audio(audio_data, end=True)
45
+ print(f"Sent audio request: {request_id}")
46
+
47
+ await asyncio.sleep(10)
48
+ await session.close()
49
+
50
+
51
+ if __name__ == "__main__":
52
+ asyncio.run(main())
53
+ ```
54
+
55
+ ## Documentation
56
+
57
+ See the full Python SDK guide at [docs.spatius.ai/sdk-reference/python-sdk/python-sdk](https://docs.spatius.ai/sdk-reference/python-sdk/python-sdk).
58
+
59
+ ## License
60
+
61
+ MIT