Skip to main content

Official Python SDK for AgentOS — observability, runtime, and security for AI agents.

Project description

agenthog (Python SDK for AgentOS)

Official Python SDK for AgentOS — observability, runtime, and security for AI agents. Currently at v0.1.0.

The package is named agenthog because AgentHog is the developer-facing observability surface of the AgentOS platform; the SDK ships those primitives under that name. (The platform itself is still called "AgentOS" and the GitHub repo is still agentos-python.)

Install

pip install agenthog
# auto-instrumentation extras (each is opt-in):
pip install "agenthog[openai]"
pip install "agenthog[anthropic]"
pip install "agenthog[langchain]"
pip install "agenthog[otel]"
# or everything at once:
pip install "agenthog[all]"

Requires Python 3.10 or newer.

First trace (5 lines)

import agenthog

agenthog.init(api_key="agops_…", agent_id="my-agent")

with agenthog.start_task_run() as run:
    # Your agent logic here. Auto-instrumented LLM / tool calls land
    # automatically; manual events go through `client.log_*(...)`.
    ...

That's it. Every supported integration emits the same agent.* event shape so your traces look the same regardless of which framework you end up calling.

Quickstart with auto-instrumentation

import os
import agenthog
from openai import OpenAI

agenthog.init(api_key=os.environ["AGENTOS_API_KEY"], agent_id="weather-agent")
agenthog.autoinstrument()  # patches every LLM SDK importable in this process

client = OpenAI()
with agenthog.start_task_run() as run:
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "Weather in Paris?"}],
    )
    print(resp.choices[0].message.content)

That's the entire setup. autoinstrument() detects whichever LLM SDKs you have installed (openai, anthropic, langchain) and wires up the matching agenthog.integrations.X.install() for each. It returns the list of installed integrations so you can verify what got wired up — e.g. print(agenthog.autoinstrument()) shows ['openai']. The function is idempotent and never raises; a broken integration is logged and skipped while the others still install.

Selective control

agenthog.autoinstrument(only=["openai"])              # patch openai, leave the rest
agenthog.autoinstrument(skip=["langchain"])           # patch everything except langchain

Per-integration install (power users)

The auto path is sugar on top of explicit per-integration installs. Use them directly when you want fine control or you only need one:

from agenthog.integrations import openai as openai_integration
openai_integration.install()                          # equivalent to autoinstrument(only=["openai"])

Each integration ships an install() / uninstall() pair. They are transparent: original return values pass through and original exceptions re-raise unchanged.

One uniform pattern for every provider

Most LLM providers — OpenAI, Anthropic, Mistral, Moonshot/Kimi, DeepSeek, Groq, Together, Fireworks, Perplexity, xAI, OpenRouter, local Ollama / vLLM / LM Studio — expose an OpenAI-compatible endpoint. So the same five lines above work for every one of them; only base_url, api_key, and model change:

client = OpenAI(
    base_url=os.environ["LLM_BASE_URL"],              # e.g. https://api.anthropic.com/v1/
    api_key=os.environ["LLM_API_KEY"],
)
resp = client.chat.completions.create(
    model=os.environ["LLM_MODEL"],                    # e.g. claude-sonnet-4-6
    messages=[{"role": "user", "content": "..."}],
)

The SDK inspects client.base_url and tags each trace with the correct upstream provider via gen_ai.system — so a call routed through OpenRouter shows up as openrouter in your dashboard, not openai.

Configuration

The SDK reads environment variables first; explicit init(...) args override. Full set per docs/spec/05-sdk-contract.md:

Field Env var Default
api_key AGENTOS_API_KEY (required)
endpoint AGENTOS_ENDPOINT https://api.theagentos.space
environment AGENTOS_ENV unset
agent_id AGENTOS_AGENT_ID unset (must be set here or at start_task_run)
workspace_id AGENTOS_WORKSPACE_ID resolved server-side from API key
capture_content True (set False to strip input / output / tool.input / tool.output)
flush_interval_ms 250
flush_batch_size 100
buffer_max_size 10000 (drop oldest on overflow)
disable AGENTOS_DISABLE False (1 / true ⇒ fast no-op)
kind "sync" (or "async" for async-native hosts)

disable=True makes every public method a fast return — useful in unit tests that don't want a mock endpoint.

Integrations

