Python SDK
Complete Python SDK reference.
Reference for the Risicare Python SDK. The current published version is 0.6.0 — see the Changelog.
Installation
pip install risicareExports
The risicare package exports 84 public names (risicare.__all__). The tables below list them by category:
Core
| Export | Description |
|---|---|
init | Initialize the SDK and return a RisicareClient |
shutdown | Graceful shutdown with optional timeout |
flush | Send queued spans without shutting down; returns a bool |
get_metrics | Return the SDK's export counters as a dict |
disable | Disable tracing at runtime |
enable | Re-enable tracing at runtime |
is_enabled | Check if tracing is active |
RisicareClient | Main client class |
RisicareConfig | Configuration dataclass |
get_client | Get the current client instance |
reset_for_testing | Reset the SDK's global state (for tests) |
__version__ | SDK version string |
Tracer
| Export | Description |
|---|---|
Tracer | Low-level tracer for creating spans |
get_tracer | Get the current tracer instance |
report_error | Report a caught exception to the diagnosis pipeline |
score | Record a custom evaluation score on a trace |
Decorators
| Export | Description |
|---|---|
agent | Mark a function as an agent |
session | Bind a function to a user session |
trace | Generic trace decorator with customizable kind |
trace_think | Mark a thinking phase |
trace_decide | Mark a decision phase |
trace_act | Mark an action phase |
trace_observe | Mark an observation phase |
trace_message | Mark inter-agent messaging |
trace_delegate | Mark task delegation |
trace_coordinate | Mark multi-agent coordination |
Context Managers
| Export | Description |
|---|---|
session_context | Sync context manager for sessions |
async_session_context | Async context manager for sessions |
agent_context | Sync context manager for agent identity |
async_agent_context | Async context manager for agent identity |
phase_context | Context manager for semantic phases |
restore_trace_context | Restore a saved trace context |
Context Accessors
| Export | Description |
|---|---|
get_current_session | Get active session context |
get_current_agent | Get active agent context |
get_current_span | Get active span |
get_current_phase | Get active semantic phase |
get_current_context | Get full context state |
get_trace_context | Get W3C trace context |
get_current_session_id | Get active session ID |
get_current_trace_id | Get active trace ID |
get_current_span_id | Get active span ID |
get_current_agent_id | Get active agent ID |
get_current_parent_span_id | Get parent span ID |
Span Registry
| Export | Description |
|---|---|
register_span | Register a span for ID-based lookup |
get_span_by_id | Retrieve a span by its ID |
unregister_span | Remove a span from the registry |
extend_span_ttl | Extend the TTL of a registered span |
Streaming
| Export | Description |
|---|---|
traced_stream | Wrap an async stream with tracing |
traced_stream_sync | Wrap a sync stream with tracing |
W3C Trace Context
| Export | Description |
|---|---|
inject_trace_context | Inject trace context into headers |
extract_trace_context | Extract trace context from headers |
Types
| Export | Description |
|---|---|
Span | Span data class |
SpanKind | Span kind enum |
SpanStatus | Span status enum |
SessionContext | Session context dataclass |
AgentContext | Agent context dataclass |
TraceContext | Trace context for W3C propagation |
SemanticPhase | Phase enum (THINK, DECIDE, ACT, REFLECT, OBSERVE, COMMUNICATE, COORDINATE) |
AgentRole | Agent role enum |
MessageType | Message type enum |
Fix Runtime
| Export | Description |
|---|---|
FixRuntime | Main fix runtime class |
FixLoader | Loads fix configurations |
FixApplier | Applies fixes to spans |
FixCache | Caches fix configs locally |
FixRuntimeConfig | Fix runtime configuration |
FixInterceptor | Intercepts and applies fixes |
get_runtime | Get fix runtime instance |
init_runtime | Initialize fix runtime |
shutdown_runtime | Shutdown fix runtime |
GuardRejectedError | Raised when a guard fix rejects a call (before it, or after it for the output guard) |
Auto-Instrumentation
| Export | Description |
|---|---|
install_import_hooks | Install auto-instrumentation hooks |
remove_import_hooks | Remove auto-instrumentation hooks |
instrument_already_imported | Instrument already-loaded modules |
is_instrumented | Check if a module is instrumented |
get_instrumented_modules | List instrumented modules |
get_supported_modules | List all supported modules |
PatchResult | Dataclass that records which hooks of an integration landed |
InstrumentationError | Raised when RISICARE_STRICT_INSTRUMENTATION=1 and an integration fails |
Exporters
| Export | Description |
|---|---|
OTLPExporter | OTLP/HTTP span exporter |
Masking
| Export | Description |
|---|---|
MaskFn | Type of the mask callback: Callable[[str, Any], Any] |
MASK_ERROR_MARKER | "<risicare:mask-error>", the value of a field whose mask call raised |
Webhooks
| Export | Description |
|---|---|
verify_webhook_signature | Check the signature of a webhook payload; raises WebhookVerificationError on failure |
WebhookVerificationError | Raised when a webhook signature check fails |
DEFAULT_TIMESTAMP_TOLERANCE_S | 300, the default timestamp tolerance in seconds |
risicare.init
Initialize the SDK.
risicare.init(
api_key: str | None = None,
*,
endpoint: str | None = None,
api_endpoint: str | None = None,
project_id: str | None = None,
environment: str = "development",
service_name: str | None = None,
service_version: str | None = None,
enabled: bool = True,
trace_content: bool = False,
sample_rate: float = 1.0,
batch_size: int = 500,
batch_timeout_ms: int = 1000,
auto_patch: bool = True,
debug: bool = False,
exporters: list | None = None,
metadata: dict | None = None,
otlp_endpoint: str | None = None,
otlp_headers: dict | None = None,
otel_bridge: bool = False,
mask: Callable[[str, Any], Any] | None = None,
fix_runtime: bool = False,
) -> RisicareClientEvery argument after api_key is keyword-only: risicare.init("rsk-...", "https://...") raises TypeError.
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key | str | env | API key (or RISICARE_API_KEY). Each key is scoped to one project. |
endpoint | str | "https://ingest.risicare.ai" | Ingest gateway URL (or RISICARE_ENDPOINT). The SDK sends spans to this host. |
api_endpoint | str | "https://api.risicare.ai" | API URL (or RISICARE_API_ENDPOINT). The SDK sends scores to this host. If you pass endpoint (not the default ingest host) and no api_endpoint, scores go to endpoint, also when RISICARE_API_ENDPOINT is set. |
project_id | str | None | Deprecated. Emits a DeprecationWarning. The gateway ignores it. |
environment | str | "development" | Environment name (within-project org) |
service_name | str | None | Service name (within-project org) |
service_version | str | None | Service version for traces |
enabled | bool | True | Enable tracing |
trace_content | bool | False | Capture prompts/completions. Off by default — pass trace_content=True to capture them |
sample_rate | float | 1.0 | Trace sampling rate (0.0-1.0) |
batch_size | int | 500 | Spans per batch export. Values outside [1, 10000] are clamped and a WARNING risicare.exporters.batch log line is emitted on each init() call that triggers a clamp. |
batch_timeout_ms | int | 1000 | Milliseconds between flushes |
auto_patch | bool | True | Patch ThreadPoolExecutor, ProcessPoolExecutor and asyncio.create_task for context propagation. It does not control the provider import hooks |
debug | bool | False | Attach a console exporter that prints every span to stdout. See Configuration |
exporters | list | None | Custom span exporters |
metadata | dict | None | Global metadata for all spans |
otlp_endpoint | str | None | OTLP exporter endpoint |
otlp_headers | dict | None | OTLP exporter headers |
otel_bridge | bool | False | Bridge spans to OpenTelemetry |
mask | callable | None | Function mask(key, value) that returns the value to export. It runs on content-bearing fields before export. It has no environment variable. |
fix_runtime | bool | False | Start the fix runtime (or RISICARE_FIX_RUNTIME). It is off by default since 0.4.0. The route that the fix runtime reads is not reachable with an API key during the beta, so keep it off. |
risicare.shutdown
Graceful shutdown — flushes pending spans and closes connections.
risicare.shutdown(timeout_ms: int = 5000) -> None| Parameter | Type | Default | Description |
|---|---|---|---|
timeout_ms | int | 5000 | Maximum milliseconds to wait for the batch processor thread to finish flushing |
The shutdown sequence:
- Signals the batch processor to stop
- Waits up to
timeout_msfor the worker thread to drain its queue - Performs a final flush of any remaining buffered spans
- Shuts down all exporters (HTTP connections closed)
- Restores the executor and asyncio patches if
auto_patchwas enabled. Provider patches stay
Automatic atexit handler
risicare.init() registers an atexit handler that calls shutdown() automatically on normal interpreter exit. You only need to call shutdown() explicitly if you want to flush spans before your process ends (e.g., in a serverless function or before a long-running phase that doesn't generate spans).
Potential hang on network issues
If the HTTP exporter is blocked on a network call when shutdown() is invoked, the worker thread join may block for up to timeout_ms (default 5 seconds). In latency-sensitive shutdown paths (e.g., Lambda), reduce the timeout: risicare.shutdown(timeout_ms=1000).
Running this in production
Circuit-breaker behavior during a backend outage, fork() safety for
gunicorn / uvicorn / celery, and which spans are dropped when the queue
fills are covered in
Production & Failure Modes.
risicare.flush
Send the spans queued so far. The SDK stays usable after the call.
risicare.flush(timeout_ms: int = 5000) -> bool| Parameter | Type | Default | Description |
|---|---|---|---|
timeout_ms | int | 5000 | Time to wait for delivery, in milliseconds. The call returns within this time. |
Returns a bool. True means that the gateway (or an exporter that you named) acknowledged every span and every score that the SDK accepted since the previous flush() returned (or since init()), and that nothing is in the queue or in flight. This holds also in a signal handler and after shutdown(). A span that you start after shutdown() has returned is not made, and a call to a traced provider in your handler after the SDK's drain makes no span; nothing counts them, so a True does not show that they were traced.
A loss makes the next flush() return False one time, and the one after it True again. A loss is a span dropped for any reason (shutdown_residue included), a span rejected or left out of an acknowledgement, and a failed score (also a score that score() refuses at the call). Each one raises a counter that names the reason: dropped_spans_by_reason, rejected_spans, failed_scores. A flush() that returns False at its deadline while none of these counters went up since the previous flush() means that the spans are still in flight, not lost. failed_exports is not a loss counter: a later round can still deliver those spans.
flush() also returns False when the SDK has no evidence of delivery: before init(), with tracing off, and with no API key (and no exporter of your own). A flush() that overlaps the SDK's own SIGTERM drain, or a shutdown() on another thread, waits for it inside its own deadline. With more than one exporter, a batch counts as delivered when the first exporter that succeeds takes it, so True means that at least one of them acknowledged each span. A new init() starts a new count: report a loss with flush() or get_metrics() before you call init() again.
flush() only waits: it does not stop a request that is already sent. To end a process in a fixed time, call shutdown(timeout_ms). When flush() returns False, read risicare.get_metrics().
In Python 0.5.1 and earlier, flush() returned True before init(), with tracing off and with no key, and it could return True in a signal handler or after shutdown() with nothing delivered. After a shutdown() that left spans, it returned False for every later call.
risicare.get_metrics
Return the SDK's export counters.
risicare.get_metrics() -> dict| Key | Description |
|---|---|
exported_spans | Spans that the gateway accepted. |
rejected_spans | Spans that the gateway acknowledged and rejected. |
dropped_spans | Total spans dropped. It is the sum of the counts in dropped_spans_by_reason. |
dropped_spans_by_reason | A dict with the keys queue_full, transport_refused, shutdown_residue and unacknowledged (spans that could not be serialized, or that the acknowledgement left out). |
failed_exports | Failed export attempts. It counts attempts, not spans. It is not a loss counter: a later round can still deliver those spans. |
mask_errors | Fields for which the mask function raised. This key is absent when sdk_state is "uninitialised". |
queue_size | Spans in the queue now. |
queue_capacity | Maximum number of spans in the queue. |
queue_utilization | queue_size divided by queue_capacity. |
failed_scores | Score requests that failed. |
sdk_state | "running", "shutdown" (the final counts, kept after shutdown()) or "uninitialised". |
import risicare
risicare.init(api_key="rsk-...")
# ... your application code ...
risicare.flush()
metrics = risicare.get_metrics()
print(metrics["exported_spans"], metrics["failed_exports"], metrics["dropped_spans"])A gateway acknowledgement means that the spans are in the gateway's queue. It does not mean that they are stored. To confirm that a trace is stored, open it in the dashboard.
@risicare.agent
Decorator for agent functions.
@risicare.agent(
name: str | None = None,
role: str | None = None,
agent_type: str | None = None,
version: int | None = None,
)
def my_agent():
...| Parameter | Type | Default | Description |
|---|---|---|---|
name | str | Function name | Human-readable agent name (optional) |
role | str | None | Agent role (orchestrator, worker, supervisor, critic — see AgentRole for all 12 values) |
agent_type | str | None | Type identifier for filtering |
version | int | None | Agent version number |
@risicare.session
Decorator for session-bound functions.
@risicare.session(
session_id: str | None = None,
session_id_arg: str = "session_id",
user_id_arg: str | None = "user_id",
auto_generate: bool = False,
)
def handle_request(session_id: str, user_id: str, query: str):
...| Parameter | Type | Default | Description |
|---|---|---|---|
session_id | str | None | None | Explicit session ID — bypasses argument inspection entirely |
session_id_arg | str | "session_id" | Name of the function parameter containing the session ID |
user_id_arg | str | None | "user_id" | Name of the function parameter containing the user ID |
auto_generate | bool | False | Generate a fresh UUID per call if no session ID is found |
@risicare.trace_think
Decorator for thinking phase.
@risicare.trace_think
def analyze():
...@risicare.trace_decide
Decorator for decision phase.
@risicare.trace_decide
def plan():
...@risicare.trace_act
Decorator for action phase.
@risicare.trace_act
def execute():
...@risicare.trace_observe
Decorator for observation phase (state reading).
@risicare.trace_observe
def check_memory(key: str):
"""Read state from memory."""
return memory.get(key)@risicare.trace_message
Decorator for inter-agent message passing.
@risicare.trace_message(
name: str | None = None,
target: str | None = None,
target_name: str | None = None,
)
def send_to_reviewer(message: str):
"""Send a message to another agent."""
return response| Parameter | Type | Default | Description |
|---|---|---|---|
name | str | <phase>:<function name> | Span name. The default is communicate:<function name> for trace_message and coordinate:<function name> for trace_delegate |
target | str | None | Target agent ID |
target_name | str | None | Target agent name |
@risicare.trace_delegate
Decorator for task delegation.
@risicare.trace_delegate(
name: str | None = None,
target: str | None = None,
target_name: str | None = None,
)
def assign_research_task(task: str):
"""Delegate a research task to a worker agent."""
return researcher.execute(task)| Parameter | Type | Default | Description |
|---|---|---|---|
name | str | <phase>:<function name> | Span name. The default is communicate:<function name> for trace_message and coordinate:<function name> for trace_delegate |
target | str | None | Target agent ID |
target_name | str | None | Target agent name |
@risicare.trace_coordinate
Decorator for multi-agent coordination.
@risicare.trace_coordinate(
name: str | None = None,
)
def sync_agents(agent_ids: list):
"""Coordinate state between multiple agents."""
return sync_result@risicare.trace
Generic trace decorator with customizable kind.
from risicare import SpanKind
@risicare.trace(
name: str | None = None,
kind: SpanKind = SpanKind.INTERNAL,
attributes: dict | None = None,
)
def custom_operation():
...| Parameter | Type | Default | Description |
|---|---|---|---|
name | str | Function name | Span name |
kind | SpanKind | INTERNAL | Span kind |
attributes | dict | None | Static attributes to attach to the span |
risicare.session_context
Context manager for sessions.
with risicare.session_context(
session_id: str,
user_id: str | None = None,
metadata: dict | None = None,
parent_session_id: str | None = None,
turn_number: int = 1,
):
...risicare.agent_context
Context manager for agents.
with risicare.agent_context(
agent_id: str,
agent_name: str | None = None,
agent_role: str | None = None,
agent_type: str | None = None,
version: int | None = None,
metadata: dict | None = None,
):
...risicare.phase_context
Context manager for phases.
from risicare import SemanticPhase
with risicare.phase_context(phase: SemanticPhase):
# Example: phase_context(SemanticPhase.THINK)
...risicare.is_enabled
Check if tracing is enabled. It is False before init(), with tracing off, with no API key (and no exporter of your own), and after the SDK's own SIGTERM drain. In 0.5.1 and earlier it was True with no key.
risicare.is_enabled() -> boolrisicare.get_current_trace_id
Get current trace ID.
trace_id = risicare.get_current_trace_id() -> str | Nonerisicare.get_current_span_id
Get current span ID.
span_id = risicare.get_current_span_id() -> str | Nonerisicare.get_current_session_id
Get current session ID.
session_id = risicare.get_current_session_id() -> str | Nonerisicare.get_current_agent_id
Get current agent ID.
agent_id = risicare.get_current_agent_id() -> str | Nonerisicare.get_current_parent_span_id
Get the parent span ID from the current context.
parent_span_id = risicare.get_current_parent_span_id() -> str | Nonerisicare.get_current_session
Get the active SessionContext, or None if no session is active.
session = risicare.get_current_session() -> SessionContext | NoneReturns the full SessionContext dataclass with session_id, user_id, metadata, parent_session_id, and turn_number.
risicare.get_current_agent
Get the active AgentContext, or None if no agent context is active.
agent = risicare.get_current_agent() -> AgentContext | NoneReturns the full AgentContext dataclass with agent_id, agent_name, agent_role, agent_type, version, and metadata.
risicare.get_current_span
Get the current active span, or None if no span is active.
span = risicare.get_current_span() -> Span | NoneAsync generators
In async generators, contextvars may lose the span reference after the first yield. Use the span registry (get_span_by_id) instead for async generator contexts.
risicare.get_current_phase
Get the active SemanticPhase, or None if no phase is active.
phase = risicare.get_current_phase() -> SemanticPhase | NoneReturns one of: SemanticPhase.THINK, DECIDE, ACT, REFLECT, OBSERVE, COMMUNICATE, COORDINATE.
risicare.get_current_context
Get the full trace context state.
ctx = risicare.get_current_context() -> dictReturns a dict with session, agent, span, and phase keys (each None when not set), capturing the current context state in a single call. Useful for debugging, logging, or serialization.
risicare.get_trace_context
Get W3C trace context for distributed tracing propagation.
trace_ctx = risicare.get_trace_context() -> TraceContextrisicare.get_tracer
Get the global Tracer instance. Use this for low-level span creation.
tracer = risicare.get_tracer() -> Tracertracer = risicare.get_tracer()
span = tracer.start_span("my-operation")
span.set_attribute("key", "value")
span.end()risicare.report_error
Report a caught exception so Risicare records it on the trace and classifies it against the error taxonomy. Use this when your code catches an exception you still want recorded. Root-cause analysis and fix suggestions are held for the beta, so reporting an error does not produce them.
risicare.report_error(
exception: BaseException,
*,
name: Optional[str] = None,
attributes: Optional[Dict[str, Any]] = None,
) -> None| Parameter | Type | Default | Description |
|---|---|---|---|
exception | BaseException | (required) | The caught exception |
name | str | None | None | Span name (only used outside a traced context; defaults to "error:{ExceptionType}") |
attributes | dict | None | None | Extra attributes for the standalone error span. Inside a traced context they are not recorded |
Inside a traced context: records the exception on the current span and sets error=True, error.type, and error.message attributes. The span's status is left unchanged — the traced function may still complete successfully.
Outside any traced context: creates a standalone error span with status ERROR. Duplicate errors (same type + message) are suppressed for 5 minutes.
This function never raises.
from risicare import report_error
def answer(prompt):
try:
return llm.invoke(prompt)
except openai.RateLimitError as e:
report_error(e) # Records the error on a span and classifies it
return cached_responserisicare.score
Record a custom evaluation score on a trace. Scores appear in the dashboard and can be used for filtering, analytics, and quality monitoring.
risicare.score(
trace_id: str,
name: str,
value: float,
*,
span_id: Optional[str] = None,
comment: Optional[str] = None,
) -> None| Parameter | Type | Default | Description |
|---|---|---|---|
trace_id | str | (required) | The trace to score: 32 lowercase hex characters. The SDK does not check the form; the API refuses any other form |
name | str | (required) | Score name (e.g., "accuracy", "user_satisfaction") |
value | float | (required) | Score value between 0.0 and 1.0 inclusive. Values outside this range are rejected client-side. |
span_id | str | None | None | Optional span within the trace |
comment | str | None | None | Human-readable explanation |
This function is non-blocking — it dispatches the score in a background thread and returns immediately. It never raises. A failed request (an error, a timeout, or a response status of 300 or more) adds 1 to failed_scores, and the next flush() returns False. The first failure logs a WARNING with the score name and the reason; failures in the next 10 seconds are counted, and the next WARNING gives their number.
import risicare
risicare.init(api_key="rsk-...")
risicare.score(
trace_id="4bf92f3577b34da6a3ce929d0e0e4736",
name="factual_accuracy",
value=0.92,
comment="Verified against source documents"
)risicare.enable
Re-enable tracing after it was disabled. It cannot turn tracing on when the SDK has no API key (and no exporter of your own): is_enabled() stays False.
risicare.enable() -> Nonerisicare.disable
Temporarily disable all tracing. Instrumented calls become no-ops with zero overhead.
risicare.disable() -> None# Disable tracing for a sensitive operation
risicare.disable()
result = do_sensitive_work()
risicare.enable()risicare.get_client
Get the current RisicareClient instance, or None if not initialized.
client = risicare.get_client() -> RisicareClient | NoneSpan Registry
The span registry provides ID-based span lookup, which is essential for contexts where contextvars may not propagate reliably (async generators, thread pools).
risicare.register_span
Register a span in the global registry for later retrieval by ID.
risicare.register_span(
span: Span,
ttl_seconds: Optional[float] = None,
) -> NoneA registered span expires after ttl_seconds (default: 60 seconds). The SDK registers each span while it is open, also the spans of decorators and context managers, and removes it when the span ends. The TTL applies to an open span too: for a span that stays open longer, call extend_span_ttl(), which risicare exports since 0.5.1 (see Streaming). Use register_span() to give a span a different TTL.
risicare.get_span_by_id
Retrieve a span by its ID from the global registry.
span = risicare.get_span_by_id(span_id: str) -> Span | None# Useful in async generators where contextvars may be lost
async def stream_results(span_id: str):
span = risicare.get_span_by_id(span_id)
async for chunk in source:
span.add_event("chunk", {"size": len(chunk)})
yield chunkrisicare.unregister_span
Remove a span from the global registry.
risicare.unregister_span(span_id: str) -> NoneA span leaves the registry when it ends, so this call is optional.
Streaming
Utilities for tracing streaming responses from LLM providers.
risicare.traced_stream
Wrap an async iterator with tracing. Each chunk adds an event to the span, and the totals are set when the stream ends. Use it inside a with tracer.start_span() block: the block ends the span, also when the code raises before the stream starts. Outside a block, traced_stream ends the span itself when the stream completes, raises or is closed (status unset); a stream that never starts then leaves its span open and unsent. When the block is inside an async generator, read the generator to its end or close it with contextlib.aclosing() — see Streaming.
risicare.traced_stream(
span_id: str,
stream: AsyncIterator,
event_name: str = "chunk",
) -> AsyncIteratorasync def consume():
with tracer.start_span("stream-response") as span:
async for chunk in risicare.traced_stream(span.span_id, response.stream()):
print(chunk)risicare.traced_stream_sync
Synchronous version of traced_stream.
risicare.traced_stream_sync(
span_id: str,
stream: Iterator,
event_name: str = "chunk",
) -> Iteratorwith tracer.start_span("stream-response") as span:
for chunk in risicare.traced_stream_sync(span.span_id, response.stream()):
print(chunk)W3C Trace Context
Functions for propagating trace context across service boundaries using W3C traceparent and tracestate headers.
risicare.inject_trace_context
Inject W3C traceparent and tracestate headers into a headers dict.
risicare.inject_trace_context(headers: dict) -> dictheaders = {"Content-Type": "application/json"}
risicare.inject_trace_context(headers)
# Inside an active span, headers now includes "traceparent".
# "tracestate" is added only when a session or an agent is current.
response = httpx.post(url, headers=headers)risicare.extract_trace_context
Extract trace context from incoming W3C headers.
ctx = risicare.extract_trace_context(headers: dict) -> dictReturns a dict (empty if no trace headers are present). When a valid traceparent is present it always includes version, trace_id, parent_span_id, and flags; session_id and agent_id are added only when the caller propagated them via tracestate. To restore context, build a TraceContext from these keys first — restore_trace_context() expects a TraceContext object, not the raw dict:
from risicare import TraceContext, restore_trace_context, extract_trace_context
# In your HTTP handler. The lookup needs lower-case header names.
raw = extract_trace_context({k.lower(): v for k, v in request.headers.items()})
if raw.get("trace_id") and raw.get("parent_span_id"):
ctx = TraceContext(
trace_id=raw["trace_id"],
span_id=raw["parent_span_id"],
session_id=raw.get("session_id"),
agent_id=raw.get("agent_id"),
)
with restore_trace_context(ctx):
process_request()Auto-Instrumentation
Functions to control which modules are auto-instrumented.
risicare.install_import_hooks
Install Python import hooks for auto-instrumentation. Called automatically by risicare.init() when tracing is enabled, whatever the value of auto_patch.
risicare.install_import_hooks(
allowed_modules: Optional[Set[str]] = None,
) -> RisicareImportFinderPass allowed_modules to limit which modules are instrumented (e.g., {"openai", "anthropic"}). Returns the import finder instance.
risicare.remove_import_hooks
Remove auto-instrumentation import hooks. Modules already instrumented remain instrumented.
risicare.remove_import_hooks() -> Nonerisicare.instrument_already_imported
Instrument modules that were imported before risicare.init() was called. Called automatically by init().
risicare.instrument_already_imported() -> intReturns the number of modules that were instrumented.
risicare.is_instrumented
Check if at least one instrumentation hook landed for a module. True when hooks_landed > 0, also for a partial patch. False when no hook landed, when the module is pending, and when it was never imported. For the full state, read get_instrumented_modules().
risicare.is_instrumented(module_name: str) -> boolif risicare.is_instrumented("openai"):
print("OpenAI calls are traced")risicare.get_instrumented_modules
Get the per-module instrumentation state. Returns a dict keyed by module name; each value is a state record with eight keys: state, hooks_landed, hooks_attempted, succeeded, failed, inert, version_compat and targets_resolved.
modules = risicare.get_instrumented_modules() -> dict[str, dict]
# {
# "openai": {"state": "instrumented", "hooks_landed": 4, ...},
# "langchain": {"state": "attached-inert", "hooks_landed": 0, ...},
# }state is one of instrumented, attached-inert, partially_instrumented, failed, pending or attempted. Only state == "instrumented" means the integration is attached and on a live call path. A patched provider reports instrumented, and one whose patch raised reports failed. pending: the module is imported, but the module that holds its patch points is not yet (for example botocore imported and botocore.client not yet, or vertexai before vertexai.generative_models). The OpenTelemetry bridge still reports attempted.
risicare.get_supported_modules
Get the set of all module names supported for auto-instrumentation.
supported = risicare.get_supported_modules() -> set[str]
# e.g. {"openai", "anthropic", "cohere", "langchain", ...}Exporters
OTLPExporter
Export spans via OTLP/HTTP protocol. Use this when sending traces to an OpenTelemetry-compatible backend alongside Risicare.
from risicare import OTLPExporter
exporter = OTLPExporter(
endpoint="http://localhost:4318/v1/traces",
headers={"Authorization": "Bearer token"},
)
risicare.init(exporters=[exporter])Fix Runtime
Held for the beta
The fix runtime is off by default since 0.4.0. The route that it reads is not
reachable with an API key during the beta, so a runtime that you start loads no
fix. See Fix Runtime.
The fix runtime loads, caches, and applies fixes at the SDK level. It runs inside your agent process and intercepts matching errors to apply the configured fix strategy.
FixRuntimeConfig
Configuration for the fix runtime.
from risicare import FixRuntimeConfig
config = FixRuntimeConfig(
enabled=True,
refresh_interval_seconds=30,
cache_ttl_seconds=300,
)See Fix Runtime for every field and its default.
risicare.init_runtime
Initialize the fix runtime with a configuration.
risicare.init_runtime(config: Optional[FixRuntimeConfig] = None, auto_start: bool = True) -> FixRuntimerisicare.get_runtime
Get the current fix runtime instance.
runtime = risicare.get_runtime() -> FixRuntime | Nonerisicare.shutdown_runtime
Shutdown the fix runtime gracefully.
risicare.shutdown_runtime() -> NoneFixRuntime
Main fix runtime class. Coordinates FixLoader, FixCache, FixApplier, and FixInterceptor.
FixLoader
Loads fix configurations from the Risicare API.
FixCache
Caches fix configurations locally with configurable TTL to minimize API calls.
FixApplier
Applies a fix configuration to an error span (e.g., retry, fallback, parameter change).
FixInterceptor
Intercepts errors matching a fix rule and applies the configured fix before the error propagates.
Async Context Managers
risicare.async_session_context
Async version of session_context for use in async code.
async with risicare.async_session_context(
session_id: str,
user_id: str | None = None,
metadata: dict | None = None,
parent_session_id: str | None = None,
turn_number: int = 1,
):
await handle_request()risicare.async_agent_context
Async version of agent_context for use in async code.
async with risicare.async_agent_context(
agent_id: str,
agent_name: str | None = None,
agent_role: str | None = None,
agent_type: str | None = None,
version: int | None = None,
metadata: dict | None = None,
):
await run_agent()risicare.restore_trace_context
Restore a previously saved or extracted TraceContext. Use this to re-establish context in a new scope, such as after extracting from W3C headers or restoring in a background task.
with risicare.restore_trace_context(ctx: TraceContext):
process_request()Types
Span
Core span data class representing a single unit of work.
from risicare import Span
span.trace_id # str — 32 hex characters
span.span_id # str — 16 hex characters
span.parent_span_id # str | None
span.name # str
span.kind # SpanKind
span.status # SpanStatus
span.start_time # float (epoch seconds)
span.end_time # float | None
span.attributes # dict
span.events # listSpanKind
from risicare import SpanKind
# Standard (OpenTelemetry compatible)
SpanKind.INTERNAL # Default, internal operation
SpanKind.SERVER # Server-side of RPC
SpanKind.CLIENT # Client-side of RPC
SpanKind.PRODUCER # Message producer
SpanKind.CONSUMER # Message consumer
# Agent-specific
SpanKind.AGENT # Agent lifecycle span
SpanKind.LLM_CALL # LLM API call
SpanKind.TOOL_CALL # Tool/function execution
SpanKind.RETRIEVAL # RAG retrieval operation
SpanKind.DECISION # Agent decision point
SpanKind.DELEGATION # Delegation to another agent
SpanKind.COORDINATION # Multi-agent coordination
SpanKind.MESSAGE # Inter-agent message
# Phase-specific
SpanKind.THINK # Reasoning/planning phase
SpanKind.DECIDE # Decision-making phase
SpanKind.OBSERVE # Environment observation phase
SpanKind.REFLECT # Self-evaluation phaseSpanStatus
from risicare import SpanStatus
SpanStatus.OK # Successful
SpanStatus.ERROR # Error occurred
SpanStatus.UNSET # Status not setSemanticPhase
from risicare import SemanticPhase
SemanticPhase.THINK # Reasoning / analysis
SemanticPhase.DECIDE # Decision making
SemanticPhase.ACT # Action execution
SemanticPhase.REFLECT # Self-evaluation
SemanticPhase.OBSERVE # State reading
SemanticPhase.COMMUNICATE # Inter-agent messaging
SemanticPhase.COORDINATE # Multi-agent coordinationAgentRole
from risicare import AgentRole
# Primary roles
AgentRole.ORCHESTRATOR # Coordinates other agents
AgentRole.WORKER # Executes assigned tasks
AgentRole.SUPERVISOR # Monitors and validates
AgentRole.SPECIALIST # Domain expert
# Communication roles
AgentRole.ROUTER # Routes messages/tasks
AgentRole.AGGREGATOR # Aggregates results
AgentRole.BROADCASTER # Broadcasts to multiple agents
# Specialized roles
AgentRole.CRITIC # Reviews and critiques
AgentRole.PLANNER # Creates execution plans
AgentRole.EXECUTOR # Executes plans
AgentRole.RETRIEVER # Retrieves information
AgentRole.VALIDATOR # Validates outputsMessageType
from risicare import MessageType
# Control messages
MessageType.TASK # Task assignment
MessageType.RESULT # Task result
MessageType.STATUS # Status update
MessageType.ERROR # Error notification
# Communication messages
MessageType.QUERY # Information request
MessageType.RESPONSE # Information response
MessageType.BROADCAST # Broadcast to all
MessageType.DIRECT # Direct message
# Coordination messages
MessageType.PROPOSAL # Propose action
MessageType.VOTE # Vote on proposal
MessageType.CONSENSUS # Consensus reached
MessageType.CONFLICT # Conflict detected
# Lifecycle messages
MessageType.HEARTBEAT # Agent alive signal
MessageType.SHUTDOWN # Shutdown signal
MessageType.HANDOFF # Handoff to another agentExceptionInfo
Exception data attached to spans. Imported from risicare_core (not re-exported by the risicare package).
from risicare_core import ExceptionInfo
info = ExceptionInfo(
type="TimeoutError",
message="Request timed out after 30s",
stacktrace="Traceback (most recent call last):\n ...",
# timestamp: auto-set to UTC now
# escaped: defaults to True
)| Field | Type | Default | Description |
|---|---|---|---|
type | str | required | Exception class name |
message | str | required | Exception message |
stacktrace | str | required | Full stack trace as string |
timestamp | datetime | utc_now() | When the exception occurred |
escaped | bool | True | Whether the exception escaped the span |
SessionContext
from risicare import SessionContext
session.session_id # str
session.user_id # str | None
session.metadata # dict
session.parent_session_id # str | None
session.turn_number # int
session.started_at # datetimeAgentContext
from risicare import AgentContext
agent.agent_id # str
agent.agent_name # str | None
agent.agent_role # str | None
agent.agent_type # str | None
agent.parent_agent_id # str | None
agent.version # int | None
agent.metadata # dictTraceContext
from risicare import TraceContext
ctx.trace_id # str — 32-char lowercase hex
ctx.span_id # str — 16-char lowercase hex
ctx.session_id # str | None
ctx.agent_id # str | NoneTesting Utilities
risicare.reset_for_testing
Reset all SDK state for clean test isolation. Call in test teardown to prevent state leakage between tests.
risicare.reset_for_testing() -> NonePerforms a full reset:
- Calls
shutdown(timeout_ms=5000)(swallows errors) - Resets the client singleton
- Resets the tracer singleton
- Resets all context variables (session, agent, span, phase)
# pytest fixture
import pytest
import risicare
@pytest.fixture(autouse=True)
def clean_risicare():
yield
risicare.reset_for_testing()