liveavatar-platform-rtc 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 (63) hide show
  1. liveavatar_platform_rtc-1.0.0/.codegraph/.gitignore +16 -0
  2. liveavatar_platform_rtc-1.0.0/.codegraph/codegraph.db +0 -0
  3. liveavatar_platform_rtc-1.0.0/.codegraph/codegraph.db-shm +0 -0
  4. liveavatar_platform_rtc-1.0.0/.codegraph/codegraph.db-wal +0 -0
  5. liveavatar_platform_rtc-1.0.0/.gitattributes +2 -0
  6. liveavatar_platform_rtc-1.0.0/.gitignore +198 -0
  7. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/.gitignore +1 -0
  8. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/final-fix-report.md +66 -0
  9. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/progress.md +6 -0
  10. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-1-brief.md +102 -0
  11. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-1-report.md +104 -0
  12. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-2-brief.md +63 -0
  13. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-2-report.md +86 -0
  14. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-3-brief.md +59 -0
  15. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-3-report.md +100 -0
  16. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-4-brief.md +89 -0
  17. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-4-report.md +90 -0
  18. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-5-brief.md +60 -0
  19. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-5-report.md +71 -0
  20. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-6-brief.md +56 -0
  21. liveavatar_platform_rtc-1.0.0/.superpowers/sdd/task-6-report.md +98 -0
  22. liveavatar_platform_rtc-1.0.0/LICENSE +201 -0
  23. liveavatar_platform_rtc-1.0.0/MANIFEST.in +4 -0
  24. liveavatar_platform_rtc-1.0.0/PKG-INFO +170 -0
  25. liveavatar_platform_rtc-1.0.0/PROTOCOL.md +147 -0
  26. liveavatar_platform_rtc-1.0.0/PROTOCOL.zh.md +135 -0
  27. liveavatar_platform_rtc-1.0.0/README.md +138 -0
  28. liveavatar_platform_rtc-1.0.0/README.zh.md +129 -0
  29. liveavatar_platform_rtc-1.0.0/docs/superpowers/plans/2026-07-15-rtc-agent-sdk-v2.md +1027 -0
  30. liveavatar_platform_rtc-1.0.0/docs/superpowers/plans/2026-07-15-rtc-chat-default-port-8009.md +89 -0
  31. liveavatar_platform_rtc-1.0.0/docs/superpowers/plans/2026-07-15-rtc-chat-demo.md +470 -0
  32. liveavatar_platform_rtc-1.0.0/docs/superpowers/plans/2026-07-15-rtc-chat-tts-connect-cancellation.md +167 -0
  33. liveavatar_platform_rtc-1.0.0/docs/superpowers/plans/2026-07-15-rtc-chat-tts-null-cancel.md +92 -0
  34. liveavatar_platform_rtc-1.0.0/docs/superpowers/plans/2026-07-15-rtc-chat-unexpected-disconnect-diagnostics.md +176 -0
  35. liveavatar_platform_rtc-1.0.0/docs/superpowers/plans/2026-07-15-rtc-data-channel-without-topic.md +185 -0
  36. liveavatar_platform_rtc-1.0.0/docs/superpowers/plans/2026-07-16-rtc-chat-fork-join-startup.md +388 -0
  37. liveavatar_platform_rtc-1.0.0/docs/superpowers/plans/2026-07-16-rtc-chat-text-input.md +149 -0
  38. liveavatar_platform_rtc-1.0.0/docs/superpowers/specs/2026-07-15-rtc-agent-sdk-v2-design.md +406 -0
  39. liveavatar_platform_rtc-1.0.0/docs/superpowers/specs/2026-07-15-rtc-chat-default-port-8009-design.md +21 -0
  40. liveavatar_platform_rtc-1.0.0/docs/superpowers/specs/2026-07-15-rtc-chat-demo-design.md +188 -0
  41. liveavatar_platform_rtc-1.0.0/docs/superpowers/specs/2026-07-15-rtc-chat-tts-connect-cancellation-design.md +46 -0
  42. liveavatar_platform_rtc-1.0.0/docs/superpowers/specs/2026-07-15-rtc-chat-tts-null-cancel-design.md +17 -0
  43. liveavatar_platform_rtc-1.0.0/docs/superpowers/specs/2026-07-15-rtc-chat-unexpected-disconnect-diagnostics-design.md +48 -0
  44. liveavatar_platform_rtc-1.0.0/docs/superpowers/specs/2026-07-15-rtc-data-channel-without-topic-design.md +54 -0
  45. liveavatar_platform_rtc-1.0.0/docs/superpowers/specs/2026-07-16-rtc-chat-fork-join-startup-design.md +99 -0
  46. liveavatar_platform_rtc-1.0.0/docs/superpowers/specs/2026-07-16-rtc-chat-text-input-design.md +58 -0
  47. liveavatar_platform_rtc-1.0.0/examples/basic_agent.py +80 -0
  48. liveavatar_platform_rtc-1.0.0/pyproject.toml +60 -0
  49. liveavatar_platform_rtc-1.0.0/src/liveavatar_rtc/__init__.py +73 -0
  50. liveavatar_platform_rtc-1.0.0/src/liveavatar_rtc/api_client.py +75 -0
  51. liveavatar_platform_rtc-1.0.0/src/liveavatar_rtc/client.py +194 -0
  52. liveavatar_platform_rtc-1.0.0/src/liveavatar_rtc/errors.py +37 -0
  53. liveavatar_platform_rtc-1.0.0/src/liveavatar_rtc/event_dispatcher.py +45 -0
  54. liveavatar_platform_rtc-1.0.0/src/liveavatar_rtc/livekit_manager.py +452 -0
  55. liveavatar_platform_rtc-1.0.0/src/liveavatar_rtc/py.typed +0 -0
  56. liveavatar_platform_rtc-1.0.0/src/liveavatar_rtc/session.py +317 -0
  57. liveavatar_platform_rtc-1.0.0/src/liveavatar_rtc/types.py +87 -0
  58. liveavatar_platform_rtc-1.0.0/tests/test_api_client.py +255 -0
  59. liveavatar_platform_rtc-1.0.0/tests/test_client.py +344 -0
  60. liveavatar_platform_rtc-1.0.0/tests/test_event_dispatcher.py +209 -0
  61. liveavatar_platform_rtc-1.0.0/tests/test_livekit_manager.py +1008 -0
  62. liveavatar_platform_rtc-1.0.0/tests/test_session.py +589 -0
  63. liveavatar_platform_rtc-1.0.0/tests/test_types.py +41 -0
