Configuration
Configure the Risicare SDK for your application.
Configure the Risicare SDK to connect to your project and customize tracing behavior.
JavaScript SDK?
Basic Setup
import risicare
risicare.init(
api_key="rsk-...", # Or use RISICARE_API_KEY env var
service_name="my-agent", # Identify your service
environment="production", # Environment name
)API Key = Project
Your API key is scoped to the project it was created under (visible in Settings → General). No separate project_id is needed — the gateway resolves it from your key.
project_id is deprecated
Passing project_id to init() emits a DeprecationWarning in Python (or console.warn in JavaScript). The parameter is ignored by the gateway and will be removed in v1.0. Use service_name and environment for within-project organization instead.
Configuration Options
Required
| Option | Type | Description |
|---|---|---|
api_key | str | Your Risicare API key, from the dashboard. Paste it exactly as the dashboard shows it. Each key is scoped to one project. |
With no API key the SDK sends nothing
With no key (also a key of only whitespace; the SDK trims the key) and no exporter of your own, tracing is off. The SDK sends nothing to Risicare, is_enabled() and flush() return False, and nothing is counted as exported. init() logs one WARNING that says so (none when you turned tracing off on purpose). RISICARE_TRACING=true and enable() cannot turn tracing on without a key. An empty api_key argument counts as not given, so RISICARE_API_KEY applies. An exporter that you name yourself (otlp_endpoint, exporters=[...]) still delivers without a key: then is_enabled() is True and flush() reports the delivery of that exporter, with no WARNING. In 0.5.1 and earlier, is_enabled() and flush() returned True with no key.
Optional
| Option | Type | Default | Description |
|---|---|---|---|
endpoint | str | "https://ingest.risicare.ai" | Ingest gateway URL. The SDK sends spans to this host. |
api_endpoint | str | "https://api.risicare.ai" | API URL. 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. Ignored by the gateway. See the warning above. |
environment | str | "development" | Environment name (development, staging, production) |
service_name | str | None | Service name for within-project organization |
service_version | str | None | Service version for traces |
enabled | bool | True | Enable/disable tracing globally |
trace_content | bool | False | Capture prompt/completion content. Off by default — pass trace_content=True (or set RISICARE_TRACE_CONTENT=true) to capture it |
sample_rate | float | 1.0 | Sampling rate (0.0-1.0, clamped) |
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 batch exports |
auto_patch | bool | True | Patch ThreadPoolExecutor, ProcessPoolExecutor, and asyncio.create_task for automatic context propagation |
debug | bool | False | Echo every span to stdout via a console exporter. Prints span attributes as they are exported (after mask, if one is set), including prompt and completion text when content capture is on — see the warning below before enabling. |
exporters | list[SpanExporter] | None | Custom span exporters. Import the base class with from risicare.exporters import SpanExporter, ExportResult, and implement export(self, spans) -> ExportResult. When you pass exporters, the SDK does not add its HTTP exporter (it logs a WARNING). Default: the HTTP exporter when api_key is provided |
metadata | dict | {} | Global metadata attached to all spans |
otlp_endpoint | str | None | OTLP/HTTP export endpoint |
otlp_headers | dict | None | OTLP export headers |
otel_bridge | bool | False | Enable OpenTelemetry bridge |
mask | Callable[[str, Any], Any] | None | Function mask(key, value) that returns the value to export. It runs on content-bearing fields before export. It has no environment variable. See Production & Failure Modes. |
fix_runtime | bool | False | Start the 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. |
Content truncation
When trace_content=True, captured text is truncated. Provider integrations cut each prompt and completion field at 10,000 characters, with no marker (an Anthropic system prompt at 5,000). Framework integrations cut at a limit between 500 and 10,000 characters and append ... [truncated]. These limits are not configurable — they are hardcoded safety bounds to prevent oversized spans.
auto_patch does not control provider instrumentation
When tracing is enabled, init() installs the import hooks that patch LLM provider libraries (OpenAI, Anthropic, etc.), whatever the value of auto_patch. auto_patch controls only the ThreadPoolExecutor, ProcessPoolExecutor and asyncio.create_task patches. With auto_patch=False, providers are still instrumented.
What debug=True enables
- Console exporter: Adds a
ConsoleExporterthat prints every span to stdout as it's exported — including span attributes. With no API key it still prints the spans, but they are not counted as exported and not delivered, andflush()returnsFalse - Debug log messages: Messages about an LLM call with no parent span (each such call becomes its own trace — a common Tier 1 mistake) and about initialization go to the
risicarelogger atDEBUGlevel. You see them only if you set that logger toDEBUG - No startup line: The
[risicare] Tracing activeline is not printed whendebug=True
debug=True prints prompt and completion content unredacted
The console exporter writes span attributes to stdout as they are exported (after mask, if one is set), including
gen_ai.prompt and gen_ai.completion when content capture is on. Any customer data in a prompt is
printed to your terminal and container logs. Attribute values are truncated at
~80 characters, which is not redaction — a value that begins with an SSN
or card number is printed in full.
Do not enable it against production traffic. init() also accepts a mask
callable for redacting attribute values before export.
debug=True does not show more about export failures
A failed span export logs a WARNING that names the endpoint and the HTTP status (or
the error, when there is no answer). debug=True adds nothing on that path. To see each
attempt, raise the SDK logger:
import logging
logging.getLogger("risicare").setLevel(logging.DEBUG)Span Delivery
When you pass an api_key, init() installs the HTTP exporter. This is the
default path — if you have not configured exporters or an OTLP endpoint, this
is what is sending your spans.
The exporter has a circuit breaker
After 5 consecutive export failures the exporter opens a circuit and stops attempting exports for a 60-second cooldown. Spans created during that window are dropped. This bounds the damage a gateway outage does to your application's latency, but it means a brief outage costs you more than the outage itself — delivery does not resume the moment the gateway returns.
Tracing never raises: an export failure is logged, not thrown. The SDK logs a
WARNING that names the endpoint and the HTTP status when spans stop arriving. To see
each attempt, raise the SDK logger as shown above.
The OTLPExporter also opens after 5 failed calls and stays open for 60 seconds.
After the cooldown it lets one call through; if that call fails, it opens again at once.
It counts a 429 or a 503 as a failure — see OpenTelemetry.
For the measured behaviour — how the threshold counts, which spans survive, and how long delivery really stays stopped — see Production & Failure Modes.
Environment Variables
You can set these options with environment variables:
export RISICARE_API_KEY="rsk-..."
export RISICARE_ENVIRONMENT="production"
export RISICARE_SERVICE_NAME="my-agent"
export RISICARE_TRACING="true"
export RISICARE_TRACE_CONTENT="true"
export RISICARE_SAMPLE_RATE="1.0"
export RISICARE_SERVICE_VERSION="1.0.0"
export RISICARE_DEBUG="false"
export RISICARE_OTLP_ENDPOINT="https://otel-collector:4318"
export RISICARE_OTLP_HEADERS="key1=value1,key2=value2"
export RISICARE_OTEL_BRIDGE="false"| Variable | Maps To |
|---|---|
RISICARE_API_KEY | api_key |
RISICARE_ENDPOINT | endpoint |
RISICARE_API_ENDPOINT | api_endpoint |
RISICARE_PROJECT_ID | project_id (deprecated) |
RISICARE_ENVIRONMENT | environment |
RISICARE_SERVICE_NAME | service_name |
RISICARE_SERVICE_VERSION | service_version |
RISICARE_TRACING | enabled |
RISICARE_TRACE_CONTENT | trace_content |
RISICARE_SAMPLE_RATE | sample_rate |
RISICARE_DEBUG | debug |
RISICARE_OTLP_ENDPOINT | otlp_endpoint |
RISICARE_OTLP_HEADERS | otlp_headers |
RISICARE_OTEL_BRIDGE | otel_bridge |
RISICARE_FIX_RUNTIME | fix_runtime |
risicare.init() reads these fifteen variables. The import hooks read one more,
RISICARE_STRICT_INSTRUMENTATION — see
Production & Failure Modes.
batch_size, batch_timeout_ms, auto_patch, exporters, metadata and mask have
no environment variable.
Boolean variables accept true, 1, yes and on, in upper or lower case. Any other
value, including an empty value, means false. RISICARE_TRACING has its own rule: the
value is trimmed and case is ignored; true, 1, yes and on turn tracing on;
false, 0, no and off turn it off; an empty value is the same as no value; any
other value turns tracing off and logs one WARNING.
Do not set RISICARE_ENDPOINT to the dashboard address
https://app.risicare.ai is the dashboard. It is not the ingest gateway. Leave both
variables unset to use the defaults. The SDK finds the host for scores in this order: the
api_endpoint option; the endpoint option, unless it is the default ingest host;
RISICARE_API_ENDPOINT; RISICARE_ENDPOINT, when no endpoint option is given and it is
not the default ingest host; https://api.risicare.ai. So a custom endpoint alone also
receives the scores, and https://ingest.risicare.ai never does.
Single-Import Instrumentation
Add import risicare to your entrypoint and set RISICARE_API_KEY and
RISICARE_TRACING=true to enable auto-instrumentation without calling init() or
changing your agent logic. The import itself is required — these variables are read
when the SDK is imported. This path uses the same defaults as init(): the environment
development and no service name, when RISICARE_ENVIRONMENT and
RISICARE_SERVICE_NAME are not set.
Advanced Configuration
Custom Endpoint
Override the default gateway endpoint (https://ingest.risicare.ai) — for example, to send
spans through an outbound proxy your network requires:
risicare.init(
api_key="rsk-...",
endpoint="https://llm-egress-proxy.internal.example.com",
)endpoint sets where the SDK sends spans. If you do not also pass api_endpoint, the SDK
sends scores to the same host as endpoint. To send scores to a different host, pass
api_endpoint: when endpoint is passed as an option, RISICARE_API_ENDPOINT does not
override it.
Risicare is a hosted service
Risicare runs as a managed service. There is no self-hosted or on-premise Risicare
instance to point this at — endpoint and api_endpoint retarget where the SDK sends
its requests (a proxy or a non-default host), not where Risicare itself runs.
Sampling
Control trace sampling rate:
risicare.init(
api_key="rsk-...",
sample_rate=0.1, # Sample 10% of traces
)Programmatic Control
Check Status
if risicare.is_enabled():
print("Tracing is active")is_enabled() 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.
Flush Pending Spans
# Send the spans queued so far. The SDK stays usable afterwards.
risicare.flush(timeout_ms=5000) # default: 5000msflush() returns a bool. True means that the gateway (in Python, also an exporter that you named) acknowledged every span and every score that the SDK accepted since the previous flush() returned, and that nothing is in the queue or in flight. This holds also in a signal handler and after shutdown().
A loss (a span dropped, rejected or not acknowledged, or a score that failed) makes the next flush() return False one time, and the one after it True again. flush() also returns False before init(), with tracing off, and with no API key (and no exporter of your own).
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 exporter acknowledged each span; a failure of another exporter shows only in its own WARNING and in failed_exports. A span that you start after shutdown() has returned is not made, and nothing counts it.
When it is False, read risicare.get_metrics(): dropped_spans_by_reason, rejected_spans and failed_scores name the loss; a False at the deadline with none of them raised means that the spans are still in flight. See the flush() reference for every case.
metrics = risicare.get_metrics()
print(metrics["exported_spans"], metrics["failed_exports"], metrics["dropped_spans"])See the Python SDK reference for every field.
Shutdown
# Graceful shutdown - flushes and closes connections
risicare.shutdown(timeout_ms=5000) # default: 5000msshutdown() waits up to timeout_ms for the batch processor thread to drain, then performs a final flush and closes HTTP connections. An atexit handler calls shutdown() automatically on normal exit — explicit calls are only needed when you want to flush mid-process (e.g., serverless functions, before a long non-tracing phase).
Shutdown may block on network issues
If the exporter is mid-request when shutdown() runs, the thread join blocks for up to timeout_ms. In latency-sensitive paths, reduce the timeout: risicare.shutdown(timeout_ms=1000).
Auto-Instrumentation Management
Control which libraries are automatically instrumented:
from risicare import (
install_import_hooks,
remove_import_hooks,
instrument_already_imported,
is_instrumented,
get_instrumented_modules,
get_supported_modules,
)
# See which libraries can be auto-instrumented
get_supported_modules()
# {'openai', 'anthropic', 'cohere', 'google.generativeai', 'mistralai', ...}
# Check what's currently instrumented — returns a per-module state record
get_instrumented_modules()
# {'openai': {'state': 'instrumented', 'hooks_landed': 4, ...},
# 'anthropic': {'state': 'instrumented', 'hooks_landed': 2, ...}}
# A patched provider reports "instrumented"; "failed" if its patch raised;
# "pending" if the module that holds its patch points is not imported yet.
# Check a specific library: True when at least one hook landed.
is_instrumented("openai") # True
# Instrument libraries that were imported before risicare.init()
count = instrument_already_imported()
# Returns number of newly instrumented modules
# Remove all import hooks (stops future auto-instrumentation)
remove_import_hooks()
# Re-install import hooks
install_import_hooks()Configuration Precedence
Configuration is resolved in this order (highest to lowest priority):
- Explicit
init()parameters - Environment variables
- Default values