Provider Module Patches
OpenAI agenthog.integrations.openai chat.completions.create (sync + async, streaming + non-streaming, tool calls)
Anthropic agenthog.integrations.anthropic messages.create (with stream=True) and messages.stream
LangChain agenthog.integrations.langchain BaseCallbackHandler registered globally; one event per LLM call regardless of token count
OpenTelemetry passthrough agenthog.integrations.opentelemetry TEE: translates OTel spans to agent.* events; optional forward_to= keeps your collector

All integrations honor capture_content=False and emit exactly one event per logical operation — streaming aggregations close into a single agent.llm_call event with the full output and final usage.

Manual logging

Use client.log_* for anything not auto-instrumented:

client = agenthog.get_default_client()
assert client is not None

client.log_eval("faithfulness", score=0.92, evaluator="llm")
client.log_business_event("checkout_completed", revenue=29.0, currency="USD")
client.log_security_alert("prompt_injection", severity="high", action_taken="blocked")
client.log_handoff("researcher_agent", reason="needs deep search")
client.log_retrieval("vector_kb", query="...", top_k=5, results_count=3)

All inherit the active identity context. All validate locally; failed validation drops the event with a WARN — your code never raises.

Tracing custom tools

autoinstrument() captures LLM calls, but the tools of a hand-written agent are ordinary Python functions — invisible unless you mark them. Decorate one at a time with @agenthog.tool:

@agenthog.tool
def get_weather(city: str) -> dict: ...

@agenthog.tool(name="geocode")
def _geocode(city: str) -> tuple[float, float]: ...

Or trace a whole tools module in one call — every public function defined in it becomes a tracked tool, and the return value is the list of names wrapped, so you can assert nothing was missed:

import agenthog, weather

agenthog.init()
agenthog.instrument_module(weather)   # ['get_weather', 'get_air_quality', 'geocode']

instrument_module skips private helpers (leading _), imported names, classes, and constants. Override with include=[...] (explicit allow-list) or exclude=[...]. It reuses @tool, so functions you already decorated by hand are detected and never double-wrapped. Both emit an agent.tool_call step nested under the active task_run; nested LLM/tool calls attach beneath, producing the full execution tree.

Flags

variant = client.flag("use_gpt4_for_support", default=False, user_id="user_42")

The SDK polls GET /v1/flags every 60 s and evaluates locally (priority-ascending, first-match-wins; percentage rollouts use a stable hash so the same user stays in or out across calls). Each flag() call also emits an agent.flag_check event so you can correlate flag state with downstream behavior.

Routing

decision = client.route(goal="answer_well", context={"topic": "support"})
# decision -> {"arm_id": "...", "model": "...", "decision_span_id": "..."}

# Later, after the model answers, close the bandit's loop:
client.route_outcome(decision["decision_span_id"], reward=1.0, success=True, feedback_kind="auto")

Inside an AgentRun container (AGENTOS_RUNTIME=1), route and route_outcome go through the syscall mediator sidecar over a Unix socket — same SDK code path, no env var to flip.

Privacy controls

  • capture_content=False strips input, output, tool.input, tool.output, retrieval.query, retrieval.documents from every emitted event. Token counts, model names, durations, costs, and identity fields still flow.
  • Redactors run before send: client.add_redactor(lambda e: ...). Redactors must be deterministic and fast; if one raises, it's skipped (with a WARN) and the event still ships.
  • The SDK detects email / E.164 phone patterns in user_id and emits a one-time WARN per id so you can pseudonymize before they hit the wire.

SDK logs

Internal SDK logs live under the agenthog.sdk namespace, default level WARNING. Silence or amplify via standard Python logging:

import logging
logging.getLogger("agenthog.sdk").setLevel(logging.DEBUG)

Sub-loggers per submodule: agenthog.sdk.transport, agenthog.sdk.context, agenthog.sdk.runtime, agenthog.sdk.integrations.<name>, agenthog.sdk.flags.

Failure-mode contract

Per docs/spec/05-sdk-contract.md "Failure modes": the SDK never raises out of public methods. A misconfigured endpoint, a network outage, a malformed event — every error flows to the internal logger and the corresponding metric counter, never to your application.

