atmon docs

SDKS/PYTHON.MD

Python

The Python client: install, authenticate, make the first call, and reach the four verbs from your own code. It is the counterpart of the TypeScript client and behaves the same way.

Install

The distribution is not published yet. Through the beta it runs from source, out of sdk/python in the repository:

cd sdk/python
uv sync
uv run python generate.py

generate.py writes the typed message code from the contracts. It needs no network. Run it again after pulling a change to the contracts.

When the distribution is published, this section becomes one install line and nothing else on this page changes.

Authenticate

import os

from rudrite_automaton import AutomatonClient

atmon = AutomatonClient("https://api.atmon.ai", os.environ["ATMON_API_KEY"])

The key rides on every request on every call. It resolves to one project, and that project scopes everything you can read or write.

Construction raises ValueError on an empty key or on an address that is not an http or https URL, so a missing environment variable is a startup failure rather than a mystery on the first call. A key the server rejects comes back as AutomatonError with the code unauthenticated.

The client owns its HTTP connection unless you pass your own. Close it with atmon.close(), or use it as a context manager.

If you run atmon on your own machines, the first argument is your own node's address instead.

The first call

Find the tool by describing the task:

resolution = atmon.search_tools(
    "file a bug about the failing build",
    entity_id="user-42",
    limit=5,
)

entity_id names the person you are acting for. What comes back is scoped to what that person has connected. A limit of 0 leaves the size of the list to the server. context_messages takes recent turns of a conversation, most recent last, when the intent alone is thin.

Read the tool, when the compact schema in the match is not enough:

tool = atmon.describe_tool(resolution.matches[0].tool_slug)

Then act:

call = atmon.call_tool(
    "github.create_issue",
    {"owner": "rudrite", "repo": "automaton", "title": "build is red"},
    entity_id="user-42",
    resolution_id=resolution.resolution_id,
    idempotency_key="issue-build-red-1",
)

args is a plain mapping; the helper encodes it. entity_id is required. idempotency_key makes a retry return the original call rather than acting twice. confirm=True clears the gate on a destructive tool and means the person was told what would change.

Read the answer

What comes back is the receipt, not a bare result. Check the status before you use it:

if call.status == ToolCallStatus.SUCCEEDED:
    result = json.loads(call.result_json)
else:
    # error_code is one of the stable codes; error_detail says more
    ...

A destructive call with no confirmation comes back refused, with the reason in the detail, and nothing reached the app: the gate runs before any credential is unlocked.

Close the loop when you can. Reporting whether the tool you chose was the right one is what improves the next ranking:

atmon.router.report_outcome(resolution_id=resolution.resolution_id)

Each resolution is reportable once.

Hand over a whole job

More than about twenty rows, or work that outlives the request you are serving, belongs in a job rather than a loop:

job = atmon.jobs.submit_job(program=program, idempotency_key="quarterly-invites")
state = atmon.jobs.get_job(id=job.id)

See Jobs for what a plan is made of and how to dry-run one first.

Everything else

Every call in the API reference is on the same object: atmon.catalog, atmon.connections, atmon.router, atmon.execution, atmon.jobs, atmon.traces, atmon.triggers, atmon.usage, and atmon.keys. Method names are snake_case and fields follow the contract.

flow = atmon.connections.initiate_connection(entity_id="user-42", toolkit_slug="github")