Docs / Python SDK

Python SDK reference.

Published to PyPI as mesedi. Source at mesedi-ai/mesedi/sdk-python.

Importing mesedi never requires any framework (LangChain, LangGraph, OpenAI Agents, CrewAI, Anthropic) to be installed. Framework integrations live behind optional extras and are tree-shaken out of the base install.

Install

pip install mesedi

configure(api_key=...)

Sets up the module-level client. Call once at process start. For local backend development against localhost:8080, pass an explicit base_url=. Otherwise the SDK posts to the Mesedi production backend.

import os
import mesedi

mesedi.configure(api_key=os.environ["MESEDI_API_KEY"])

# Or, for local dev:
# mesedi.configure(api_key="mesedi_sk_dev_local_only",
#                  base_url="http://localhost:8080")

@mesedi.wrap

Decorate any function as an agent execution. The SDK records start, completion (or crash), wall-clock duration, and a stable crash signature so identical exceptions cluster into a single failure group instead of paging you for every retry.

@mesedi.wrap
def run_my_agent(query: str) -> str:
# your agent logic
return "answer"

run_my_agent("hello")

For each @wrap-decorated call:

  • On entry: POST /executions with status="started".
  • On normal return: PATCH /executions/{id} with status="completed" and duration_ms.
  • On exception: PATCH with status="crashed" and a crash_signature (SHA-256-derived stable hash of exception type plus top of traceback). The original exception is then re-raised; @wrap does not swallow.

Network failures during observation never block the wrapped function. The SDK is fail-open: a Mesedi outage degrades to invisibility, not to broken production code.

@mesedi.tool

Decorate any function as an observed tool call. Emits a tool_call event into the surrounding execution context, with start time, end time, success or failure, and the exception class on failure. The tool-failures detector consumes these events to flag repeated failures of the same tool inside one execution.

@mesedi.tool
def fetch_user(user_id: str) -> dict:
return db.query(...)

@mesedi.wrap
def run_my_agent(query: str) -> str:
user = fetch_user("u_123")   # emits tool_call event
return summarize(user)

Hard-halt (local budgets + SSE remote channel)

Optional. Cap a single execution across four axes: input tokens, output tokens, wall-clock seconds, and step count (tool calls + LLM calls + explicit checkpoint()s). Pass any subset — unset fields impose no limit on that axis. When any budget is exceeded, the SDK raises mesedi.MesediHalt at the next safe boundary — between LLM calls, tool calls, or checkpoint()s, never mid-call — so your try/finally cleanup runs and open resources release. MesediHalt inherits BaseException, not Exception, so broad except Exception handlers do not swallow it.

from mesedi import wrap, Budget

@wrap(budget=Budget(
    max_wall_clock_seconds=600,   # 10 min real time
    max_steps=30,                  # 30 tool/LLM/checkpoint boundaries
    max_tokens_in=200_000,
    max_tokens_out=50_000,
))
def run_my_agent(query: str) -> str:
    ...

When a budget is supplied, the SDK also opens an SSE subscription to GET /executions/{id}/halt-stream. When an operator clicks Halt on the dashboard, the reader thread signals the BudgetTracker and the next safe-boundary check raises MesediHalt(trigger="remote_signal"). Mesedi never decides to halt on its own — operator intent (or local budget exhaustion) is always the trigger.

Fail-open by design. If the SSE subscription fails — Mesedi backend unreachable, 4xx/5xx response, network partition — the reader logs at DEBUG and returns. The wrapped agent keeps running with local budgets still enforced client-side. A Mesedi outage cannot break your production agent.

Anthropic auto-instrumentation

If your agent calls the Anthropic Python SDK directly, Mesedi can patch the client at import time so every messages.create() emits an llm_call event without any other code change. Cost-velocity and drift detectors light up the moment Anthropic calls start flowing.

import mesedi
mesedi.instrument_anthropic()

import anthropic
client = anthropic.Anthropic()

@mesedi.wrap
def run_my_agent(prompt: str) -> str:
response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": prompt}],
)
return response.content[0].text

