From ca1572f7aae734ddeff621df42c492faf4e22f01 Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 16 Jun 2026 15:04:40 +0100 Subject: [PATCH 1/5] migrating to the faster direct API-key session-start route --- src/anam/_api.py | 98 ++++++++++++------------------------------------ 1 file changed, 24 insertions(+), 74 deletions(-) diff --git a/src/anam/_api.py b/src/anam/_api.py index e224237..71a2810 100644 --- a/src/anam/_api.py +++ b/src/anam/_api.py @@ -20,7 +20,8 @@ class CoreApiClient: """Internal client for Anam REST API. - Handles session token retrieval and session creation. + Starts engine sessions via the direct API-key path. No session token is minted. + Due to the exposing the API key, this code should only run in a trusted environment. """ def __init__( @@ -32,78 +33,18 @@ def __init__( self._options = options or ClientOptions() self._base_url = self._options.api_base_url self._api_version = self._options.api_version - self._session_token: str | None = None @property def _api_url(self) -> str: """Get the full API URL.""" return f"{self._base_url}/{self._api_version}" - async def get_session_token( - self, persona_config: PersonaConfig, session_options: SessionOptions - ) -> str: - """Get a session token using the API key. - - Args: - persona_config: The persona configuration to use. - session_options: Session options. - Returns: - The session token string. - - Raises: - AuthenticationError: If authentication fails. - AnamError: For other API errors. - """ - url = f"{self._api_url}/auth/session-token" - headers = { - "Content-Type": "application/json", - "Authorization": f"Bearer {self._api_key}", - } - # Use custom client_label if provided, otherwise default to 'python-sdk' - client_label = self._options.client_label or "python-sdk" - body = { - "clientLabel": client_label, - "personaConfig": persona_config.to_dict(), - "sessionOptions": session_options.to_dict(), - } - - logger.debug("Requesting session token from %s", url) - - async with aiohttp.ClientSession() as session: - async with session.post(url, headers=headers, json=body) as response: - data = await response.json() - - if response.status == 200: - token = data.get("sessionToken") - if not token: - raise AnamError( - "No session token in response", - ErrorCode.SERVER_ERROR, - response.status, - ) - self._session_token = token - logger.debug("Session token obtained successfully") - return token - - if response.status == 401 or response.status == 403: - raise AuthenticationError( - "Invalid API key", - details={"response": data}, - ) - - raise AnamError( - f"Failed to get session token: {data.get('message', 'Unknown error')}", - ErrorCode.SERVER_ERROR, - response.status, - details={"response": data}, - ) - async def start_session( self, persona_config: PersonaConfig, session_options: SessionOptions, ) -> SessionInfo: - """Start a new streaming session. + """Start a new streaming session using direct API-key auth. Args: persona_config: The persona configuration. @@ -113,22 +54,23 @@ async def start_session( SessionInfo with connection details. Raises: - SessionError: If session creation fails. + AuthenticationError: If the API key is rejected. + SessionError: If session creation fails for any other reason. """ - # Get session token if we don't have one - if not self._session_token: - await self.get_session_token(persona_config, session_options) - url = f"{self._api_url}/engine/session" headers = { "Content-Type": "application/json", - "Authorization": f"Bearer {self._session_token}", + "Authorization": f"Bearer {self._api_key}", } + client_label = self._options.client_label or "python-sdk" body: dict[str, Any] = { + "clientLabel": client_label, + "personaConfig": persona_config.to_dict(), + "sessionOptions": session_options.to_dict(), "clientMetadata": CLIENT_METADATA, } - logger.debug("Starting session at %s", url) + logger.debug("Starting session at %s (direct API-key auth)", url) async with aiohttp.ClientSession() as session: async with session.post(url, headers=headers, json=body) as response: @@ -139,9 +81,8 @@ async def start_session( logger.debug("Session response: %s", data) return SessionInfo.from_api_response(data) - # Handle specific error codes error_cause = data.get("error", "") - message = data.get("message", "Unknown error") + message = data.get("message", error_cause or "Unknown error") if response.status == 400: raise SessionError( @@ -151,11 +92,20 @@ async def start_session( details={"response": data}, ) - if response.status in (401, 403): + if response.status == 401: + # The direct path returns ``{"error": "Invalid API key"}`` + # for a rejected key; surface that distinctly so callers + # can catch AuthenticationError instead of SessionError. + raise AuthenticationError( + message or "Invalid API key", + details={"response": data}, + ) + + if response.status == 403: raise SessionError( f"Authentication failed: {message}", ErrorCode.AUTHENTICATION_ERROR, - response.status, + 403, details={"response": data}, ) @@ -198,7 +148,7 @@ async def start_session( details={"response": data}, ) - raise SessionError( + raise AnamError( f"Failed to start session: {message}", ErrorCode.SERVER_ERROR, response.status, From 5b5d377d33f2c4ee35b0aef405e0563fe9c3a4fd Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 16 Jun 2026 17:36:55 +0100 Subject: [PATCH 2/5] reprhase docstring --- src/anam/_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/anam/_api.py b/src/anam/_api.py index 71a2810..feafd8f 100644 --- a/src/anam/_api.py +++ b/src/anam/_api.py @@ -20,8 +20,8 @@ class CoreApiClient: """Internal client for Anam REST API. - Starts engine sessions via the direct API-key path. No session token is minted. - Due to the exposing the API key, this code should only run in a trusted environment. + Starts engine sessions via the direct API-key path. + As this requires an API key, this code should only run in a trusted environment. """ def __init__( From 56c16fb149dd84a61d22a033542feb7f6d1bee07 Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 16 Jun 2026 17:42:32 +0100 Subject: [PATCH 3/5] comment --- src/anam/_api.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/anam/_api.py b/src/anam/_api.py index feafd8f..9013785 100644 --- a/src/anam/_api.py +++ b/src/anam/_api.py @@ -93,9 +93,6 @@ async def start_session( ) if response.status == 401: - # The direct path returns ``{"error": "Invalid API key"}`` - # for a rejected key; surface that distinctly so callers - # can catch AuthenticationError instead of SessionError. raise AuthenticationError( message or "Invalid API key", details={"response": data}, From 30ec01c5628c14650778c5338aacfe46783876eb Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 16 Jun 2026 17:47:48 +0100 Subject: [PATCH 4/5] simplify docstring --- src/anam/_api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/anam/_api.py b/src/anam/_api.py index 9013785..dbbff2f 100644 --- a/src/anam/_api.py +++ b/src/anam/_api.py @@ -20,8 +20,7 @@ class CoreApiClient: """Internal client for Anam REST API. - Starts engine sessions via the direct API-key path. - As this requires an API key, this code should only run in a trusted environment. + Starts sessions using the direct API-key path. """ def __init__( From 50bfed23e4f56fe1bf6fdff4e5d870eeacc85b0f Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Wed, 17 Jun 2026 18:48:20 +0100 Subject: [PATCH 5/5] docstring fix --- src/anam/_api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/anam/_api.py b/src/anam/_api.py index dbbff2f..f28c769 100644 --- a/src/anam/_api.py +++ b/src/anam/_api.py @@ -53,8 +53,9 @@ async def start_session( SessionInfo with connection details. Raises: - AuthenticationError: If the API key is rejected. - SessionError: If session creation fails for any other reason. + AuthenticationError: If the API key is rejected (HTTP 401). + SessionError: If session creation fails (HTTP 400-429). + AnamError: For any other unexpected server response. """ url = f"{self._api_url}/engine/session" headers = {