Skip to content

Commit 6ed20e8

Browse files
antriksh30Antriksh JainCopilot
authored
Migrate BYO WebSocket agents to uv (#1064)
Co-authored-by: Antriksh Jain <antrikshjain@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ec0a8f22-0687-4209-912b-1d5f4c4263f1
1 parent 650937e commit 6ed20e8

31 files changed

Lines changed: 11993 additions & 241 deletions

File tree

‎samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/README.md‎

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,32 +67,33 @@ A sample for building real-time voice agents that maintain two parallel tracks s
6767
| [main.py](src/duplex-live-agent/main.py) | The whole agent — `@app.ws_handler` opens a Voice Live session and runs two pumps. |
6868
| [azure.yaml](azure.yaml) | Hosted-agent runtime config (`invocations_ws`, 1 CPU / 1 Gi). |
6969
| [azure.yaml](azure.yaml) | `azd ai agent init` manifest. |
70-
| [Dockerfile](src/duplex-live-agent/Dockerfile) | python:3.12-slim, installs dependencies from `requirements.txt`. |
71-
| [requirements.txt](src/duplex-live-agent/requirements.txt) | Pins `azure-ai-agentserver-invocations==1.1.0` + `azure-ai-voicelive[aiohttp]` + `azure-identity` + `python-dotenv`. |
70+
| [Dockerfile](src/duplex-live-agent/Dockerfile) | Builds a frozen uv environment on `python:3.12-slim`. |
71+
| [pyproject.toml](src/duplex-live-agent/pyproject.toml) | Runtime dependencies and the local-only `websockets` development group. |
72+
| [uv.lock](src/duplex-live-agent/uv.lock) | Fully resolved runtime and development dependency graph. |
7273
| [.env.example](src/duplex-live-agent/.env.example) | Required Voice Live env vars. |
7374
| [e2e_local.py](src/duplex-live-agent/e2e_local.py) | Headless E2E that streams a 1 kHz tone in and asserts audio + events come back. |
7475

7576
## Prerequisites
7677

77-
1. Python 3.10 or later.
78-
2. Azure CLI logged in (`az login`).
79-
3. An Azure AI Services / Voice Live resource and access to a realtime model (e.g. `gpt-realtime`).
80-
4. The `Foundry User` role on that resource for your user (or the hosted agent's managed identity, post-deploy).
78+
1. Python 3.12 or later.
79+
2. [uv](https://docs.astral.sh/uv/getting-started/installation/) 0.11.7 or later.
80+
3. Azure CLI logged in (`az login`).
81+
4. An Azure AI Services / Voice Live resource and access to a realtime model (e.g. `gpt-realtime`).
82+
5. The `Foundry User` role on that resource for your user (or the hosted agent's managed identity, post-deploy).
8183

8284
## Running locally
8385

8486
```bash
85-
cd duplex-live-agent
86-
87-
python -m venv .venv
88-
source .venv/bin/activate
89-
pip install -r requirements.txt websockets
87+
cd samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent
88+
python -m pip install uv
89+
cd src/duplex-live-agent
90+
uv sync --frozen
9091

9192
cp .env.example .env
9293
# Fill in AZURE_VOICELIVE_ENDPOINT and AZURE_VOICELIVE_MODEL.
9394
export $(grep -v '^#' .env | xargs)
9495

95-
python main.py
96+
uv run --no-sync python main.py
9697
# → Uvicorn running on http://0.0.0.0:8088
9798
```
9899

@@ -101,9 +102,8 @@ python main.py
101102
In a second terminal:
102103

103104
```bash
104-
cd duplex-live-agent
105-
source .venv/bin/activate
106-
python e2e_local.py
105+
cd samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent
106+
uv run --no-sync python e2e_local.py
107107
# [e2e] session_started: True
108108
# [e2e] audio_bytes recvd: 230400
109109
# [e2e] result: PASS
@@ -118,11 +118,11 @@ spoken utterance — server-VAD is bypassed entirely.
118118

119119
### Browser test (standalone client)
120120

121-
The sample ships a tiny single-file browser client in [`chat_client/`](chat_client/index.html).
121+
The sample ships a tiny single-file browser client in [`chat_client/`](src/duplex-live-agent/chat_client/index.html).
122122
With the agent running on `:8088`, just serve the folder:
123123

124124
```bash
125-
cd chat_client
125+
cd samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/chat_client
126126
python -m http.server 8080
127127
```
128128

@@ -183,7 +183,8 @@ this on a WebSocket, so the standalone `chat_client/index.html` is for
183183
local dev only):
184184

185185
```bash
186-
python e2e_local.py \
186+
cd src/duplex-live-agent
187+
uv run --no-sync python e2e_local.py \
187188
--foundry "https://<account>.services.ai.azure.com/api/projects/<project>" \
188189
--agent duplex-live-agent
189190
```

‎samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/Dockerfile‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
FROM ghcr.io/astral-sh/uv:0.11.7 AS uv
12
FROM python:3.12-slim
23

4+
COPY --from=uv /uv /uvx /bin/
5+
36
WORKDIR /app
47

58
ENV PYTHONDONTWRITEBYTECODE=1 \
69
PYTHONUNBUFFERED=1
710

8-
COPY requirements.txt ./
9-
RUN pip install --no-cache-dir -r requirements.txt
11+
COPY pyproject.toml uv.lock uv.toml ./
12+
RUN uv sync --frozen --no-dev --no-install-project
1013

1114
COPY . ./
1215

@@ -22,7 +25,7 @@ EXPOSE 8088
2225
# container start.
2326
# PYTHONDONTWRITEBYTECODE is cleared for these commands only: it blocks
2427
# bytecode *writes*, not *reads*, so images that set it still benefit.
25-
RUN PYTHONDONTWRITEBYTECODE= python -m compileall -q $(python -c "import sysconfig as s; print(s.get_paths()['stdlib'], s.get_paths()['purelib'])") || true; \
26-
PYTHONDONTWRITEBYTECODE= python -m compileall -q .
28+
RUN PYTHONDONTWRITEBYTECODE= .venv/bin/python -m compileall -q $(.venv/bin/python -c "import sysconfig as s; print(s.get_paths()['stdlib'], s.get_paths()['purelib'])") || true; \
29+
PYTHONDONTWRITEBYTECODE= .venv/bin/python -m compileall -q -x '[.]venv' .
2730

28-
CMD ["python", "main.py"]
31+
CMD ["/app/.venv/bin/python", "main.py"]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[project]
2+
name = "duplex-live-agent"
3+
version = "0.1.0"
4+
description = "A Microsoft Foundry hosted agent using the Invocations WebSocket protocol."
5+
requires-python = ">=3.12"
6+
dependencies = [
7+
"agent-framework-core==1.10.0",
8+
"agent-framework-foundry==1.10.0",
9+
"agent-framework-orchestrations==1.0.0",
10+
"azure-ai-agentserver-invocations==1.1.0",
11+
"azure-ai-voicelive[aiohttp]>=1.0.0b1",
12+
"azure-identity>=1.20.0",
13+
"debugpy",
14+
"github-copilot-sdk==0.3.0",
15+
"python-dotenv>=1.0.0",
16+
]
17+
18+
[dependency-groups]
19+
dev = [
20+
"websockets>=14",
21+
]

‎samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/requirements.txt‎

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)