Skip to main content
The Python client is published as asapi (import from asapi import AsapiClient). A fallback PyPI name may be agentserviceapi — check release notes for pip install lines.

Install

python -m venv .venv
source .venv/bin/activate
pip install asapi
Editable install from a monorepo:
pip install -e /path/to/asapi-python
The agentservice CLI bundles asapi as well. See CLI.

Authentication

from asapi import AsapiClient

client = AsapiClient(base_url="https://sudoiq.com")
client.set_api_key("sk_...")
Keys are created in Sudoiq settings.

Add a tool

Tools have two parts: a schema on the server (so the model knows the name and arguments) and, for interactive runs, a local async handler in your process. Registering schemas on Sudoiq is not something you repeat on every agent run. Do it once when your application starts (or whenever you deploy a new build that changes tools)—not inside the hot path that calls execute_agent for each task. After a tool is registered, it stays on the platform until you explicitly delete it. If you change the name, description, or parameters later, register again with the revised schema so Sudoiq stays in sync with what the model and your handler implement.

1. Register the schema

from asapi.models.tools import ToolDefinition, ParametersSchema, ParameterProperty

await client.tools.register_tools([
    ToolDefinition(
        name="get_current_time",
        namespace="main",
        description="Return the current UTC time as an ISO 8601 string.",
        schema_data=ParametersSchema(
            properties={},
            required=[],
        ),
    ),
])

2. Attach a handler and run

Handlers are usually async def with signature (args: dict, agent_id: str, task_id: int) -> str. The client wraps each tool call so it can record errors, traces, and usage metrics alongside normal execution. When the model invokes a tool, args are exactly what the model supplied; agent_id and task_id identify the run that asked for it. That layout lets you register one handler and reuse it across many agents, while still branching on agent_id (or on values in args) when different workflows need different behavior.
import asyncio
from datetime import datetime, timezone

async def get_current_time(args: dict, agent_id: str, task_id: int) -> str:
    return datetime.now(timezone.utc).isoformat()

client.set_tools({"get_current_time": (None, get_current_time)})

result = await client.run_agent(
    agent_id="YOUR_AGENT_UUID",
    string_inputs={"1": "What time is it?"},
    environment="playground",
)
The tool name must match what the agent graph expects. See Register server tools and Interactive runs and local tools.

Tests and feedback

Store test examples and record run feedback to drive quality and workflow iteration. See Evals, tests, and feedback.

Integration patterns

For long-running or fully asynchronous backends, you can start runs and consume updates without holding a single long-lived connection—see Running agents when you need that split.