Skip to main content

AgentOS SDK for Python

Async Python SDK for AgentOS APIs. The package is a thin client over AgentOS HTTP and SSE endpoints with small compatibility helpers for bearer tokens, stream callbacks, Pythonic method names, and request defaults.

Install

pip install agentos-sdk-python

For local development:

python3 -m venv .venv
.venv/bin/python -m pip install -e ".[dev]"

Quickstart

from agentos_sdk import AgentOSSDK


async def main():
    async with AgentOSSDK(base_url="http://localhost:8888") as sdk:
        version = await sdk.agentos.get_version()
        print(version)

        registration = await sdk.agentos.register_bundle(
            {
                "bundleId": "com.demo.app",
                "appGroupId": "com.demo.group",
                "opentoolServers": [
                    {"ref": "websearch", "name": "opentool-server-websearch"}
                ],
            }
        )
        print(registration.get("opentoolServers", []))

        tasks = await sdk.cronkit.list_tasks(limit=10)
        print(tasks)

AgentOSSDK also exposes TS-style camelCase aliases for common migration paths, such as registerBundle, listTasks, chatSimple, listModels, loadTool, getPeers, and listTransfers.

Transport

The SDK uses httpx.AsyncClient and defaults to http://localhost:8888.

from agentos_sdk import AgentOSSDK

sdk = AgentOSSDK(
    base_url="http://localhost:8888",
    connect_timeout=5.0,
    receive_timeout=10.0,
)

Use async with AgentOSSDK(...) or call await sdk.aclose() when done.

Token Handling

sdk.agentos.register_bundle(...) stores the active app token. Protected facade methods in sdk.agentkit, sdk.cronkit, sdk.authkit, sdk.discoverykit, and sdk.transferkit use that token automatically.

If a protected call fails with HTTP 401, HTTP 403, or recognizable invalid-token text, the SDK re-runs registration for the same bundle once and retries the call. The cached bundle is a defensive copy and retains its opentoolServers declarations during refresh.

AgentOS Events

from agentos_sdk import AgentOSEventHandler, AgentOSSDK


class Handler(AgentOSEventHandler):
    async def on_welcome(self, welcome):
        print("welcome", welcome)

    async def on_app_event(self, event):
        if event["type"] == "toolkit.provision.succeeded":
            print("tool ready", event["data"]["ref"], event["data"]["id"])
        elif event["type"] == "toolkit.provision.failed":
            print("tool failed", event["data"]["error"])

    async def on_done(self):
        print("subscription ended")

    async def on_error(self, error):
        raise error


async with AgentOSSDK() as sdk:
    await sdk.agentos.register_bundle(
        {
            "bundleId": "com.demo.app",
            "opentoolServers": [
                {"ref": "websearch", "name": "opentool-server-websearch"}
            ],
        }
    )
    await sdk.agentos.subscribe(Handler())

Registration statuses are running, starting, and notFound; only running includes a callable tool id. A starting declaration becomes callable after toolkit.provision.succeeded. Stop using a tool while it is toolkit.tool.unavailable and resume after toolkit.tool.available.

on_call_app / onCallApp were removed in 0.2.0. Use on_app_event / onAppEvent.

AgentKit

from agentos_sdk import AgentMessageHandler, AgentOSSDK, ToolReturn


class ChatHandler(AgentMessageHandler):
    async def on_message(self, session_id, message):
        print("message", session_id, message)

    async def on_chunk(self, session_id, chunk):
        print("chunk", session_id, chunk)

    async def on_function_call(self, session_id, function_call):
        return ToolReturn(function_call["id"], {"ok": True})

    async def on_done(self):
        print("done")

    async def on_error(self, error):
        raise error


async with AgentOSSDK() as sdk:
    await sdk.agentos.register_bundle({"bundleId": "com.demo.app"})
    session = await sdk.agentkit.init_session(
        capability={"systemPrompt": "You are a helpful assistant."}
    )
    await sdk.agentkit.chat(
        session,
        {"content": [{"type": "text", "message": "Hello"}]},
        ChatHandler(),
    )

Summary-first history is available without loading every process message:

summaries = await sdk.agentkit.history_summary(session_id=session["sessionId"])
messages = await sdk.agentkit.history_process(
    session_id=session["sessionId"],
    original_task_id=summaries[0]["originalTaskId"],
)

