Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 21 additions & 74 deletions src/anam/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
class CoreApiClient:
"""Internal client for Anam REST API.

Handles session token retrieval and session creation.
Starts sessions using the direct API-key path.
"""

def __init__(
Expand All @@ -32,78 +32,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.
Expand All @@ -113,22 +53,24 @@ async def start_session(
SessionInfo with connection details.

Raises:
SessionError: If session creation fails.
AuthenticationError: If the API key is rejected (HTTP 401).
SessionError: If session creation fails (HTTP 400-429).
AnamError: For any other unexpected server response.
"""
# 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:
Expand All @@ -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(
Expand All @@ -151,11 +92,17 @@ async def start_session(
details={"response": data},
)

if response.status in (401, 403):
if response.status == 401:
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},
)

Expand Down Expand Up @@ -198,7 +145,7 @@ async def start_session(
details={"response": data},
)

raise SessionError(
raise AnamError(
Comment thread
sebvanleuven marked this conversation as resolved.
f"Failed to start session: {message}",
ErrorCode.SERVER_ERROR,
response.status,
Expand Down
Loading