From 206c6b4618667f04b9b6613a6aaef68d9261a1af Mon Sep 17 00:00:00 2001 From: xueqifang Date: Thu, 17 Sep 2026 14:14:49 +0800 Subject: [PATCH] chore: upgrade agora-agents to 2.9.0 and client toolkit to 2.10.0 --- bun.lock | 11 +++++++++-- docs/ai/L0_repo_card.md | 2 +- docs/ai/L1/01_setup.md | 4 ++-- docs/ai/L1/02_architecture.md | 2 +- docs/ai/L1/07_gotchas.md | 6 ++++++ docs/ai/L1/L2/from_scratch_bootstrap.md | 2 +- docs/ai/L1/L2/session_lifecycle.md | 9 +++++---- server/requirements.txt | 2 +- web/package.json | 3 ++- web/scripts/verify-local-proxy.ts | 3 ++- web/src/components/ConversationComponent.tsx | 2 +- 11 files changed, 31 insertions(+), 15 deletions(-) diff --git a/bun.lock b/bun.lock index d274947..6cd2408 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,7 @@ "dependencies": { "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-slot": "^1.2.4", - "agora-agent-client-toolkit": "1.2.0", + "agora-agent-client-toolkit": "2.10.0", "agora-agent-uikit": "1.1.0", "agora-rtc-react": "^2.5.1", "agora-rtc-sdk-ng": "^4.24.3", @@ -30,6 +30,7 @@ }, "devDependencies": { "@biomejs/biome": "^1.9.0", + "@types/bun": "1.3.12", "@types/node": "^25.3.1", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", @@ -237,6 +238,8 @@ "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], + "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], + "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], "@types/pako": ["@types/pako@2.0.4", "", {}, "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw=="], @@ -247,7 +250,7 @@ "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], - "agora-agent-client-toolkit": ["agora-agent-client-toolkit@1.2.0", "", { "optionalDependencies": { "@agora-js/report": ">=4.19.0", "jszip": ">=3.0.0" }, "peerDependencies": { "agora-rtc-sdk-ng": ">=4.23.4", "agora-rtm": ">=2.0.0" }, "optionalPeers": ["agora-rtm"] }, "sha512-rbSDfk6veGsvNxSgccPa1zCCJ7QQw0NjoETHiHSXGURkzw90KwOQ2ZIOX6dueMFvZLUBr1UPQodCpOHroHY5bw=="], + "agora-agent-client-toolkit": ["agora-agent-client-toolkit@2.10.0", "", { "optionalDependencies": { "@agora-js/report": ">=4.19.0", "jszip": ">=3.0.0" }, "peerDependencies": { "agora-rtc-sdk-ng": ">=4.23.4", "agora-rtm": ">=2.0.0" }, "optionalPeers": ["agora-rtm"] }, "sha512-IeFZiqXS146mFQLJQWf9o1K+UdQ6Wv2Agk7AfVK51XSujre/ULafYVQR0S+vAKNAFnrXsH3+zkAkCXzTW27DMA=="], "agora-agent-uikit": ["agora-agent-uikit@1.1.0", "", { "dependencies": { "@lottiefiles/dotlottie-react": "^0.17.13", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-switch": "^1.2.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", "dompurify": "^3.3.3", "lucide-react": ">=0.263.0", "tailwind-merge": "^3.4.0" }, "peerDependencies": { "agora-agent-client-toolkit": "^1.2.0", "agora-agent-client-toolkit-react": "^1.2.0", "agora-rtc-react": ">=2.0.0", "agora-rtm-sdk": ">=2.0.0", "react": ">=18.0.0", "react-dom": ">=18.0.0" }, "optionalPeers": ["agora-agent-client-toolkit", "agora-agent-client-toolkit-react", "agora-rtc-react", "agora-rtm-sdk"] }, "sha512-gDSleHFYavNEqf/SIUt8xA1Q+VAw4M9q/84MD/+fFG7Z15oSLH6hZnmI+8cr6yzPXBBXHX4dd9tRZod5UjaFyA=="], @@ -287,6 +290,8 @@ "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], @@ -601,6 +606,8 @@ "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "agora-agent-uikit/agora-agent-client-toolkit": ["agora-agent-client-toolkit@1.2.0", "", { "optionalDependencies": { "@agora-js/report": ">=4.19.0", "jszip": ">=3.0.0" }, "peerDependencies": { "agora-rtc-sdk-ng": ">=4.23.4", "agora-rtm": ">=2.0.0" }, "optionalPeers": ["agora-rtm"] }, "sha512-rbSDfk6veGsvNxSgccPa1zCCJ7QQw0NjoETHiHSXGURkzw90KwOQ2ZIOX6dueMFvZLUBr1UPQodCpOHroHY5bw=="], + "agora-agent-uikit/lucide-react": ["lucide-react@1.9.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-6qVAmbgCjcJz7sAGSPSSJ++RAwjlK2XCbRrZKv63Ciko1KT8jX0//CXxgI3jg2HlJu8tADqdYlNDebmYjeoruA=="], "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], diff --git a/docs/ai/L0_repo_card.md b/docs/ai/L0_repo_card.md index f5a709d..6d02a2e 100644 --- a/docs/ai/L0_repo_card.md +++ b/docs/ai/L0_repo_card.md @@ -11,7 +11,7 @@ | Language | Python 3.10+ (FastAPI + uvicorn) backend + Next.js 16 / React 19 web | | Deploy Target | `web/` as Next.js app, `server/` as a reachable FastAPI service | | Owner | Agora Conversational AI DevEx | -| Last Reviewed | 2026-08-21 | +| Last Reviewed | 2026-09-17 | | Recipe Role | `base` | | Recipe Version | `1.0.0` | | Recipe Status | `experimental` | diff --git a/docs/ai/L1/01_setup.md b/docs/ai/L1/01_setup.md index 7cd5206..ea18d6a 100644 --- a/docs/ai/L1/01_setup.md +++ b/docs/ai/L1/01_setup.md @@ -57,10 +57,10 @@ fastapi>=0.100.0 uvicorn>=0.20.0 requests>=2.31.0 python-dotenv>=1.0.0 -agora-agents>=2.0.0 +agora-agents>=2.9.0 ``` -The SDK is lower-bounded at v2 — add an upper bound or exact pin if you need reproducible SDK behavior. +The SDK requires version 2.9.0 or later. ## Quick Commands diff --git a/docs/ai/L1/02_architecture.md b/docs/ai/L1/02_architecture.md index fba4ff2..376def6 100644 --- a/docs/ai/L1/02_architecture.md +++ b/docs/ai/L1/02_architecture.md @@ -28,7 +28,7 @@ 3. `ConversationComponent` mounts inside a dynamic `AgoraRTCProvider` (RTC client in `useRef` for StrictMode safety) and: - `useJoin` joins RTC. - `useLocalMicrophoneTrack` + `usePublish` start mic publishing. - - `AgoraVoiceAI.init({ rtcEngine, rtmConfig: { rtmEngine: rtmClient } })` wires transcripts, state, metrics. + - `AgoraVoiceAI.init({ rtcEngine, rtmEngine: rtmClient })` wires transcripts, state, metrics. - `subscribeMessage(channel_name)` opens the toolkit's RTM channel. 4. End: `stopAgent(agentId)` → `POST /api/stopAgent` → FastAPI stops the agent. `rtmClient.logout()` follows. 5. Renewal: on RTC `token-privilege-will-expire`, the client fetches `getConfig()` twice (once for RTC uid, once for the stored `agoraData.uid`) and renews RTC + RTM separately. diff --git a/docs/ai/L1/07_gotchas.md b/docs/ai/L1/07_gotchas.md index c798b8b..de4f65f 100644 --- a/docs/ai/L1/07_gotchas.md +++ b/docs/ai/L1/07_gotchas.md @@ -78,6 +78,12 @@ RTM delivery, tool enablement, metrics, error messages, and data channel setting The repo's git history is human-authored. Keep it that way — see `AGENTS.md` "Git Conventions." +## Toolkit 2.10.0 Migration + +Pass `rtmEngine` directly to `AgoraVoiceAI.init`; the old nested `rtmConfig` is no longer supported. Install from the repository root with Bun and commit the root `bun.lock`. `@types/bun` supplies types for the existing `bun:test` files. + +UIKit 1.1.0 declares an optional Toolkit ^1.2.0 peer. Bun resolves a separate Toolkit 1.2.0 for UIKit while the app uses 2.10.0. Passing type checks and a production build does not establish live session compatibility; verify transcript, agent state, metrics, and teardown in a real call before shipping. + ## Related Deep Dives - [Managed Agent Config](L2/managed_agent_config.md) — Backend defaults. diff --git a/docs/ai/L1/L2/from_scratch_bootstrap.md b/docs/ai/L1/L2/from_scratch_bootstrap.md index c98f9f1..ad43360 100644 --- a/docs/ai/L1/L2/from_scratch_bootstrap.md +++ b/docs/ai/L1/L2/from_scratch_bootstrap.md @@ -26,7 +26,7 @@ Why: provider schemas, SDK builder fields, token behavior, and RTM event details Implement these pieces in order: 1. Create a bun workspace with `web` as a workspace member and root scripts that orchestrate backend, frontend, setup, doctor, verify, and clean tasks. -2. Create `server/` with FastAPI, uvicorn, python-dotenv, and `agora-agents>=2.0.0` in `server/requirements.txt`. +2. Create `server/` with FastAPI, uvicorn, python-dotenv, and `agora-agents>=2.9.0` in `server/requirements.txt`. 3. Add `server/.env.example` with `AGORA_APP_ID`, `AGORA_APP_CERTIFICATE`, and optional `PORT`. 4. Implement `server/src/agent.py` with an `Agent` class that reads env once, constructs `AsyncAgora`, builds `AgoraAgent` with managed `DeepgramSTT`, `OpenAI`, `MiniMaxTTS`, starts async sessions, stores sessions by `agent_id`, and stops by active session or `client.stop_agent`. 5. Implement `server/src/server.py` with `GET /get_config`, `POST /startAgent`, and `POST /stopAgent`; load `server/.env` relative to the source file. diff --git a/docs/ai/L1/L2/session_lifecycle.md b/docs/ai/L1/L2/session_lifecycle.md index 659aac3..06c8ea9 100644 --- a/docs/ai/L1/L2/session_lifecycle.md +++ b/docs/ai/L1/L2/session_lifecycle.md @@ -37,10 +37,11 @@ ConversationComponent.tsx ├─▶ useLocalMicrophoneTrack() ├─▶ usePublish([track]) │ - └─▶ new AgoraVoiceAI({ - rtcEngine, - rtmConfig: { rtmEngine: rtmClient }, - }).subscribeMessage(channel_name) + └─▶ const ai = await AgoraVoiceAI.init({ + rtcEngine: client, + rtmEngine: rtmClient, + }) + ai.subscribeMessage(channel_name) Listens: TRANSCRIPT_UPDATED, AGENT_STATE_CHANGED, AGENT_METRICS, MESSAGE_ERROR, MESSAGE_SAL_STATUS, AGENT_ERROR ``` diff --git a/server/requirements.txt b/server/requirements.txt index e28ebb4..d201569 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -2,6 +2,6 @@ fastapi>=0.100.0 uvicorn>=0.20.0 requests>=2.31.0 python-dotenv>=1.0.0 -agora-agents>=2.3.0 +agora-agents>=2.9.0 # Enables httpx to route through a SOCKS proxy (e.g. all_proxy=socks5://...) socksio>=1.0.0 diff --git a/web/package.json b/web/package.json index c37a38b..fe2837a 100644 --- a/web/package.json +++ b/web/package.json @@ -14,7 +14,7 @@ "dependencies": { "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-slot": "^1.2.4", - "agora-agent-client-toolkit": "1.2.0", + "agora-agent-client-toolkit": "2.10.0", "agora-agent-uikit": "1.1.0", "agora-rtc-react": "^2.5.1", "agora-rtc-sdk-ng": "^4.24.3", @@ -30,6 +30,7 @@ }, "devDependencies": { "@biomejs/biome": "^1.9.0", + "@types/bun": "1.3.12", "@types/node": "^25.3.1", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", diff --git a/web/scripts/verify-local-proxy.ts b/web/scripts/verify-local-proxy.ts index e4e897e..48e7c56 100644 --- a/web/scripts/verify-local-proxy.ts +++ b/web/scripts/verify-local-proxy.ts @@ -6,7 +6,7 @@ type Rewrite = { } type LocalServer = { - port: number + port: number | undefined stop: (closeActiveConnections?: boolean) => void } @@ -112,6 +112,7 @@ async function withStubBackend(run: (baseUrl: string) => Promise) { } try { + assert(typeof server.port === 'number', 'Stub backend must listen on a TCP port') return await run(`http://localhost:${server.port}`) } finally { server.stop(true) diff --git a/web/src/components/ConversationComponent.tsx b/web/src/components/ConversationComponent.tsx index df0110d..404901f 100644 --- a/web/src/components/ConversationComponent.tsx +++ b/web/src/components/ConversationComponent.tsx @@ -181,7 +181,7 @@ export default function ConversationComponent({ try { const ai = await AgoraVoiceAI.init({ rtcEngine: client, - rtmConfig: { rtmEngine: rtmClient }, + rtmEngine: rtmClient, renderMode: TranscriptHelperMode.TEXT, enableLog: true, });