history_summary defaults to page 1 with 30 items; history_process defaults to page 1 with 50 items. Page sizes must be between 1 and 200. CamelCase callers can use historySummary(sessionId=...) and historyProcess(sessionId=..., originalTaskId=...).

Message, chunk, and function-call handlers receive a child sessionId when the event supplies one, otherwise the root session ID. Function-call failures passed to on_error use FunctionCallLifecycleError with stage equal to function_call_parse_failed, function_call_handler_failed, or function_call_callback_failed.

AgentKit 1.0.0 follows the session-only gateway contract. Agent CRUD methods and the init_session(agent_id=...) form have been removed. Define sub-agents inline with Capability.subAgentList:

session = await sdk.agentkit.init_session(
    capability={
        "systemPrompt": "Coordinate the task.",
        "subAgentList": [
            {
                "name": "researcher",
                "capability": {"systemPrompt": "Research the requested topic."},
            }
        ],
    }
)

Direct AgentKit methods include init_session, init_simple, chat, chat_simple, history, history_summary, history_process, stop, clear, callback, and stream_callback.

CronKit

async with AgentOSSDK() as sdk:
    await sdk.agentos.register_bundle({"bundleId": "com.demo.app"})
    task = await sdk.cronkit.create_task(
        request={
            "name": "Daily summary",
            "schedule": "0 9 * * *",
            "spec": {
                "systemPrompt": "You are a scheduled agent.",
                "content": [{"type": "text", "message": "Summarize today."}],
            },
        }
    )
    await sdk.cronkit.run_task_now(cron_id=task["cronId"])

CronKit create requests default concurrency to skipIfRunning, enabled to True, and spec.timeoutSeconds to 3600.

ModelKit

async with AgentOSSDK() as sdk:
    models = await sdk.modelkit.list_models()
    chat_models = await sdk.modelkit.list_models_by_task("chat")
    print(models[0]["capabilities"]["reasoning"])

    completion = await sdk.modelkit.chat.create(
        {
            "model": "qwen3-1.7b",
            "messages": [{"role": "user", "content": "Hello"}],
            "maxTokens": 128,
        }
    )

    async for chunk in sdk.modelkit.chat.create_stream(
        {"model": "qwen3-1.7b", "messages": [{"role": "user", "content": "Stream"}]}
    ):
        print(chunk)

Normalized model capabilities include vision, tool_call, stream, and reasoning booleans. reasoning reflects only the server-provided capabilities.reasoning value and defaults to False when omitted.

ModelKit also supports:

  • sdk.modelkit.embedding.create(...)
  • sdk.modelkit.asr.transcribe(...)
  • sdk.modelkit.tts.synthesize(...)
  • sdk.modelkit.tts.list_voices()

ToolKit

async with AgentOSSDK() as sdk:
    registration = await sdk.agentos.register_bundle(
        {
            "bundleId": "com.example.app",
            "opentoolServers": [
                {
                    "ref": "websearch",
                    "name": "opentool-server-websearch",
                }
            ],
        }
    )

    tools = await sdk.toolkit.list_tool()
    websearch_tool = next(
        (
            tool
            for tool in tools
            if tool.get("status") == "running"
            and (tool.get("server") or {}).get("name")
            == "opentool-server-websearch"
        ),
        None,
    )
    if websearch_tool is None:
        raise RuntimeError("The registered websearch tool is not running")

    tool_id = websearch_tool["id"]
    definition = await sdk.toolkit.load_tool(tool_id)
    raw_json = definition.to_json() if definition else None

    result = await sdk.toolkit.call(
        tool_id,
        {
            "id": "call-1",
            "name": "websearch",
            "arguments": {
                "query": "AgentOS",
                "max_results": 5,
            },
        },
    )
    print(result)

registration["opentoolServers"] also contains the resolved id when its status is running; the example uses list_tool() so it can select the running tool by server.name.

subscribe_tool_events() is retained for legacy direct-daemon deployments. The deprecated /toolkit/events endpoint is not exposed by the AgentOS API gateway.

stream_call() is only appropriate for functions whose tool server explicitly supports streamCall. For such a function, retain the stream and close it when consumer work ends early:

async def consume_streaming_function(sdk, streaming_tool_id):
    stream = sdk.toolkit.stream_call(
        streaming_tool_id,
        {"id": "call-2", "name": "streaming-function", "arguments": {}},
    )
    try:
        async for event in stream:
            print(event["event"], event["data"])
    finally:
        await stream.aclose()

