Skip to content
Learn Agentic AI13 min read0 views

Building Agent SDKs: JavaScript, Python, and REST Clients for Your Agent Platform

Design and build developer-friendly SDKs for an AI agent platform, covering API client generation, error handling patterns, streaming support, and versioning strategies that maintain backward compatibility.

SDKs Define Developer Experience

The quality of your SDK determines how quickly developers integrate your agent platform. A great SDK reduces integration from hours to minutes. A bad SDK generates support tickets. The best agent platform SDKs feel like natural extensions of the developer's language — Pythonic in Python, idiomatic in JavaScript, and consistent with the conventions developers already know.

The non-obvious lesson is that SDK design is API design. If your SDK feels awkward, the underlying API is probably wrong. Fix the API first, then the SDK writes itself.

Python SDK Design

The Python SDK should support both synchronous and asynchronous usage, handle streaming responses, and provide clear error types:

# agentplatform/client.py — Python SDK core client
import httpx
from typing import Optional, AsyncIterator
from dataclasses import dataclass

class AgentPlatformError(Exception):
    def __init__(self, status_code: int, message: str, error_code: str = None):
        self.status_code = status_code
        self.message = message
        self.error_code = error_code
        super().__init__(f"[{status_code}] {message}")

class AuthenticationError(AgentPlatformError):
    pass

class RateLimitError(AgentPlatformError):
    def __init__(self, retry_after: int, **kwargs):
        self.retry_after = retry_after
        super().__init__(**kwargs)

class NotFoundError(AgentPlatformError):
    pass

@dataclass
class ChatResponse:
    message: str
    conversation_id: str
    agent_id: str
    tool_calls: list
    tokens_used: int
    latency_ms: float

class AgentPlatform:
    """Synchronous client for the Agent Platform API."""

    BASE_URL = "https://api.agentplatform.com/v1"

    def __init__(self, api_key: str, base_url: str = None, timeout: float = 30.0):
        self.api_key = api_key
        self.base_url = base_url or self.BASE_URL
        self._client = httpx.Client(
            base_url=self.base_url,
            headers={"X-API-Key": api_key, "Content-Type": "application/json"},
            timeout=timeout,
        )

    def chat(
        self,
        agent_id: str,
        message: str,
        conversation_id: Optional[str] = None,
        metadata: Optional[dict] = None,
    ) -> ChatResponse:
        payload = {"message": message}
        if conversation_id:
            payload["conversation_id"] = conversation_id
        if metadata:
            payload["metadata"] = metadata

        resp = self._request("POST", f"/agents/{agent_id}/chat", json=payload)
        return ChatResponse(**resp)

    def list_agents(self, page: int = 1, per_page: int = 20) -> dict:
        return self._request("GET", "/agents", params={"page": page, "per_page": per_page})

    def get_agent(self, agent_id: str) -> dict:
        return self._request("GET", f"/agents/{agent_id}")

    def _request(self, method: str, path: str, **kwargs) -> dict:
        resp = self._client.request(method, path, **kwargs)
        if resp.status_code == 401:
            raise AuthenticationError(401, "Invalid API key")
        if resp.status_code == 404:
            raise NotFoundError(404, "Resource not found")
        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 60))
            raise RateLimitError(
                retry_after=retry_after, status_code=429, message="Rate limit exceeded"
            )
        if resp.status_code >= 400:
            body = resp.json()
            raise AgentPlatformError(
                resp.status_code, body.get("detail", "Unknown error"), body.get("code")
            )
        return resp.json()

    def close(self):
        self._client.close()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()

Notice the context manager support. This ensures proper connection cleanup and lets developers write:

with AgentPlatform(api_key="sk-...") as client:
    response = client.chat("agent-123", "What are your business hours?")
    print(response.message)

Async Client with Streaming

For production applications, provide an async client that supports streaming responses:

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

# agentplatform/async_client.py — Async SDK with streaming
import httpx
from typing import AsyncIterator