Counters live on client.metrics:

  • events_sent — successfully ingested
  • events_dropped — buffer overflow
  • events_validation_failed — schema rejection (local)
  • events_retried — retried at least once before success or permanent-fail
  • events_failed_permanent — gave up after max_retries

Hook into them via client.on_metric(lambda name, delta: ...).

Versioning (SemVer)

  • Patch (0.1.00.1.1): new event types, new optional fields, bug fixes.
  • Minor (0.1.00.2.0) while still in 0.x: any breaking change to public methods.
  • Major (0.x1.0.0): graduation to stable.
  • Once on 1.x, breaking changes bump major.

The __version__ constant in agenthog/__init__.py and the version field in pyproject.toml are kept in sync; the publish workflow refuses to ship if the tag and __version__ disagree.

Local development

uv sync
uv run pytest -q
uv run mypy agenthog/
uv run ruff check . && uv run ruff format .

Integration tests

The end-to-end test in tests/integration/test_e2e.py runs against a live AgentOS platform. It is skipped automatically unless both env vars are set:

export AGENTOS_TEST_ENDPOINT=https://api.theagentos.space
export AGENTOS_TEST_API_KEY=agops_…
# Optional override:
export AGENTOS_TEST_AGENT_ID=e2e-test-agent

uv run pytest tests/integration/test_e2e.py -v

The test mocks the OpenAI client (no real API costs) but every event flows through the actual transport, hits the actual ingestion API, and is read back via GET /v1/traces/:trace_id.

Publishing

This repo publishes via Trusted Publishing (OpenID Connect) — no API tokens stored anywhere. Two workflows:

One-time setup (per index — TestPyPI and PyPI are independent):

  1. Register the project on the index (one manual upload is enough to claim the agenthog name on each).
  2. On the project's "Publishing" settings page, add a Trusted Publisher with:
    • Owner: your GitHub user / org.
    • Repository: agentos-python.
    • Workflow: publish.yml (PyPI) or publish-test.yml (TestPyPI).
    • Environment: pypi or testpypi respectively.
  3. In this repo's GitHub settings → Environments, create the matching environment(s). No required reviewers — the OIDC handshake is the gate.

Recommended release flow:

# 1. Dry-run on TestPyPI: Actions → publish-test → Run workflow → main
pip install -i https://test.pypi.org/simple/ \
  --extra-index-url https://pypi.org/simple/ \
  agenthog==0.1.0
# … smoke-test in a fresh venv …

# 2. Tag for production
git tag v0.1.0
git push origin v0.1.0

The publish workflow builds, verifies the tag matches __version__, and publishes to PyPI.

main branch protection requires the test workflow to pass before merge.

Spec

The four spec files this SDK honors live under docs/spec/:

When spec and code disagree, fix the spec first. Same rule as the platform monorepo.

License

Apache License 2.0. The published agenthog package on PyPI ships under the same terms — including the explicit patent grant and the standard "AS IS" disclaimer in section 7 of the license. The package contains no code from the AgentOS platform monorepo; only the SDK client surface defined in docs/spec/05-sdk-contract.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agenthog-0.9.1.tar.gz (124.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agenthog-0.9.1-py3-none-any.whl (159.1 kB view details)

Uploaded Python 3

File details

Details for the file agenthog-0.9.1.tar.gz.

File metadata

  • Download URL: agenthog-0.9.1.tar.gz
  • Upload date:
  • Size: 124.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for agenthog-0.9.1.tar.gz
Algorithm Hash digest
SHA256 31ce5adae6d562dd365ab6eefef7167c98e7a7225d0bb864f19ecc615350d00e
MD5 9c4308a3922e3b809e3ac8c2b3eafae7
BLAKE2b-256 e1c3bcaa9e60d2d1feec09683f8151639ce3ea13108bfa47d0f00ed2bc17f7ce

See more details on using hashes here.

File details

Details for the file agenthog-0.9.1-py3-none-any.whl.

File metadata

  • Download URL: agenthog-0.9.1-py3-none-any.whl
  • Upload date:
  • Size: 159.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for agenthog-0.9.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d829e327af254d1e4460cf739fd0a3cee933f1e6e98b2492b79e73fda525a87b
MD5 3e65148a2efde791de09325acc1aa83f
BLAKE2b-256 dd2b77fc432f21f85ff3f09bc68844dfd5e1ac43f4cf21cfeefc8ced612cd261

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page