Skip to main content

Staso Python SDK

Trace explicitly instrumented agent runs and evaluate proposed tool calls before your code executes them.

Supported launch configuration

  • Python 3.11+
  • OpenAI Chat Completions, Anthropic Messages, or direct LiteLLM completion calls on the tested OpenAI route
  • Synchronous or asynchronous, non-streaming calls
  • An explicit @st.agent root and @st.tool on executed Python tools
  • Claude Code and Codex through one-command CLI setup
  • Observe plus configured Guard policies in Audit or Enforce mode

Copy-paste launch sample

Use the canonical launch quickstart at https://staso.ai/docs/quickstart/launch. If you have a repository checkout, you can run the pinned copy in examples/launch_quickstart/:

pip install -r examples/launch_quickstart/requirements.txt

requirements.txt pins:

staso[openai]==0.5.15
openai==3.6.0

Set STASO_API_KEY, OPENAI_API_KEY, STASO_AGENT_NAME, STASO_ENVIRONMENT, STASO_WORKSPACE_SLUG, and STASO_SESSION_ID=launch-quickstart-first-trace, then run:

python examples/launch_quickstart/openai_guard_quickstart.py

The sample checks SDK delivery. Onboarding should wait for the dashboard/backend trace query before marking the first trace visible.

OpenAI quickstart

pip install "staso[openai]"
import json

import staso as st
from openai import OpenAI

st.init(api_key="ak_...", agent_name="weather-agent")
client = OpenAI()

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the weather for a city.",
            "parameters": {
                "type": "object",
                "properties": {"city": {"type": "string"}},
                "required": ["city"],
            },
        },
    }
]


@st.tool
def get_weather(city: str) -> dict:
    return {"city": city, "temperature_c": 18}


@st.agent
def answer(question: str) -> dict | str:
    try:
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": question}],
            tools=TOOLS,
        )
    except st.GuardBlocked as blocked:
        return f"Blocked: {blocked.reason}"

    tool_call = response.choices[0].message.tool_calls[0]
    arguments = json.loads(tool_call.function.arguments)
    return get_weather(**arguments)


print(answer("What is the weather in London?"))
delivery = st.shutdown(timeout=5)
if not delivery.ok:
    raise RuntimeError(f"Trace delivery incomplete: {delivery.last_failure}")

st.init() patches an installed OpenAI SDK. The explicit decorators define the supported trace shape: @st.agent creates the root and @st.tool records the real tool duration, status, input, output, and error.

Guard evaluates tool proposals returned by the patched provider before the normal dispatch loop receives them. An audit finding records a would-block and lets the proposal continue. A block raises st.GuardBlocked.

@st.tool observes a Python function. It does not run Guard by itself. Check a direct tool call explicitly when it needs enforcement:

decision = st.guard("delete_record", {"record_id": "r_42"})
if decision.action == "block":
    raise RuntimeError(decision.reason)
delete_record("r_42")

Anthropic

Install staso[anthropic] and use a non-streaming messages.create call under the same explicit decorators. See the Anthropic quickstart.

CLI agents

Connect Claude Code or Codex without changing application code:

pip install staso
staso setup --target claude-code  # or codex

See the Claude Code quickstart, Codex quickstart, or CLI reference.

Pricing

Personal supports custom Python rules and your own LLM judge in Guard audit mode, with one Personal account per user. Saved-rule limits and execution allowances are separate.

Team capacity is purchased in units. Traces, Python rule runs, and LLM-judge evaluations each have an independent allowance shared across the organization's workspaces.

See current pricing and allowances for prices, included usage, retention, and saved-rule limits. Your signed-in quote shows the final amount, currency, and capacity. See how usage is counted before testing or running rules.

Allowances are shared across organization workspaces. See how usage is counted for Python-rule tests, billing-period resets, and capacity changes.

Capture boundaries

  • requested_model records the request. model records the provider-resolved ID when the response supplies one, and otherwise falls back to the request. provider records the provider identity.
  • usage_available=True means the provider returned usage, including valid zero-token values. False means the compatibility token counters are not evidence of known zero.
  • Cost is estimated by the Staso service for recognized models. Unknown usage or an unrecognized model stays unknown instead of becoming $0.
  • Five independent categories default on: request, system_prompt, tool_schema, tool_arguments, and output. Configure them with st.init(capture_<category>=...) or STASO_CAPTURE_<CATEGORY>.
  • Suppression removes governed values before SDK payload serialization. Enabled content can still be redacted by the backend before storage; these are different states.
  • The deprecated capture_messages=False fallback suppresses request and system_prompt only. Explicit category controls win.
  • Span delivery is asynchronous. Call st.flush() to wait for events accepted before that call, or st.shutdown() when the process is done. Both return a DeliveryReport; check report.ok when delivery is required. report.rejected and report.last_rejection expose local queue drops that happened during automatic decorator cleanup.
  • Guard fails open when no policy decision is available by default. Use fail_closed=True or STASO_GUARD_FAIL_CLOSED=true for calls that must block on explicit degradation.

For a warm serverless worker, flush after each invocation without stopping the shared client:

def handler(event, context):
    result = answer(event["question"])
    report = st.flush(timeout=2)
    if not report.ok:
        logger.warning("Staso delivery incomplete: %s", report.last_failure)
    return result

Calling st.init() again retires the previous client. Inspect new_client.previous_shutdown_report to verify that drain without making initialization throw by default.

Documentation

Python 3.11+ | Apache 2.0

Metadata

Release files for staso 0.5.22

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

Source distribution (sdist)

Source distribution for staso 0.5.22
File Size Uploaded Provenance
staso-0.5.22.tar.gz 138.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for staso 0.5.22
File Interpreter ABI Platform
staso-0.5.22-py3-none-any.whl Python 3 none any Details

Total release size: 312.5 kB

Release files / staso-0.5.22.tar.gz

Download URL staso-0.5.22.tar.gz
Size 138.1 kB
Tags Source
SHA-256 checksum
How to use checksums
0f72f941118c9e9e2c0a13689ce444cd6900abcbfcc1c2e2b5d7d798f1314fa6
BLAKE2b-256 checksum
How to use checksums
06a7287171946bd0b2ecc52f00ccbcd67f2622d925b421d08e2768ffe2ffc712
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / staso-0.5.22-py3-none-any.whl

Download URL staso-0.5.22-py3-none-any.whl
Size 174.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
28e30ac32624488af613296832877dde815a82cbef0074c34c67af211006e91a
BLAKE2b-256 checksum
How to use checksums
ff7e47317a179d622904cefdd7ff38754638944641598b17f4f9a84453028fb2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Security for staso 0.5.22

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for staso-0.5.22.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.5.22 This release

2 release files Full provenance

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