From 088f76a3d7722a933552402f69f617cde921612f Mon Sep 17 00:00:00 2001 From: plutoless Date: Wed, 17 Jun 2026 11:53:19 -0700 Subject: [PATCH 1/8] fix(cn): use typing.List so pydantic can build models on Python 3.8 list[...] (PEP 585) is not evaluatable at runtime on 3.8, where pydantic eval's the annotation string at model-build time. typing.List is identical to type-checkers and IDEs but works on 3.8. --- src/agora_agent/agentkit/vendors/cn.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/agora_agent/agentkit/vendors/cn.py b/src/agora_agent/agentkit/vendors/cn.py index b19c5f7..2021817 100644 --- a/src/agora_agent/agentkit/vendors/cn.py +++ b/src/agora_agent/agentkit/vendors/cn.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional from pydantic import BaseModel, ConfigDict, Field, model_validator @@ -148,7 +148,7 @@ class MicrosoftSTTOptions(BaseModel): key: str = Field(..., description="Azure subscription key") region: str = Field(..., description="Azure region (e.g., eastus)") language: str = Field(..., description="Language code (e.g., zh-CN)") - phrase_list: Optional[list[str]] = Field(default=None, description="Microsoft ASR phrase list") + phrase_list: Optional[List[str]] = Field(default=None, description="Microsoft ASR phrase list") additional_params: Optional[Dict[str, Any]] = Field(default=None) @@ -183,7 +183,7 @@ class TencentTTSOptions(BaseModel): emotion_category: Optional[str] = Field(default=None, description="Tencent TTS emotion category") emotion_intensity: Optional[int] = Field(default=None, description="Tencent TTS emotion intensity") additional_params: Optional[Dict[str, Any]] = Field(default=None, description="Additional Tencent TTS params") - skip_patterns: Optional[list[int]] = Field(default=None) + skip_patterns: Optional[List[int]] = Field(default=None) class TencentTTS(_BaseTTSCompat): @@ -239,7 +239,7 @@ class BytedanceTTSOptions(BaseModel): pitch_ratio: Optional[float] = Field(default=None, description="Bytedance TTS pitch ratio") emotion: Optional[str] = Field(default=None, description="Bytedance TTS emotion") additional_params: Optional[Dict[str, Any]] = Field(default=None, description="Additional Bytedance TTS params") - skip_patterns: Optional[list[int]] = Field(default=None) + skip_patterns: Optional[List[int]] = Field(default=None) class BytedanceTTS(_BaseTTSCompat): @@ -290,7 +290,7 @@ class BytedanceDuplexTTSOptions(BaseModel): app_id: str = Field(..., description="Bytedance Duplex TTS app id") speaker: str = Field(..., description="Bytedance Duplex TTS speaker") additional_params: Optional[Dict[str, Any]] = Field(default=None, description="Additional Bytedance Duplex TTS params") - skip_patterns: Optional[list[int]] = Field(default=None) + skip_patterns: Optional[List[int]] = Field(default=None) class BytedanceDuplexTTS(_BaseTTSCompat): @@ -333,7 +333,7 @@ class CosyVoiceTTSOptions(BaseModel): sample_rate: Optional[int] = Field(default=None, description="Output sample rate in Hz") voice: Optional[str] = Field(default=None, description="CosyVoice voice") additional_params: Optional[Dict[str, Any]] = Field(default=None, description="CosyVoice TTS params from REST doc") - skip_patterns: Optional[list[int]] = Field(default=None) + skip_patterns: Optional[List[int]] = Field(default=None) class CosyVoiceTTS(_BaseTTSCompat): @@ -377,7 +377,7 @@ class StepFunTTSOptions(BaseModel): model: Optional[str] = Field(default=None, description="StepFun TTS model") voice_id: Optional[str] = Field(default=None, description="StepFun TTS voice id") additional_params: Optional[Dict[str, Any]] = Field(default=None, description="StepFun TTS params from REST doc") - skip_patterns: Optional[list[int]] = Field(default=None) + skip_patterns: Optional[List[int]] = Field(default=None) class StepFunTTS(_BaseTTSCompat): @@ -420,7 +420,7 @@ class MicrosoftTTSOptions(BaseModel): speed: Optional[float] = Field(default=None, description="Speaking rate multiplier") volume: Optional[float] = Field(default=None, description="Audio volume") additional_params: Optional[Dict[str, Any]] = Field(default=None, description="Additional Microsoft TTS params") - skip_patterns: Optional[list[int]] = Field(default=None) + skip_patterns: Optional[List[int]] = Field(default=None) class MicrosoftTTS(_BaseTTSCompat): @@ -463,12 +463,12 @@ class MiniMaxTTSOptions(BaseModel): emotion: Optional[str] = Field(default=None, description="Emotion style") latex_read: Optional[bool] = Field(default=None, description="Whether to read LaTeX expressions") english_normalization: Optional[bool] = Field(default=None, description="Whether to normalize English text") - timber_weights: Optional[list[Dict[str, Any]]] = Field(default=None, description="Alternative timbre mix config") + timber_weights: Optional[List[Dict[str, Any]]] = Field(default=None, description="Alternative timbre mix config") sample_rate: Optional[int] = Field(default=None, description="Output sample rate in Hz") pronunciation_dict: Optional[Dict[str, Any]] = Field(default=None, description="Pronunciation replacement dictionary") language_boost: Optional[str] = Field(default=None, description="Language boost strategy") additional_params: Optional[Dict[str, Any]] = Field(default=None, description="Additional MiniMax TTS params") - skip_patterns: Optional[list[int]] = Field(default=None) + skip_patterns: Optional[List[int]] = Field(default=None) @model_validator(mode="after") def _validate_params(self) -> "MiniMaxTTSOptions": @@ -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: List[Dict[str, Any]] = Field(..., alias="sceneList", description="SenseTime scene list") enable: Optional[bool] = Field(default=None) additional_params: Optional[Dict[str, Any]] = Field(default=None) From 99ddafc2e41668ac3d845f191095dd8fab18c41e Mon Sep 17 00:00:00 2001 From: plutoless Date: Wed, 17 Jun 2026 11:54:54 -0700 Subject: [PATCH 2/8] fix(agent): place __new__ overloads adjacent to impl for mypy Overloads were gated behind TYPE_CHECKING while the implementation lived outside it, producing no-overload-impl/no-redef/name-defined. Un-gate them (runtime-safe via __future__ annotations) and import CNAgent/GlobalAgent under TYPE_CHECKING. --- src/agora_agent/agentkit/agent.py | 90 ++++++++++++++++--------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/agora_agent/agentkit/agent.py b/src/agora_agent/agentkit/agent.py index 2590561..ec6338d 100644 --- a/src/agora_agent/agentkit/agent.py +++ b/src/agora_agent/agentkit/agent.py @@ -351,52 +351,54 @@ class Agent: """ if typing.TYPE_CHECKING: + from .regional_agent import CNAgent, GlobalAgent + _GlobalArea = typing_extensions.Literal[Area.US, Area.EU, Area.AP] - @typing.overload - def __new__( - cls, - client: "Agora[typing_extensions.Literal[Area.CN]]", - *args: typing.Any, - **kwargs: typing.Any, - ) -> "CNAgent": - ... - - @typing.overload - def __new__( - cls, - client: "Agora[_GlobalArea]", - *args: typing.Any, - **kwargs: typing.Any, - ) -> "GlobalAgent": - ... - - @typing.overload - def __new__( - cls, - client: "AsyncAgora[typing_extensions.Literal[Area.CN]]", - *args: typing.Any, - **kwargs: typing.Any, - ) -> "CNAgent": - ... - - @typing.overload - def __new__( - cls, - client: "AsyncAgora[_GlobalArea]", - *args: typing.Any, - **kwargs: typing.Any, - ) -> "GlobalAgent": - ... - - @typing.overload - def __new__( - cls, - client: typing.Any, - *args: typing.Any, - **kwargs: typing.Any, - ) -> "Agent": - ... + @typing.overload + def __new__( + cls, + client: "Agora[typing_extensions.Literal[Area.CN]]", + *args: typing.Any, + **kwargs: typing.Any, + ) -> "CNAgent": + ... + + @typing.overload + def __new__( + cls, + client: "Agora[_GlobalArea]", + *args: typing.Any, + **kwargs: typing.Any, + ) -> "GlobalAgent": + ... + + @typing.overload + def __new__( + cls, + client: "AsyncAgora[typing_extensions.Literal[Area.CN]]", + *args: typing.Any, + **kwargs: typing.Any, + ) -> "CNAgent": + ... + + @typing.overload + def __new__( + cls, + client: "AsyncAgora[_GlobalArea]", + *args: typing.Any, + **kwargs: typing.Any, + ) -> "GlobalAgent": + ... + + @typing.overload + def __new__( + cls, + client: typing.Any, + *args: typing.Any, + **kwargs: typing.Any, + ) -> "Agent": + ... def __new__( cls, From f3c7a3cffe48add171d3ea082fce9d88d1d9cbc9 Mon Sep 17 00:00:00 2001 From: plutoless Date: Wed, 17 Jun 2026 11:57:47 -0700 Subject: [PATCH 3/8] fix(agent): type turn_detection as model-or-dict to match runtime The runtime has accepted a dict since 583eccc (coerced to the model at parse_obj_as time). Widen the constructor, with_turn_detection, the property, and _resolve_asr_config to Union[TurnDetectionConfig, Dict]. --- src/agora_agent/agentkit/agent.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/agora_agent/agentkit/agent.py b/src/agora_agent/agentkit/agent.py index ec6338d..6e9d3ac 100644 --- a/src/agora_agent/agentkit/agent.py +++ b/src/agora_agent/agentkit/agent.py @@ -93,6 +93,7 @@ AvatarConfig = StartAgentsRequestPropertiesAvatar AvatarVendor = StartAgentsRequestPropertiesAvatarVendor TurnDetectionConfig = StartAgentsRequestPropertiesTurnDetection +TurnDetectionInput = typing.Union[TurnDetectionConfig, typing.Dict[str, typing.Any]] SalConfig = StartAgentsRequestPropertiesSal SalMode = StartAgentsRequestPropertiesSalSalMode AdvancedFeatures = StartAgentsRequestPropertiesAdvancedFeatures @@ -424,7 +425,7 @@ def __init__( self, client: typing.Any, instructions: typing.Optional[str] = None, - turn_detection: typing.Optional[TurnDetectionConfig] = None, + turn_detection: typing.Optional[TurnDetectionInput] = None, interruption: typing.Optional[InterruptionConfig] = None, sal: typing.Optional[SalConfig] = None, advanced_features: typing.Optional[AdvancedFeatures] = None, @@ -543,7 +544,7 @@ def with_avatar(self, vendor: BaseAvatar) -> "Agent": new_agent._avatar_required_sample_rate = required_sample_rate return new_agent - def with_turn_detection(self, config: TurnDetectionConfig) -> "Agent": + def with_turn_detection(self, config: TurnDetectionInput) -> "Agent": new_agent = self._clone() new_agent._turn_detection = config return new_agent @@ -735,7 +736,7 @@ def mllm(self) -> typing.Optional[typing.Dict[str, typing.Any]]: return self._mllm @property - def turn_detection(self) -> typing.Optional[TurnDetectionConfig]: + def turn_detection(self) -> typing.Optional[TurnDetectionInput]: return self._turn_detection @property @@ -1074,7 +1075,7 @@ def _resolve_llm_config(self) -> typing.Dict[str, typing.Any]: llm_config["max_history"] = self._max_history return llm_config - def _resolve_asr_config(self, turn_detection_config: TurnDetectionConfig) -> typing.Dict[str, typing.Any]: + def _resolve_asr_config(self, turn_detection_config: TurnDetectionInput) -> typing.Dict[str, typing.Any]: asr_config = dict(self._stt or {}) if not asr_config: asr_config["vendor"] = "ares" From a9ad9263b8d46dc4ef92e45c6099ebf2df9015f0 Mon Sep 17 00:00:00 2001 From: plutoless Date: Wed, 17 Jun 2026 11:59:30 -0700 Subject: [PATCH 4/8] fix(pool_client): order __new__/__init__ overloads with their impls Un-gate the overloads from TYPE_CHECKING and interleave so each overload group directly precedes its implementation, as mypy requires. Runtime-safe via __future__ annotations. --- src/agora_agent/pool_client.py | 408 ++++++++++++++++----------------- 1 file changed, 202 insertions(+), 206 deletions(-) diff --git a/src/agora_agent/pool_client.py b/src/agora_agent/pool_client.py index 440af10..752499b 100644 --- a/src/agora_agent/pool_client.py +++ b/src/agora_agent/pool_client.py @@ -209,109 +209,56 @@ class Agora(BaseAgora, typing.Generic[_AreaT]): ) """ - if typing.TYPE_CHECKING: - - @typing.overload - def __new__( - cls, - *, - area: typing_extensions.Literal[Area.CN], - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.Client] = None, - debug: bool = False, - ) -> "CNAgora": ... - - @typing.overload - def __new__( - cls, - *, - area: _GlobalArea, - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.Client] = None, - debug: bool = False, - ) -> "GlobalAgoraClient": ... - - @typing.overload - def __new__( - cls, - *, - area: _AreaT, - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.Client] = None, - debug: bool = False, - ) -> "Agora[_AreaT]": ... - - @typing.overload - def __init__( - self: "Agora[typing_extensions.Literal[Area.CN]]", - *, - area: typing_extensions.Literal[Area.CN], - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.Client] = None, - debug: bool = False, - ) -> None: ... - - @typing.overload - def __init__( - self: "Agora[_GlobalArea]", - *, - area: _GlobalArea, - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.Client] = None, - debug: bool = False, - ) -> None: ... - - @typing.overload - def __init__( - self, - *, - area: _AreaT, - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.Client] = None, - debug: bool = False, - ) -> None: ... + @typing.overload + def __new__( + cls, + *, + area: typing_extensions.Literal[Area.CN], + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + debug: bool = False, + ) -> "CNAgora": ... + + @typing.overload + def __new__( + cls, + *, + area: _GlobalArea, + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + debug: bool = False, + ) -> "GlobalAgoraClient": ... + + @typing.overload + def __new__( + cls, + *, + area: _AreaT, + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + debug: bool = False, + ) -> "Agora[_AreaT]": ... def __new__(cls, **kwargs: typing.Any) -> typing.Any: if cls is Agora: @@ -322,6 +269,57 @@ def __new__(cls, **kwargs: typing.Any) -> typing.Any: return object.__new__(GlobalAgoraClient) return object.__new__(cls) + @typing.overload + def __init__( + self: "Agora[typing_extensions.Literal[Area.CN]]", + *, + area: typing_extensions.Literal[Area.CN], + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + debug: bool = False, + ) -> None: ... + + @typing.overload + def __init__( + self: "Agora[_GlobalArea]", + *, + area: _GlobalArea, + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + debug: bool = False, + ) -> None: ... + + @typing.overload + def __init__( + self, + *, + area: _AreaT, + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + debug: bool = False, + ) -> None: ... + def __init__( self, *, @@ -547,109 +545,56 @@ class AsyncAgora(BaseAsyncAgora, typing.Generic[_AreaT]): ) """ - if typing.TYPE_CHECKING: - - @typing.overload - def __new__( - cls, - *, - area: typing_extensions.Literal[Area.CN], - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.AsyncClient] = None, - debug: bool = False, - ) -> "CNAsyncAgora": ... - - @typing.overload - def __new__( - cls, - *, - area: _GlobalArea, - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.AsyncClient] = None, - debug: bool = False, - ) -> "GlobalAsyncAgoraClient": ... - - @typing.overload - def __new__( - cls, - *, - area: _AreaT, - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.AsyncClient] = None, - debug: bool = False, - ) -> "AsyncAgora[_AreaT]": ... - - @typing.overload - def __init__( - self: "AsyncAgora[typing_extensions.Literal[Area.CN]]", - *, - area: typing_extensions.Literal[Area.CN], - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.AsyncClient] = None, - debug: bool = False, - ) -> None: ... - - @typing.overload - def __init__( - self: "AsyncAgora[_GlobalArea]", - *, - area: _GlobalArea, - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.AsyncClient] = None, - debug: bool = False, - ) -> None: ... - - @typing.overload - def __init__( - self, - *, - area: _AreaT, - app_id: str, - app_certificate: str, - customer_id: typing.Optional[str] = None, - customer_secret: typing.Optional[str] = None, - auth_token: typing.Optional[str] = None, - headers: typing.Optional[typing.Dict[str, str]] = None, - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = True, - httpx_client: typing.Optional[httpx.AsyncClient] = None, - debug: bool = False, - ) -> None: ... + @typing.overload + def __new__( + cls, + *, + area: typing_extensions.Literal[Area.CN], + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + debug: bool = False, + ) -> "CNAsyncAgora": ... + + @typing.overload + def __new__( + cls, + *, + area: _GlobalArea, + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + debug: bool = False, + ) -> "GlobalAsyncAgoraClient": ... + + @typing.overload + def __new__( + cls, + *, + area: _AreaT, + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + debug: bool = False, + ) -> "AsyncAgora[_AreaT]": ... def __new__(cls, **kwargs: typing.Any) -> typing.Any: if cls is AsyncAgora: @@ -660,6 +605,57 @@ def __new__(cls, **kwargs: typing.Any) -> typing.Any: return object.__new__(GlobalAsyncAgoraClient) return object.__new__(cls) + @typing.overload + def __init__( + self: "AsyncAgora[typing_extensions.Literal[Area.CN]]", + *, + area: typing_extensions.Literal[Area.CN], + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + debug: bool = False, + ) -> None: ... + + @typing.overload + def __init__( + self: "AsyncAgora[_GlobalArea]", + *, + area: _GlobalArea, + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + debug: bool = False, + ) -> None: ... + + @typing.overload + def __init__( + self, + *, + area: _AreaT, + app_id: str, + app_certificate: str, + customer_id: typing.Optional[str] = None, + customer_secret: typing.Optional[str] = None, + auth_token: typing.Optional[str] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + debug: bool = False, + ) -> None: ... + def __init__( self, *, From 07e810adc8eff27a016fc4904205797bd2cd89c7 Mon Sep 17 00:00:00 2001 From: plutoless Date: Wed, 17 Jun 2026 12:02:45 -0700 Subject: [PATCH 5/8] fix(pool_client): broaden __init__ impl area param to Any Reordering the overloads adjacent to their impls (prior commit) surfaced a latent mismatch: the impl signature area: _AreaT did not accept overload signatures binding area to Literal[Area.CN]/_GlobalArea. Per mypy convention the overload *implementation* signature uses Any; callers still see the precise overloads. --- src/agora_agent/pool_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/agora_agent/pool_client.py b/src/agora_agent/pool_client.py index 752499b..03b402a 100644 --- a/src/agora_agent/pool_client.py +++ b/src/agora_agent/pool_client.py @@ -323,7 +323,7 @@ def __init__( def __init__( self, *, - area: _AreaT, + area: typing.Any, app_id: str, app_certificate: str, customer_id: typing.Optional[str] = None, @@ -659,7 +659,7 @@ def __init__( def __init__( self, *, - area: _AreaT, + area: typing.Any, app_id: str, app_certificate: str, customer_id: typing.Optional[str] = None, From 003660cee9bfda6368267559da8db26c8f6efbff Mon Sep 17 00:00:00 2001 From: plutoless Date: Wed, 17 Jun 2026 12:04:06 -0700 Subject: [PATCH 6/8] fix(regional_agent): narrow return type only, not vendor params Narrowing the param types statically enforced the very vendor/area compatibility PR #37 says it does NOT enforce (and which tests exercise cross-region). Keep the -> CNAgent/-> GlobalAgent return narrowing for fluent chaining; accept the base vendor types. Valid override, no type: ignore. --- src/agora_agent/agentkit/regional_agent.py | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/agora_agent/agentkit/regional_agent.py b/src/agora_agent/agentkit/regional_agent.py index c8742b3..e7982c7 100644 --- a/src/agora_agent/agentkit/regional_agent.py +++ b/src/agora_agent/agentkit/regional_agent.py @@ -108,31 +108,31 @@ class CNAgent(Agent): - def with_stt(self, vendor: CNSTT) -> "CNAgent": - return typing.cast("CNAgent", super().with_stt(typing.cast(BaseSTT, vendor))) + def with_stt(self, vendor: BaseSTT) -> "CNAgent": + return typing.cast("CNAgent", super().with_stt(vendor)) - def with_llm(self, vendor: CNLLM) -> "CNAgent": - return typing.cast("CNAgent", super().with_llm(typing.cast(BaseLLM, vendor))) + def with_llm(self, vendor: BaseLLM) -> "CNAgent": + return typing.cast("CNAgent", super().with_llm(vendor)) - def with_tts(self, vendor: CNTTS) -> "CNAgent": - return typing.cast("CNAgent", super().with_tts(typing.cast(BaseTTS, vendor))) + def with_tts(self, vendor: BaseTTS) -> "CNAgent": + return typing.cast("CNAgent", super().with_tts(vendor)) - def with_avatar(self, vendor: CNAvatar) -> "CNAgent": - return typing.cast("CNAgent", super().with_avatar(typing.cast(BaseAvatar, vendor))) + def with_avatar(self, vendor: BaseAvatar) -> "CNAgent": + return typing.cast("CNAgent", super().with_avatar(vendor)) class GlobalAgent(Agent): - def with_stt(self, vendor: GlobalSTT) -> "GlobalAgent": - return typing.cast("GlobalAgent", super().with_stt(typing.cast(BaseSTT, vendor))) + def with_stt(self, vendor: BaseSTT) -> "GlobalAgent": + return typing.cast("GlobalAgent", super().with_stt(vendor)) - def with_llm(self, vendor: GlobalLLM) -> "GlobalAgent": - return typing.cast("GlobalAgent", super().with_llm(typing.cast(BaseLLM, vendor))) + def with_llm(self, vendor: BaseLLM) -> "GlobalAgent": + return typing.cast("GlobalAgent", super().with_llm(vendor)) - def with_tts(self, vendor: GlobalTTS) -> "GlobalAgent": - return typing.cast("GlobalAgent", super().with_tts(typing.cast(BaseTTS, vendor))) + def with_tts(self, vendor: BaseTTS) -> "GlobalAgent": + return typing.cast("GlobalAgent", super().with_tts(vendor)) - def with_avatar(self, vendor: GlobalAvatar) -> "GlobalAgent": - return typing.cast("GlobalAgent", super().with_avatar(typing.cast(BaseAvatar, vendor))) + def with_avatar(self, vendor: BaseAvatar) -> "GlobalAgent": + return typing.cast("GlobalAgent", super().with_avatar(vendor)) RegionalAgent = typing.Union[CNAgent, GlobalAgent] From 9f3986ab958f49458ba0fc073d034a82d4d6e35a Mon Sep 17 00:00:00 2001 From: plutoless Date: Wed, 17 Jun 2026 12:05:14 -0700 Subject: [PATCH 7/8] fix(root): import AgentClient/AsyncAgentClient from .pool_client They are defined in pool_client (= Agora/AsyncAgora), not agentkit. Only the TYPE_CHECKING import location was wrong. --- src/agora_agent/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/agora_agent/__init__.py b/src/agora_agent/__init__.py index b2a41ac..56b91e7 100644 --- a/src/agora_agent/__init__.py +++ b/src/agora_agent/__init__.py @@ -12,14 +12,12 @@ if typing.TYPE_CHECKING: from . import agents, agentkit, core, phone_numbers, telephony from .core.domain import Area, Pool, create_pool - from .pool_client import Agora, AsyncAgora + from .pool_client import Agora, AsyncAgora, AgentClient, AsyncAgentClient from .version import __version__ from .agentkit import ( Agent, AgentSession, AgentSessionOptions, - AgentClient, - AsyncAgentClient, CNAgent, GlobalAgent, GenericAvatar, From 16096c9de250e0284476841e28194d581373bc10 Mon Sep 17 00:00:00 2001 From: plutoless Date: Wed, 17 Jun 2026 12:06:22 -0700 Subject: [PATCH 8/8] test: fix py3.8 type issues (typing.List, guard Optional stt indexing) Co-Authored-By: Claude Opus 4.8 --- tests/custom/test_regional_vendors.py | 2 ++ tests/custom/test_sensetime_avatar.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/custom/test_regional_vendors.py b/tests/custom/test_regional_vendors.py index a1c42b8..b1d32fb 100644 --- a/tests/custom/test_regional_vendors.py +++ b/tests/custom/test_regional_vendors.py @@ -59,6 +59,7 @@ def test_cn_client_allows_global_only_vendor() -> None: DeepgramSTT(api_key="dg-key", model="nova-2", language="en-US") ) assert agent.__class__.__name__ == "CNAgent" + assert agent.stt is not None assert agent.stt["vendor"] == "deepgram" @@ -69,6 +70,7 @@ def test_global_client_allows_cn_only_vendor() -> None: ) agent = Agent(client=client).with_stt(tencent_stt) assert agent.__class__.__name__ == "GlobalAgent" + assert agent.stt is not None assert agent.stt["vendor"] == "tencent" diff --git a/tests/custom/test_sensetime_avatar.py b/tests/custom/test_sensetime_avatar.py index 732d336..ca9a729 100644 --- a/tests/custom/test_sensetime_avatar.py +++ b/tests/custom/test_sensetime_avatar.py @@ -1,5 +1,6 @@ import pytest from types import SimpleNamespace +from typing import Any, Dict, List from agora_agent.agentkit import Agent, AgentSession, validate_avatar_config from agora_agent.agentkit.avatar_types import is_sensetime_avatar @@ -35,7 +36,7 @@ def _session(agent): ) -def _scene_list() -> list[dict]: +def _scene_list() -> List[Dict[str, Any]]: return [{"digital_role": {"face_feature_id": "role-1"}}]