From e67bd0f13df7fcd4e46feb6466cc4f07247f63d0 Mon Sep 17 00:00:00 2001 From: seymourtang Date: Tue, 30 Jun 2026 17:51:03 +0800 Subject: [PATCH] fix: update SenseTimeAvatar configuration to make sceneList optional --- docs/concepts/vendors.md | 2 +- docs/guides/avatars.md | 4 +- docs/reference/vendors.md | 2 +- src/agora_agent/agentkit/avatar_types.py | 3 -- src/agora_agent/agentkit/vendors/cn.py | 5 ++- tests/custom/test_sensetime_avatar.py | 47 ++++++++++++++++++++++-- 6 files changed, 50 insertions(+), 13 deletions(-) diff --git a/docs/concepts/vendors.md b/docs/concepts/vendors.md index b82c2d7..9fc4c9e 100644 --- a/docs/concepts/vendors.md +++ b/docs/concepts/vendors.md @@ -167,7 +167,7 @@ Used with `agent.with_avatar()` in the cascading ASR + LLM + TTS pipeline. Some | `AkoolAvatar` | Akool | `api_key` | 16000 Hz | | `AnamAvatar` | Anam | `api_key` | None | | `GenericAvatar` | Generic Avatar | `api_key`, `api_base_url`, `avatar_id`, `agora_uid` | None | -| `SenseTimeAvatar` | SenseTime (CN) | `agora_uid`, `app_key`, `sceneList` | None | +| `SenseTimeAvatar` | SenseTime (CN) | `agora_uid`, `app_key` | None | | `SpatiusAvatar` | Spatius (CN) | `spatius_api_key`, `spatius_app_id`, `spatius_avatar_id`, `agora_uid` | Optional avatar-declared sample rate | diff --git a/docs/guides/avatars.md b/docs/guides/avatars.md index 590e4e8..af50888 100644 --- a/docs/guides/avatars.md +++ b/docs/guides/avatars.md @@ -103,7 +103,7 @@ agent = agent.with_avatar(GenericAvatar( ## SenseTime Avatar (CN) -`SenseTimeAvatar` is available for `Area.CN` sessions. Provide `agora_uid`, `app_key`, and `sceneList` when constructing the avatar. `agora_token` is optional and is generated at session start when omitted, like LiveAvatar and Generic avatars. +`SenseTimeAvatar` is available for `Area.CN` sessions. Provide `agora_uid` and `app_key` when constructing the avatar. `sceneList` is optional. `agora_token` is optional and is generated at session start when omitted, like LiveAvatar and Generic avatars. ```python from agora_agent import Agora, Area, CNAgent, MiniMaxCNTTS, SenseTimeAvatar, TencentSTT @@ -276,7 +276,7 @@ If you call `with_avatar()` before `with_tts()`, the sample rate check is deferr | `agora_token` | `str` | No | Avatar publisher RTC token; generated at session start when omitted | | `agora_uid` | `str` | Yes | Avatar publisher RTC UID | | `app_key` | `str` | Yes | SenseTime application key | -| `sceneList` | `List[Dict[str, Any]]` | Yes | SenseTime scene configuration list | +| `sceneList` | `List[Dict[str, Any]]` | No | SenseTime scene configuration list | | `appId` | `str` | No | SenseTime application ID | | `enable` | `bool` | No | Whether to enable the avatar | | `additional_params` | `Dict[str, Any]` | No | Additional SenseTime avatar parameters | diff --git a/docs/reference/vendors.md b/docs/reference/vendors.md index 697109e..20980cf 100644 --- a/docs/reference/vendors.md +++ b/docs/reference/vendors.md @@ -698,7 +698,7 @@ No constructor parameters. Use `FengmingSTT()`. | `agora_uid` | `str` | Yes | — | Avatar RTC publisher uid | | `appId` | `str` | No | `None` | SenseTime app id | | `app_key` | `str` | Yes | — | SenseTime app key | -| `sceneList` | `List[Dict[str, Any]]` | Yes | — | SenseTime scene list | +| `sceneList` | `List[Dict[str, Any]]` | No | `None` | SenseTime scene list | | `enable` | `bool` | No | `None` | Whether to enable the avatar | | `additional_params` | `Dict[str, Any]` | No | `None` | Additional SenseTime avatar parameters | diff --git a/src/agora_agent/agentkit/avatar_types.py b/src/agora_agent/agentkit/avatar_types.py index 9d565fa..4759e73 100644 --- a/src/agora_agent/agentkit/avatar_types.py +++ b/src/agora_agent/agentkit/avatar_types.py @@ -127,9 +127,6 @@ def validate_avatar_config( params = config.get("params", {}) if not params.get("app_key"): raise ValueError("SenseTime avatar requires app_key") - scene_list = params.get("sceneList") - if not scene_list: - raise ValueError("SenseTime avatar requires sceneList") if not params.get("agora_uid"): raise ValueError("SenseTime avatar requires agora_uid") if require_session_fields and not params.get("agora_token"): diff --git a/src/agora_agent/agentkit/vendors/cn.py b/src/agora_agent/agentkit/vendors/cn.py index 23934ab..e5dbca3 100644 --- a/src/agora_agent/agentkit/vendors/cn.py +++ b/src/agora_agent/agentkit/vendors/cn.py @@ -556,7 +556,7 @@ class SenseTimeAvatarOptions(BaseModel): agora_uid: str = Field(..., description="Avatar RTC publisher uid") app_id: Optional[str] = Field(default=None, alias="appId", description="SenseTime app id") app_key: str = Field(..., description="SenseTime app key") - scene_list: List[Dict[str, Any]] = Field(..., alias="sceneList", description="SenseTime scene list") + scene_list: Optional[List[Dict[str, Any]]] = Field(default=None, alias="sceneList", description="SenseTime scene list") enable: Optional[bool] = Field(default=None) additional_params: Optional[Dict[str, Any]] = Field(default=None) @@ -573,12 +573,13 @@ def to_config(self) -> Dict[str, Any]: params: Dict[str, Any] = { "agora_uid": self.options.agora_uid, "app_key": self.options.app_key, - "sceneList": self.options.scene_list, } if self.options.agora_token is not None: params["agora_token"] = self.options.agora_token if self.options.app_id is not None: params["appId"] = self.options.app_id + if self.options.scene_list is not None: + params["sceneList"] = self.options.scene_list if self.options.additional_params is not None: params = {**self.options.additional_params, **params} diff --git a/tests/custom/test_sensetime_avatar.py b/tests/custom/test_sensetime_avatar.py index ca9a729..bc582fb 100644 --- a/tests/custom/test_sensetime_avatar.py +++ b/tests/custom/test_sensetime_avatar.py @@ -74,14 +74,20 @@ def test_sensetime_avatar_to_config_omits_token_when_not_provided() -> None: assert config["params"]["agora_uid"] == "2" +def test_sensetime_avatar_to_config_omits_scene_list_when_not_provided() -> None: + config = SenseTimeAvatar( + agora_uid="2", + app_key="sensetime-app-key", + ).to_config() + + assert "sceneList" not in config["params"] + assert config["params"]["agora_uid"] == "2" + + @pytest.mark.parametrize( ("params", "message"), [ ({}, "SenseTime avatar requires app_key"), - ( - {"app_key": "key", "agora_uid": "2"}, - "SenseTime avatar requires sceneList", - ), ( {"app_key": "key", "sceneList": _scene_list()}, "SenseTime avatar requires agora_uid", @@ -110,6 +116,18 @@ def test_validate_avatar_config_requires_agora_token_at_session_time() -> None: ) +def test_validate_avatar_config_allows_missing_scene_list() -> None: + validate_avatar_config( + { + "vendor": "sensetime", + "params": { + "app_key": "key", + "agora_uid": "2", + }, + } + ) + + def test_sensetime_avatar_session_validation_and_token_passthrough() -> None: agent = Agent(test_client()).with_avatar( SenseTimeAvatar( @@ -173,3 +191,24 @@ def test_sensetime_avatar_user_token_is_not_overwritten() -> None: ) assert properties["avatar"]["params"]["agora_token"] == "user-token" + + +def test_sensetime_avatar_enrichment_without_scene_list() -> None: + agent = Agent(test_client()).with_avatar( + SenseTimeAvatar( + agora_uid="2", + app_key="sensetime-app-key", + ) + ) + session = _session(agent) + + properties = session._build_start_properties( # noqa: SLF001 + {"app_id": APP_ID, "app_certificate": APP_CERTIFICATE}, + skip_vendor_validation_categories=set(), + allow_missing_vendor_categories={"tts", "llm", "asr"}, + ) + + params = properties["avatar"]["params"] + assert "sceneList" not in params + assert params["agora_uid"] == "2" + assert params["agora_token"]