SDK Reference
Python SDK Reference
Method signatures, parameter schemas, return types, runnable code examples, and framework integrations for the Blocklog Python client.
Method
blocklog.init()
Initialize the global SDK configuration. Call once at application startup.
def init(
api_key: str | None = None,
*,
base_url: str | None = None,
signing_key: str | None = None,
timeout: float | None = None,
max_retries: int | None = None,
debug: bool = False,
) -> BlocklogClientParameters
api_key· str, optionalKey for verification. Falls back to BLOCKLOG_API_KEY.
base_url· str, optionalCustom API endpoint override. Falls back to BLOCKLOG_BASE_URL.
signing_key· str, optionalHMAC-SHA256 key for tamper-evident log signatures. Falls back to BLOCKLOG_SDK_SIGNING_KEY. This is not asymmetric Ed25519 signing.
timeout· float, optionalRequest timeout in seconds. Default is 10.
max_retries· int, optionalIngestion retries. Default is 3.
Example
import blocklog
blocklog.init(api_key="blk_live_xxxx", debug=True)Decorator
@blocklog.agent
Trace an AI agent function, linking it to a Blocklog session. Handles sync and async functions automatically.
def agent(
func: F | None = None,
*,
name: str | None = None,
version: str = "1.0",
tags: list[str] | None = None,
metadata: dict[str, Any] | None = None,
) -> F | Callable[[F], F]Parameters
name· str, optionalHuman-readable agent name. Defaults to func.__name__.
version· strSemver-style version string. Default is "1.0".
tags· list[str], optionalOptional list of string tags.
metadata· dict, optionalArbitrary extra data attached to the agent session.
Example
@blocklog.agent(name="stock-trader", version="2.0", tags=["prod"])
def run_trading(ticker):
passDecorator
@blocklog.tool
Wrap a helper function or external API call to record it as a TOOL_CALL event. Inherits trace context from the surrounding @agent.
def tool(
func: F | None = None,
*,
name: str | None = None,
schema: dict[str, Any] | None = None,
tags: list[str] | None = None,
metadata: dict[str, Any] | None = None,
) -> F | Callable[[F], F]Parameters
name· str, optionalHuman-readable tool name. Defaults to func.__name__.
schema· dict, optionalOptional dict describing the input schema.
tags· list[str], optionalOptional string tags.
metadata· dict, optionalArbitrary extra data.
Example
@blocklog.tool(name="fetch-price", tags=["market-data"])
def get_price(ticker):
return 189.30Context Manager
blocklog.decision()
Define a block for recording structured inputs, outputs, and review tasks associated with a system choice.
def decision(
*,
type: str,
asset: str | None = None,
confidence: float | None = None,
metadata: dict[str, Any] | None = None,
agent_id: str | None = None,
trace_id: str | None = None,
) -> Generator[DecisionContext, None, None]Parameters
type· strDecision type identifier (e.g. "BUY").
asset· str, optionalAsset this decision is about.
confidence· float, optionalModel confidence score, from 0 to 1.
metadata· dict, optionalExtra structured fields.
agent_id· str, optionalLinks this decision to a specific agent session.
trace_id· str, optionalLinks this decision to a specific trace.
Returns: DecisionContext
record_input(**kwargs)Record structured inputs. Returns self.
record_output(**kwargs)Record structured outputs. Returns self.
tag(*tags)Attach string labels. Returns self.
request_approval(reason, reviewer=None)Non-blocking request for human approval. Returns self.
verify()Verify the decision after the with block has exited. Raises RuntimeError if called before the decision is committed.
Example
with blocklog.decision(type="BUY", asset="AAPL", confidence=0.9) as d:
d.record_input(price=189.30)
d.tag("high-conviction")
d.record_output(order_id="ord_99")Governance Method
blocklog.approval.request()
Request human review for an action. Triggers webhooks asynchronously.
def request(
decision_id: str | None = None,
*,
reason: str,
reviewer: str | None = None,
log_id: str | None = None,
metadata: dict[str, Any] | None = None,
) -> dict[str, Any]Parameters
decision_id· str, optionalID of the decision this approval request relates to.
reason· strWhy human review is needed.
reviewer· str, optionalEmail or identifier of the assigned reviewer.
log_id· str, optionalID of a specific log entry this request relates to, if not tied to a decision.
metadata· dict, optionalArbitrary extra data.
Example
blocklog.approval.request(
decision_id="dec_123",
reason="Price exceeds limits",
reviewer="cro@company.com"
)- —Related: blocklog.approval.reject(), escalate(), list_overrides(), and audit_trail() round out the human-in-the-loop workflow.
Forensic Method
blocklog.replay()
Instantiate a session to examine execution traces, staleness heatmaps, and counterfactual outcomes.
def replay(
trace_id: str,
*,
token_id: str | None = None,
metadata: dict[str, Any] | None = None,
) -> ReplaySessionParameters
trace_id· strTrace ID to replay.
token_id· str, optionalRestrict the replay to a specific token within the trace.
metadata· dict, optionalExtra context attached to the replay session.
Example
session = blocklog.replay("trace-uuid")
cause = session.root_cause()
print(cause["description"])Verification Method
blocklog.verify.log()
Confirm a single log entry has not been tampered with since creation.
def log(log_id: str) -> dict[str, Any]Parameters
log_id· strID of the log entry to verify.
Example
result = blocklog.verify.log("log-uuid")
assert result["status"] == "verified"Framework instrumentation
Integrations
Built-in instrumentation for LangChain, LangGraph, the OpenAI SDK, and LiteLLM. Each integration wires into the framework’s native execution model and emits structured events to the Blocklog ingest API — covering LLM calls, tool use, chain and graph lifecycle, streaming, and errors.
Common behaviour across all integrations
- —Concurrent run safety — LangChain/LangGraph key state by run_id, LiteLLM by litellm_call_id, and OpenAI by a ContextVar, so nested or parallel runs never clobber each other.
- —Context inheritance — trace_id, session_id, workflow_id, and agent_id are pulled automatically from the active Blocklog context (blocklog.context.vars); no manual threading required.
- —Safe serialization — Pydantic v1 (.dict()) and v2 (.model_dump()) models, plain Python types, and arbitrary objects are all handled; unknown types fall back to str().
- —Error events always fire — on any exception, an errored event is emitted before it propagates, so failures are never silently dropped from your audit trail.
Integration
LangChain
Attach a callback handler to any chain, agent, or LLM to capture chain, model, and tool lifecycle events.
Setup
import blocklog
client = blocklog.init(api_key="blk_...")
handler = client.instrument_langchain()Usage
chain.invoke(inputs, config={"callbacks": [handler]})Events emitted
| Event | Causality type | Fired when |
|---|---|---|
agent.chain.started | chain_start | A chain begins; includes inputs and input_keys. |
agent.chain.completed | chain_end | A chain finishes successfully; includes outputs. |
agent.chain.errored | chain_error | A chain raises an exception. |
agent.model.started | llm_start | An LLM call begins; includes serialized model info and prompts. |
agent.model.completed | llm_end | An LLM call finishes; includes the full response. |
agent.model.errored | llm_error | An LLM call raises an exception. |
agent.tool.started | tool_start | A tool begins execution; includes the raw input string. |
agent.tool.completed | tool_end | A tool finishes; includes output. |
agent.tool.errored | tool_error | A tool raises an exception. |
- —Every event carries a span_id (derived from run_id), a parent_run_id for causality linking, and an agent_metadata block with framework, captured_at, and any LangChain-provided metadata.
Integration
LangGraph
Extends the LangChain handler pattern with graph-native event families. Because LangGraph routes both top-level graphs and individual nodes through on_chain_*, the handler inspects the serialized payload to tell them apart and emit the right event.
Setup
import blocklog
client = blocklog.init(api_key="blk_...")
handler = client.instrument_langgraph()Usage
result = graph.invoke(state, config={"callbacks": [handler]})
# Or via RunnableConfig
from langchain_core.runnables import RunnableConfig
result = graph.invoke(state, config=RunnableConfig(callbacks=[handler]))Events emitted
Graph lifecycle
| Event | Causality type | Fired when |
|---|---|---|
agent.graph.started | graph_start | The top-level CompiledGraph begins; includes graph_name, inputs, and input_keys. |
agent.graph.completed | graph_end | The graph finishes successfully; includes outputs. |
agent.graph.errored | graph_error | The graph raises an unhandled exception. |
Node lifecycle
| Event | Causality type | Fired when |
|---|---|---|
agent.graph.node.started | node_start | A node begins; includes node_name and inputs. |
agent.graph.node.completed | node_end | A node finishes; includes node_name and outputs. |
agent.graph.node.errored | node_error | A node raises an exception; includes node_name and error details. |
Subgraph lifecycle
| Event | Causality type | Fired when |
|---|---|---|
agent.graph.subgraph.started | subgraph_start | A nested subgraph begins; includes subgraph_name and inputs. |
agent.graph.subgraph.completed | subgraph_end | A nested subgraph finishes; includes outputs. |
Checkpoint lifecycle
| Event | Causality type | Fired when |
|---|---|---|
agent.graph.checkpoint.started | checkpoint_start | LangGraph is about to write a checkpoint; includes thread_id and checkpoint_ns. |
agent.graph.checkpoint.completed | checkpoint_end | A checkpoint is successfully persisted; includes checkpoint_id, thread_id, and checkpoint_ns. |
- —All agent.model.* and agent.tool.* events are also emitted for calls made inside nodes, identical to the LangChain integration.
- —A _node_name_stack keyed by run_id ensures node names are correctly attributed on completion and error callbacks, which LangGraph does not re-supply.
- —State dicts — which often contain Pydantic models — are safely serialized before emission.
Integration
OpenAI SDK
The OpenAI Python SDK has no callback system, so instrumentation works by monkey-patching the create method on the SDK's resource classes directly. Every OpenAI(), AsyncOpenAI(), and AzureOpenAI() instance in the process — including ones already constructed — is instrumented automatically.
Setup
import blocklog
client = blocklog.init(api_key="blk_...")
client.instrument_openai_agents()Instrument a single instance instead
import openai
from blocklog.integrations.openai_agents import instrument_openai
blocklog_client = blocklog.init(api_key="blk_...")
openai_client = openai.OpenAI(api_key="...")
instrument_openai(blocklog_client, openai_client=openai_client)Events emitted
| Event | Causality type | Fired when |
|---|---|---|
agent.model.started | llm_start | A chat.completions.create or responses.create call begins; includes model, messages, and remaining request parameters. |
agent.model.completed | llm_end | The call finishes; includes the full response, usage, duration_s, and a streamed flag. |
agent.model.errored | llm_error | The call raises an exception. |
- —Streaming is handled transparently for sync and async clients — chunks are re-yielded unmodified, and agent.model.completed fires only once the stream is fully consumed, with deltas merged into one coherent response.
- —Async create methods are wrapped with an async-native path, including async streaming via async for; sync vs. async is detected automatically.
- —A contextvars.ContextVar (blocklog_openai_span_id) propagates span_id across nested or concurrent calls within the same async context.
- —A _blocklog_wrapped sentinel flag makes calling instrument_openai_agents() more than once safe.
Integration
LiteLLM
LiteLLM has a first-class CustomLogger base class. The handler subclasses it and registers via litellm.callbacks — the same pattern every other LiteLLM observability tool uses, with no monkey-patching required.
Setup
import blocklog
client = blocklog.init(api_key="blk_...")
handler = client.instrument_litellm()Usage
import litellm
# Replace all existing callbacks
litellm.callbacks = [handler]
# Or append alongside existing callbacks
litellm.callbacks += [handler]Events emitted
| Event | Causality type | Fired when |
|---|---|---|
agent.model.pre_call | llm_pre_call | Before the HTTP request is dispatched; includes model, messages, stream, and litellm_call_id. |
agent.model.post_call | llm_post_call | After the API responds, before the success/failure split; includes the full request fields, raw response, and duration_s. |
agent.model.stream_chunk | llm_stream_chunk | For each individual chunk on the sync streaming path; includes model, chunk, and litellm_call_id. |
agent.model.completed | llm_end | A call finishes successfully (sync or async); includes response, usage, response_cost, duration_s, stream, and cache_hit. |
agent.model.errored | llm_error | A call fails (sync or async); includes error_type, error_message, and duration_s. |
- —span_id is derived from litellm_call_id, stamped onto kwargs by LiteLLM before any hook fires, so pre-call, post-call, and success/failure events for one request share a consistent span ID.
- —usage (prompt_tokens, completion_tokens, total_tokens) and response_cost are promoted to the top level of agent.model.completed payloads.
- —log_success_event / log_failure_event handle sync calls; async_log_success_event / async_log_failure_event handle acompletion and async streaming — both delegate to shared handlers so payloads are identical regardless of mode.
- —Any metadata dict passed via litellm_params is forwarded into the agent_metadata block on every event.