Ollama auto-instrumentation (local runtime)

If your agent calls Ollama for local-runtime inference, mesedi.instrument_ollama() patches the native ollama client so every Client.chat() emits an llm_call event with provider="ollama". Both sync (ollama.Client) and async (ollama.AsyncClient) classes are patched. No real Ollama server is needed for Mesedi to instrument the client — patching happens at import time.

import mesedi
mesedi.instrument_ollama()

from ollama import Client
client = Client(host="http://localhost:11434")

@mesedi.wrap
def run_my_agent(prompt: str) -> str:
response = client.chat(
    model="llama3.1:8b",
    messages=[{"role": "user", "content": prompt}],
)
return response["message"]["content"]

Local runtime means a few detectors are architecturally inapplicable — provider_incident, infrastructure_throttled, and cost_velocity have no upstream signal to monitor. The dashboard renders a Detectors not applicable chip row explaining this for Ollama-only projects. Every other detector (data_leakage, prompt_injection, context_overflow, semantic_loop, etc.) runs on Ollama payloads identically to commercial-provider payloads.

Cost model: Ollama models price at $0/token in the canonical pricing table — local-runtime is the honest answer. For customers wanting GPU/electricity amortization or running fine-tuned variants, set per-project rates via the Custom Model Pricing card on the Settings page. Per-project overrides win over the canonical table for exact-name matches.

Context windows: The major Ollama families (llama3+, qwen2+, deepseek-r1/v3/coder, gemma2+, phi3+, codellama) ship with conservative upper-end-of-family defaults. Customers running smaller variants override per-project via the Context Overflow → custom_model_windows knob.

Streaming: Calls with stream=True currently emit no llm_call event and log a one-time INFO message explaining the omission. The chunk-aggregating wrapper that observes streaming Ollama calls ships in a follow-up sub-wave. Non- streaming calls are unaffected.

LangChain adapter

Install the optional extra:

pip install mesedi[langchain]

Pass the callback handler to any LangChain invocation:

import mesedi
from mesedi.integrations.langchain import MesediCallbackHandler

@mesedi.wrap
def run_agent(question: str) -> str:
chain = build_chain()
result = chain.invoke(
    {"input": question},
    config={"callbacks": [MesediCallbackHandler()]},
)
return result["output"]

The handler subscribes to LangChain's on_llm_start / on_llm_end / on_tool_start / on_tool_end hooks and emits llm_call and tool_call events with the same wire format as a hand-written @mesedi.tool pair. Detectors see no difference between the two paths.

CrewAI adapter

Install the optional extra:

pip install mesedi[crewai]

One line attaches Mesedi to a Crew:

import mesedi
from mesedi.integrations.crewai import instrument_crew

@mesedi.wrap
def run_my_crew(question: str) -> str:
crew = build_crew()
instrument_crew(crew)
return str(crew.kickoff(inputs={"question": question}))

instrument_crewis idempotent and does three things: attaches the LangChain callback handler to each agent's LLM (CrewAI uses LangChain under the hood), sets a step callback to emit crewai.agent_action / crewai.agent_finish checkpoint events per agent step, and sets a task callback to emit crewai.task_completedper finished task. The dashboard timeline ends up showing LLM and tool detail interleaved with CrewAI's higher-level reasoning rhythm.

Lower-level building blocks

The decorators are sugar over a lower-level client API. You rarely need it, but it's there:

  • mesedi.MesediClient: the HTTP client under configure().
  • mesedi.Event, mesedi.EventType: build event payloads directly.
  • mesedi.Execution, mesedi.Status: build execution payloads directly.
  • mesedi.flush(timeout=5.0): block until the background shipper drains. Useful in test suites and short-lived scripts where the process exits before the async shipper has shipped.

What's next?

TypeScript SDK reference covers the matching surface for Node and the Vercel AI SDK adapter.

HTTP API reference covers what the SDK posts on the wire, so you can instrument from any language.

Failure classes and playbooks explains what each detector is looking for in the telemetry the SDK ships.