class AsyncAgentPlatform:
    """Asynchronous client with streaming support."""

    BASE_URL = "https://api.agentplatform.com/v1"

    def __init__(self, api_key: str, base_url: str = None, timeout: float = 30.0):
        self.api_key = api_key
        self.base_url = base_url or self.BASE_URL
        self._client = httpx.AsyncClient(
            base_url=self.base_url,
            headers={"X-API-Key": api_key, "Content-Type": "application/json"},
            timeout=timeout,
        )

    async def chat(self, agent_id: str, message: str, **kwargs) -> ChatResponse:
        payload = {"message": message, **kwargs}
        resp = await self._request("POST", f"/agents/{agent_id}/chat", json=payload)
        return ChatResponse(**resp)

    async def chat_stream(
        self, agent_id: str, message: str, **kwargs
    ) -> AsyncIterator[str]:
        payload = {"message": message, "stream": True, **kwargs}
        async with self._client.stream(
            "POST",
            f"/agents/{agent_id}/chat",
            json=payload,
        ) as resp:
            if resp.status_code >= 400:
                body = await resp.aread()
                raise AgentPlatformError(resp.status_code, body.decode())
            async for line in resp.aiter_lines():
                if line.startswith("data: "):
                    chunk = line[6:]
                    if chunk == "[DONE]":
                        break
                    yield chunk

    async def _request(self, method: str, path: str, **kwargs) -> dict:
        resp = await self._client.request(method, path, **kwargs)
        if resp.status_code == 401:
            raise AuthenticationError(401, "Invalid API key")
        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 60))
            raise RateLimitError(
                retry_after=retry_after, status_code=429, message="Rate limit exceeded"
            )
        if resp.status_code >= 400:
            body = resp.json()
            raise AgentPlatformError(resp.status_code, body.get("detail", "Unknown error"))
        return resp.json()

    async def close(self):
        await self._client.aclose()

    async def __aenter__(self):
        return self

    async def __aexit__(self, *args):
        await self.close()

Usage with streaming:

import asyncio

async def main():
    async with AsyncAgentPlatform(api_key="sk-...") as client:
        async for chunk in client.chat_stream("agent-123", "Explain your pricing"):
            print(chunk, end="", flush=True)
        print()  # Final newline

asyncio.run(main())

Automatic Retry with Backoff

Production SDKs must handle transient failures gracefully:

# agentplatform/retry.py — Retry logic with exponential backoff
import time
import random
from functools import wraps

def with_retry(max_retries=3, base_delay=1.0, max_delay=30.0):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except RateLimitError as e:
                    last_exception = e
                    delay = e.retry_after
                except AgentPlatformError as e:
                    if e.status_code < 500:
                        raise  # Client errors are not retryable
                    last_exception = e
                    delay = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay)

                if attempt < max_retries:
                    time.sleep(delay)
            raise last_exception
        return wrapper
    return decorator

SDK Packaging and Distribution

Structure the SDK as a proper Python package with type hints and clear documentation:

# setup.py (or pyproject.toml) — SDK packaging
from setuptools import setup, find_packages

setup(
    name="agentplatform",
    version="1.2.0",
    packages=find_packages(),
    install_requires=["httpx>=0.25.0"],
    python_requires=">=3.9",
    description="Official Python SDK for the Agent Platform API",
    author="Agent Platform Team",
    url="https://github.com/agentplatform/python-sdk",
    classifiers=[
        "Programming Language :: Python :: 3",
        "Typing :: Typed",
    ],
)

FAQ

How do I handle breaking API changes without breaking existing SDK users?

Use API versioning in the URL path (/v1/, /v2/) and maintain SDK versions that map to API versions. When you release a new API version, release a new major SDK version. Continue supporting the old SDK version with security patches for at least 12 months. In the SDK, default to the latest API version but allow users to pin a specific version.

Should I auto-generate SDKs from an OpenAPI spec or hand-write them?

Use a hybrid approach. Auto-generate the low-level HTTP client and request/response types from your OpenAPI spec, then hand-write the high-level convenience methods, error handling, and streaming logic on top. Pure auto-generated SDKs feel robotic and miss language-specific idioms. Pure hand-written SDKs drift out of sync with the API.

How do I test SDKs without making real API calls in CI?

Use recorded HTTP interactions with a library like vcrpy for Python or nock for Node.js. Record real API responses once, then replay them in CI. This catches serialization bugs and response format changes without requiring live API access. Also maintain a small integration test suite that runs against a staging environment on a weekly schedule.


#SDKDesign #DeveloperExperience #APIClients #Python #JavaScript #AgenticAI #LearnAI #AIEngineering

Share this article
C

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.