The high-level ToolKit list, load, call, and stream-call methods use the bearer token returned by register_bundle() and refresh it once after a 401/403 response. load_tool() returns an OpenToolDefinition wrapper that preserves the raw JSON definition.

AuthKit

async with AgentOSSDK() as sdk:
    await sdk.agentos.register_bundle({"bundleId": "com.demo.app"})

    auth = await sdk.authkit.authorize({"scopes": ["profile"], "appName": "Demo"})
    print(auth["requestId"], auth["status"])

    status = await sdk.authkit.get_status(request_id=auth["requestId"])
    if status.get("authCode"):
        token_resp = await sdk.authkit.exchange_token(
            request={"authCode": status["authCode"]}
        )
        user = await sdk.authkit.get_user_info(access_token=token_resp["accessToken"])
        print(user["name"])

    await sdk.authkit.revoke({"bundleId": "com.other.app"})

DiscoveryKit

async with AgentOSSDK() as sdk:
    await sdk.agentos.register_bundle({"bundleId": "com.demo.app"})

    await sdk.discoverykit.register({"receivePolicy": "autoAccept"})

    peers = await sdk.discoverykit.get_peers()
    print(peers)

    self_info = await sdk.discoverykit.get_self()
    print(self_info["deviceName"], self_info["registered"])

    async for event in sdk.discoverykit.stream_peers():
        print(event["type"], event["device"]["name"])

    await sdk.discoverykit.unregister()

TransferKit

async with AgentOSSDK() as sdk:
    await sdk.agentos.register_bundle({"bundleId": "com.demo.app"})

    session = await sdk.transferkit.send(
        request={
            "targetDeviceId": "device-abc",
            "files": [
                {"fileName": "photo.jpg", "size": 1024, "filePath": "/tmp/photo.jpg"}
            ],
        }
    )
    print(session["transferId"], session["state"])

    transfers = await sdk.transferkit.list_transfers()

    async for progress in sdk.transferkit.stream_progress(
        transfer_id=session["transferId"]
    ):
        print(progress["progress"])

    await sdk.transferkit.set_policy(request={"policy": "autoAccept"})
    policy = await sdk.transferkit.get_policy()

    history = await sdk.transferkit.get_history()
    await sdk.transferkit.clear_history()

list_tool() is typed as list[ToolDto] and exposes daemon 0.5 metadata. Its optional server identity uses namespace, name, and version; materializedArtifactDigest identifies installed content when available, and contentState reports whether it is current.

Direct Clients

The low-level clients are available for explicit-token or custom transport usage:

from agentos_sdk.clients import AgentKitClient, CronKitClient, ModelKitClient
from agentos_sdk.transport import ApiClient

api = ApiClient(base_url="http://localhost:8888")
agentkit = AgentKitClient(api)
session = await agentkit.init_session(
    "bearer-token",
    capability={"systemPrompt": "You are a helpful assistant."},
)

Development

.venv/bin/python -m compileall agentos_sdk
.venv/bin/python -m pytest -q
.venv/bin/ruff check .

Metadata

Release files for agentos-sdk-python 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agentos-sdk-python 1.0.0
File Size Uploaded
agentos_sdk_python-1.0.0.tar.gz 36.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agentos-sdk-python 1.0.0
File Interpreter ABI Platform
agentos_sdk_python-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 64.5 kB

Release files / agentos_sdk_python-1.0.0.tar.gz

Download URL agentos_sdk_python-1.0.0.tar.gz
Size 36.9 kB
Tags Source
SHA-256 checksum
How to use checksums
80df7611fc008b54e84dfc82eeb48b313a1cc2ce2877e1365d2c345b93aad174
BLAKE2b-256 checksum
How to use checksums
6fd9e5175d418aef3c11a20e904e83cbac46935ca28098373d88da2a0450af38
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Release files / agentos_sdk_python-1.0.0-py3-none-any.whl

Download URL agentos_sdk_python-1.0.0-py3-none-any.whl
Size 27.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
71f897658ba711a3246096a2172f73b5f18c2d447c5f7ea8839c94bb589386c7
BLAKE2b-256 checksum
How to use checksums
b937e52ea1d049d26558bf212b0091fec1e4a6b108c7881c789751d06ba6dd92
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Release history Release notifications | RSS feed

1.1.0

2 release files

This release

1.0.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page