@@ -0,0 +1,16 @@
1
+ # CodeGraph data files
2
+ # These are local to each machine and should not be committed
3
+
4
+ # Database
5
+ *.db
6
+ *.db-wal
7
+ *.db-shm
8
+
9
+ # Cache
10
+ cache/
11
+
12
+ # Logs
13
+ *.log
14
+
15
+ # Hook markers
16
+ .dirty
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -0,0 +1,198 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
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
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # Ruff stuff:
171
+ .ruff_cache/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
175
+
176
+ # Cursor
177
+ # Cursor is an AI-powered code editor.`.cursorignore` specifies files/directories to
178
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
179
+ # refer to https://docs.cursor.com/context/ignore-files
180
+ .cursorignore
181
+ .cursorindexingignore
182
+
183
+ # macOS
184
+ .DS_Store
185
+
186
+ # Claude Code local settings
187
+ .claude/
188
+
189
+ # oh-my-claudecode runtime state
190
+ .omc/
191
+
192
+ # Local isolated development worktrees
193
+ .worktrees/
194
+
195
+ # Accidental nested project copy (tracked by mistake)
196
+ /liveavatar-platform-rtc-python/
197
+ ragflow/
198
+ .codegraph/daemon.pid
@@ -0,0 +1,66 @@
1
+ # RTC chat final fix report
2
+
3
+ ## Status
4
+
5
+ Implemented in `/tmp/rtc-chat-final-fix` with surgical changes to `agent.py`, `llm.py`, `tts.py`, `QUICKSTART.md`, and focused tests.
6
+
7
+ - Chat readiness now has explicit state/event gating. Handlers register early; audio is dropped before all waits and ASR connect complete; text waits for readiness and is released without hanging by close/failure. Ready is set only after successful ASR connect.
8
+ - TTS terminal items retain per-generation provider failures. JSON `error` and abnormal close raise from `frames()` with provider detail; normal done/close completes normally.
9
+ - Response failures send the request-context error event and do not append conversation or log completion.
10
+ - LLM turns use candidate history and commit only after a successful stream/callback; failure and cancellation preserve the pre-turn history. `close()` supports sync/async client close and is idempotent.
11
+ - Pipeline close includes LLM close in best-effort parallel cleanup.
12
+ - Runtime readiness failure schedules active cleanup, guarded by readiness-task/pipeline/client identity; the done callback never directly awaits or cancels itself.
13
+ - RTC sample-rate publish/format failures propagate `avatar configured for 24 kHz output / align avatar configuration`; QUICKSTART uses the same wording.
14
+ - Provider construction failure before pipeline ownership best-effort closes every already-created ASR/LLM/TTS provider before disconnecting the RTC client.
15
+ - Invalid Base64 or malformed TTS PCM is retained as a generation terminal error (`invalid TTS PCM payload from provider`), raised by `frames()`, and cannot produce a completed response.
16
+ - Added race/failure tests for readiness gating and close release, stale readiness cleanup, provider errors, sample-rate guidance, transactional LLM failures, and client close.
17
+
18
+ ## Verification command summaries
19
+
20
+ TDD red run:
21
+
22
+ ```text
23
+ PYTHONPATH=... python -m pytest -q tests/test_llm.py tests/test_tts.py tests/test_agent.py
24
+ 9 failed, 50 passed in 0.93s
25
+ ```
26
+
27
+ Focused green run after implementation:
28
+
29
+ ```text
30
+ PYTHONPATH=... python -m pytest -q tests/test_agent.py tests/test_llm.py tests/test_tts.py
31
+ 60 passed, 1 failed (the remaining failure exposed a bare re-raise of the original AudioPublishError)
32
+ ```
33
+
34
+ Residual-review red/green run:
35
+
36
+ ```text
37
+ python -m pytest -q tests/test_agent.py::test_tts_factory_failure_closes_created_llm_and_other_resources tests/test_tts.py::test_invalid_audio_payload_raises_in_frames
38
+ RED: 2 failed in 0.61s
39
+ GREEN: 2 passed in 0.50s
40
+ ```
41
+
42
+ Final full offline suite after the two residual review fixes (combined venv/site-packages; localhost bind allowed for aiohttp route tests):
43
+
44
+ ```text
45
+ PYTHONPATH=/tmp/rtc-chat-final-fix:/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/.venv/lib/python3.14/site-packages:/Users/luffer/workspace/liveavatar-platform-rtc-python/src /Users/luffer/workspace/liveavatar-platform-rtc-python/.venv/bin/python -m pytest -q
46
+ ........................................................................ [ 92%]
47
+ ...... [100%]
48
+ 78 passed in 0.84s
49
+ ```
50
+
51
+ Compile and independence/forbidden-symbol scan:
52
+
53
+ ```text
54
+ PYTHONPATH=... python -m compileall -q agent.py asr.py llm.py tts.py tests
55
+ ! rg -n "liveavatar_channel_sdk|AvatarAgent|developer_tts|voice_id|voiceId" . -g '*.py'
56
+ PYTHONPATH=... python -c 'import agent, asr, llm, tts; print("independent imports: ok")'
57
+ independent imports: ok
58
+ ```
59
+
60
+ The forbidden-symbol scan produced no matches and the combined command exited 0.
61
+
62
+ ## Findings
63
+
64
+ - Existing offline coverage remains green; total suite is now 78 tests (the prior 64 plus focused additions and existing review additions).
65
+ - One sandbox-only run could not bind `127.0.0.1` for six aiohttp route tests; rerunning the identical full suite with localhost bind permission passed all 78 tests.
66
+ - Live smoke: **NOT RUN**, as requested. It requires real provider credentials/platform access.
@@ -0,0 +1,6 @@
1
+ Task 1: complete (staged implementation, 10 tests passed, review clean)
2
+ Task 2: complete (staged implementation, 7 tests passed, review clean)
3
+ Task 3: complete (staged implementation, 17 tests passed, review clean)
4
+ Task 4: complete (staged implementation, 19 tests passed, review clean)
5
+ Task 5: complete (staged implementation, 34 tests passed, review clean)
6
+ Task 6: complete (final implementation, 78 tests passed, whole-project review clean)
@@ -0,0 +1,102 @@
1
+ ### Task 1: Standalone configuration and LLM adapter
2
+
3
+ **Files:**
4
+ - Create: `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/rtc-chat/agent.py`
5
+ - Create: `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/rtc-chat/llm.py`
6
+ - Create: `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/rtc-chat/requirements.txt`
7
+ - Create: `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/rtc-chat/tests/test_llm.py`
8
+
9
+ **Interfaces:**
10
+ - Produces: `Settings.from_env() -> Settings`, `Settings.validate() -> None`.
11
+ - Produces: `LlmClient.stream_reply(user_text: str, on_delta: Callable[[str], Awaitable[None]]) -> str` and `reset() -> None`.
12
+
13
+ - [ ] **Step 1: Write failing configuration and LLM tests**
14
+
15
+ ```python
16
+ # tests/test_llm.py
17
+ import pytest
18
+ from agent import Settings
19
+ from llm import LlmClient
20
+
21
+ def test_settings_reject_missing_credentials():
22
+ settings = Settings("", "", None, "", "", "", "", "", "", 8080)
23
+ with pytest.raises(ValueError, match="LIVEAVATAR_API_KEY"):
24
+ settings.validate()
25
+
26
+ @pytest.mark.asyncio
27
+ async def test_stream_reply_forwards_deltas_and_retains_history(fake_openai):
28
+ client = LlmClient(api_key="key", base_url="https://llm.test", model="model",
29
+ system_prompt="system", client=fake_openai(["你", "好"]))
30
+ deltas = []
31
+ reply = await client.stream_reply("hello", deltas.append)
32
+ assert reply == "你好"
33
+ assert deltas == ["你", "好"]
34
+ assert client.messages[-2:] == [
35
+ {"role": "user", "content": "hello"},
36
+ {"role": "assistant", "content": "你好"},
37
+ ]
38
+ ```
39
+
40
+ - [ ] **Step 2: Run the focused tests and verify failure**
41
+
42
+ Run: `cd /Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/rtc-chat && pytest tests/test_llm.py -q`
43
+
44
+ Expected: import failure because `agent.py` and `llm.py` do not exist.
45
+
46
+ - [ ] **Step 3: Implement settings and the minimal streaming LLM client**
47
+
48
+ ```python
49
+ # llm.py core interface
50
+ class LlmClient:
51
+ def __init__(self, *, api_key, base_url, model, system_prompt, client=None):
52
+ self._client = client or AsyncOpenAI(api_key=api_key, base_url=base_url)
53
+ self._model = model
54
+ self._system_prompt = system_prompt
55
+ self.messages = [{"role": "system", "content": system_prompt}]
56
+ self._lock = asyncio.Lock()
57
+
58
+ async def stream_reply(self, user_text, on_delta):
59
+ async with self._lock:
60
+ self.messages.append({"role": "user", "content": user_text})
61
+ self.messages = [self.messages[0]] + self.messages[-20:]
62
+ stream = await self._client.chat.completions.create(
63
+ model=self._model, messages=self.messages, stream=True,
64
+ max_tokens=512, temperature=0.7)
65
+ parts = []
66
+ async for chunk in stream:
67
+ text = chunk.choices[0].delta.content
68
+ if text:
69
+ parts.append(text)
70
+ result = on_delta(text)
71
+ if inspect.isawaitable(result):
72
+ await result
73
+ reply = "".join(parts)
74
+ if reply.strip():
75
+ self.messages.append({"role": "assistant", "content": reply})
76
+ return reply
77
+
78
+ def reset(self):
79
+ self.messages = [{"role": "system", "content": self._system_prompt}]
80
+ ```
81
+
82
+ Implement `Settings` as a frozen dataclass populated from the exact environment variables in the design. `validate()` checks required values in this order: `LIVEAVATAR_API_KEY`, `LIVEAVATAR_AVATAR_ID`, `DASHSCOPE_API_KEY`, `DEEPSEEK_API_KEY`.
83
+
84
+ Set `requirements.txt` to:
85
+
86
+ ```text
87
+ aiohttp>=3.9
88
+ dashscope>=1.25.11
89
+ openai>=1.0.0
90
+ pytest>=8.0
91
+ pytest-asyncio>=0.23
92
+ liveavatar-platform-rtc @ file:///Users/luffer/workspace/liveavatar-platform-rtc-python
93
+ ```
94
+
95
+ - [ ] **Step 4: Run focused tests**
96
+
97
+ Run: `pytest tests/test_llm.py -q`
98
+
99
+ Expected: all tests pass.
100
+
101
+ ---
102
+
@@ -0,0 +1,104 @@
1
+ # Task 1 Report
2
+
3
+ ## Status
4
+
5
+ DONE_WITH_CONCERNS
6
+
7
+ Implementation is complete in the approved staging location `/tmp/rtc-chat-task1`. The requested destination is outside this agent's writable roots, so the main agent must copy the staged files into `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/rtc-chat`.
8
+
9
+ ## Files
10
+
11
+ - `/tmp/rtc-chat-task1/agent.py`
12
+ - `/tmp/rtc-chat-task1/llm.py`
13
+ - `/tmp/rtc-chat-task1/requirements.txt`
14
+ - `/tmp/rtc-chat-task1/tests/test_llm.py`
15
+
16
+ ## TDD and test results
17
+
18
+ RED command:
19
+
20
+ `pytest tests/test_llm.py -q`
21
+
22
+ Raw result summary: exit 2 during collection with `ModuleNotFoundError: No module named 'agent'`, the expected missing-production-module failure before implementation.
23
+
24
+ GREEN command attempted after implementation:
25
+
26
+ `pytest tests/test_llm.py -q`
27
+
28
+ Raw result summary: exit 2 during collection. In this environment the `pytest` console entry point did not add the working directory to `sys.path`, so it still reported `ModuleNotFoundError: No module named 'agent'` even though `agent.py` exists.
29
+
30
+ Focused retry:
31
+
32
+ `PYTHONPATH=. pytest tests/test_llm.py -q`
33
+
34
+ Raw result summary: exit 2 during collection with `ModuleNotFoundError: No module named 'openai'`. Dependencies were not installed, per the instruction not to install anything.
35
+
36
+ Compilation check:
37
+
38
+ `python3 -m py_compile agent.py llm.py tests/test_llm.py`
39
+
40
+ Raw result summary: exit 0, no output.
41
+
42
+ ## Review Follow-up (2026-07-15)
43
+
44
+ - Removed the remaining forbidden-field assertion from `/tmp/rtc-chat-task1/tests/test_llm.py` so the staged source tree contains no match for that identifier.
45
+
46
+ Command:
47
+
48
+ `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/.venv/bin/python -m pytest -q /tmp/rtc-chat-task1/tests/test_llm.py`
49
+
50
+ Raw output:
51
+
52
+ ```text
53
+ .......... [100%]
54
+ 10 passed in 0.25s
55
+ ```
56
+
57
+ ## Self-review
58
+
59
+ - `Settings` is a frozen dataclass with the ten-field positional interface required by the supplied test.
60
+ - `from_env()` reads the LiveAvatar, DashScope, DeepSeek, system prompt, and HTTP port environment variables with the specified demo defaults.
61
+ - `validate()` checks missing credentials in the required order: `LIVEAVATAR_API_KEY`, `LIVEAVATAR_AVATAR_ID`, `DASHSCOPE_API_KEY`, `DEEPSEEK_API_KEY`.
62
+ - `LlmClient` serializes turns with an asyncio lock, retains the system prompt plus the latest 20 conversation messages, forwards non-empty deltas to sync or async callbacks, stores non-blank assistant replies, and resets history to the system prompt.
63
+ - `requirements.txt` exactly matches the brief.
64
+ - No existing WebSocket chat, shared frontend, shared LLM client, or SDK source was modified.
65
+
66
+ ## Concerns
67
+
68
+ - Main-agent authorization is required to copy the four staged files to the requested external directory.
69
+ - The focused test could not reach GREEN in the current interpreter because `openai` is not installed. After copying, run the brief's test command in the demo environment with requirements installed; if its `pytest` entry point has the same path behavior, use `PYTHONPATH=.` or `python -m pytest`.
70
+
71
+ ## Review Fix (2026-07-15)
72
+
73
+ ### Fixes
74
+
75
+ - Removed `Settings.liveavatar_voice_id` and the `LIVEAVATAR_VOICE_ID` environment read from `/tmp/rtc-chat-task1/agent.py`, because RTC start must not accept `voiceId`.
76
+ - Corrected history trimming in `/tmp/rtc-chat-task1/llm.py` to retain the original system message plus only the latest 20 non-system messages, without duplicating the system entry.
77
+ - Expanded `/tmp/rtc-chat-task1/tests/test_llm.py` to cover `Settings.from_env()`, all four required credential failures in validation order, `reset()`, async delta callbacks, and history trimming/system-message uniqueness.
78
+
79
+ ### RED result
80
+
81
+ Command:
82
+
83
+ `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/.venv/bin/python -m pytest -q /tmp/rtc-chat-task1/tests/test_llm.py`
84
+
85
+ Raw result summary: exit 1; `6 failed, 4 passed in 0.27s`. The failures showed that `Settings` still required the removed voice field. The short-history assertion was then tightened to enforce exactly one system message before applying the production fix.
86
+
87
+ ### GREEN result
88
+
89
+ Command:
90
+
91
+ `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/.venv/bin/python -m pytest -q /tmp/rtc-chat-task1/tests/test_llm.py`
92
+
93
+ Raw output:
94
+
95
+ ```text
96
+ .......... [100%]
97
+ 10 passed in 0.23s
98
+ ```
99
+
100
+ Compilation check:
101
+
102
+ `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/.venv/bin/python -m py_compile /tmp/rtc-chat-task1/agent.py /tmp/rtc-chat-task1/llm.py /tmp/rtc-chat-task1/tests/test_llm.py`
103
+
104
+ Raw result summary: exit 0, no output.
@@ -0,0 +1,63 @@
1
+ ### Task 2: Qwen realtime ASR adapter
2
+
3
+ **Files:**
4
+ - Create: `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/rtc-chat/asr.py`
5
+ - Create: `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/rtc-chat/tests/test_asr.py`
6
+
7
+ **Interfaces:**
8
+ - Consumes: RTC `AudioFrame.data` containing 16 kHz mono PCM16.
9
+ - Produces: `QwenAsr.connect()`, `feed_audio(frame: AudioFrame)`, and `close()`.
10
+ - Produces callbacks `on_speech_started`, `on_speech_stopped`, `on_partial(text)`, `on_final(text)`, and `on_error(message)` on the owning asyncio loop.
11
+
12
+ - [ ] **Step 1: Write failing callback and audio tests**
13
+
14
+ ```python
15
+ @pytest.mark.asyncio
16
+ async def test_feed_audio_encodes_pcm(fake_conversation, audio_frame):
17
+ asr = make_asr(fake_conversation)
18
+ await asr.connect()
19
+ asr.feed_audio(audio_frame)
20
+ fake_conversation.append_audio.assert_called_once_with(
21
+ base64.b64encode(audio_frame.data.tobytes()).decode("ascii"))
22
+
23
+ @pytest.mark.asyncio
24
+ async def test_callback_dispatches_final_on_event_loop(callback, final_texts):
25
+ callback.on_event({"type": "conversation.item.input_audio_transcription.completed",
26
+ "transcript": "你好"})
27
+ await asyncio.sleep(0)
28
+ assert final_texts == ["你好"]
29
+ ```
30
+
31
+ - [ ] **Step 2: Run tests and verify failure**
32
+
33
+ Run: `pytest tests/test_asr.py -q`
34
+
35
+ Expected: import failure for `asr.QwenAsr`.
36
+
37
+ - [ ] **Step 3: Implement the adapter around DashScope**
38
+
39
+ Use `OmniRealtimeConversation`, `OmniRealtimeCallback`, and `TranscriptionParams`. In `connect()`, run blocking `conversation.connect` with `asyncio.to_thread`, then configure:
40
+
41
+ ```python
42
+ conversation.update_session(
43
+ output_modalities=[MultiModality.TEXT],
44
+ enable_turn_detection=True,
45
+ turn_detection_type="server_vad",
46
+ turn_detection_threshold=0.0,
47
+ turn_detection_silence_duration_ms=400,
48
+ enable_input_audio_transcription=True,
49
+ transcription_params=TranscriptionParams(
50
+ language="zh", sample_rate=16000, input_audio_format="pcm"),
51
+ )
52
+ ```
53
+
54
+ Implement callback dispatch with `asyncio.run_coroutine_threadsafe()` for coroutine callbacks and `loop.call_soon_threadsafe()` for synchronous callbacks. Ignore empty partial/final strings. Make `close()` idempotent and run the blocking provider close in a thread.
55
+
56
+ - [ ] **Step 4: Run focused tests**
57
+
58
+ Run: `pytest tests/test_asr.py -q`
59
+
60
+ Expected: all tests pass.
61
+
62
+ ---
63
+
@@ -0,0 +1,86 @@
1
+ # Task 2 Report: Qwen realtime ASR adapter
2
+
3
+ ## Status
4
+
5
+ Complete in the required staging area only:
6
+
7
+ - `/tmp/rtc-chat-task2/asr.py`
8
+ - `/tmp/rtc-chat-task2/tests/test_asr.py`
9
+
10
+ No Task 2 files were copied into the formal demo or SDK source trees.
11
+
12
+ ## TDD evidence
13
+
14
+ ### RED
15
+
16
+ Command:
17
+
18
+ ```text
19
+ cd /tmp/rtc-chat-task2
20
+ /Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/.venv/bin/python -m pytest tests/test_asr.py -q
21
+ ```
22
+
23
+ Result: collection failed with `ModuleNotFoundError: No module named 'asr'`, which is the expected missing-feature failure.
24
+
25
+ ### GREEN
26
+
27
+ Command:
28
+
29
+ ```text
30
+ cd /tmp/rtc-chat-task2
31
+ PYTHONPATH=/Users/luffer/workspace/liveavatar-platform-rtc-python/src /Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/.venv/bin/python -m pytest tests/test_asr.py -q
32
+ ```
33
+
34
+ Initial result before review fixes: `6 passed in 0.29s`.
35
+
36
+ ### Final verification
37
+
38
+ Command:
39
+
40
+ ```text
41
+ cd /tmp/rtc-chat-task2
42
+ PYTHONPATH=/Users/luffer/workspace/liveavatar-platform-rtc-python/src /Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/.venv/bin/python -m pytest tests/test_asr.py -q
43
+ /Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/.venv/bin/python -m py_compile asr.py tests/test_asr.py
44
+ ```
45
+
46
+ Initial result before review fixes: `6 passed in 0.29s`; bytecode compilation
47
+ exited successfully.
48
+
49
+ ## Implemented behavior
50
+
51
+ - Injected DashScope provider boundary for network-free tests.
52
+ - Blocking provider `connect()` and `close()` run through `asyncio.to_thread()`.
53
+ - Session configuration matches the brief: text modality, server VAD, threshold `0.0`, 400 ms silence, and Chinese 16 kHz PCM transcription.
54
+ - RTC audio uses `frame.to_pcm_bytes()` when available and falls back to `frame.data.tobytes()`, then base64-encodes for `append_audio()`.
55
+ - Speech start/stop, partial/final transcript, and provider error events map to owner callbacks.
56
+ - Provider-thread callbacks use `loop.call_soon_threadsafe()` to invoke on the
57
+ owning loop; returned awaitables are scheduled with `loop.create_task()`.
58
+ - Empty partial/final values and unknown events are ignored.
59
+ - Concurrent and repeated `close()` calls invoke provider close exactly once.
60
+
61
+ ## Self-check
62
+
63
+ - Changes are limited to the required `/tmp` staging files and this report.
64
+ - No dependency was installed and no network call was made.
65
+ - Formal demo path `/Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/rtc-chat/asr.py` remains absent.
66
+ - Existing Task 1 files and old demo/SDK files were not modified.
67
+
68
+ ## Concerns
69
+
70
+ - DashScope in the supplied virtual environment defines `TranscriptionParams` in `dashscope.audio.qwen_omni.omni_realtime` but does not export it from `dashscope.audio.qwen_omni`; the implementation uses that official submodule import for compatibility.
71
+ - Provider event schemas were implemented exactly from the supplied design/brief. A live credential smoke test was intentionally not run because this task requires network-free provider injection tests.
72
+
73
+ ## Review Fix Verification
74
+
75
+ - Added the official Qwen ASR interim event contract
76
+ `conversation.item.input_audio_transcription.text` with `text`/`stash`, while
77
+ retaining compatible `.delta` handling.
78
+ - Added an async callable-object regression test for loop-side awaitable
79
+ scheduling.
80
+ - Fresh verification command run by the main agent:
81
+
82
+ ```text
83
+ PYTHONPATH=/tmp/rtc-chat-task2:/Users/luffer/workspace/liveavatar-platform-rtc-python/src /Users/luffer/workspace/liveavatar-ws-integration-demo/python-agent/.venv/bin/python -m pytest -q /tmp/rtc-chat-task2/tests/test_asr.py
84
+ ```
85
+
86
+ Result: `7 passed in 0.22s`.