SDK Parity
Feature comparison between the Python and JavaScript SDKs.
Feature comparison between the Python and JavaScript/TypeScript SDKs. The published versions are Python risicare 0.6.0 and npm risicare 0.9.0; not every row below has been re-checked against them. Both SDKs share the same wire format and connect to the same gateway — differences are in API surface and integration breadth.
Core API
| Feature | Python | JavaScript | Notes |
|---|---|---|---|
init() | risicare.init() | init() | Same config options |
shutdown() | risicare.shutdown(timeout_ms=5000) | await shutdown(timeoutMs?) | JS is async. Both default to 5000 ms |
flush() | risicare.flush(timeout_ms=5000) | await flush(timeoutMs?) | JS is async. Both default to 5000 ms and return a boolean |
enable() / disable() | risicare.enable() | enable() | |
is_enabled() | risicare.is_enabled() | isEnabled() | |
get_client() | risicare.get_client() | — | JS uses getTracer() instead |
get_tracer() | risicare.get_tracer() | getTracer() | |
reset_for_testing() | risicare.reset_for_testing() | — | Python only |
Progressive Integration Tiers
| Tier | Feature | Python | JavaScript |
|---|---|---|---|
| 0 | Import + env var auto-instrumentation | import risicare + RISICARE_API_KEY + RISICARE_TRACING=true | — (requires init(); see Tier 1) |
| 1 | Explicit init | risicare.init() | init() |
| 1 | @trace / trace() | Decorator + context manager | — (no trace() wrapper; auto-traced via patchX() provider patches) |
| 2 | @agent | Decorator | agent(opts, fn) wrapper |
| 3 | @session / session_context() | Decorator + context manager | session(opts, fn) / withSession() |
| 4 | Phase decorators | @trace_think, @trace_decide, @trace_act, @trace_observe | traceThink(fn), traceDecide(fn), traceAct(fn), traceObserve(fn) |
| 5 | Multi-agent | @trace_message, @trace_delegate, @trace_coordinate | traceMessage(opts, fn), traceDelegate(opts, fn), traceCoordinate(opts, fn) |
Phase wrappers return a callable in both SDKs
Phase wrappers behave the same way in Python and JavaScript — each returns a wrapped function that you invoke later (not the function's result).
Python — @trace_think is a decorator; applying it to a function yields a wrapped function:
@trace_think("analyze") # or bare: @trace_think
def analyze():
return "the answer"
result = analyze() # result = "the answer"JavaScript — traceThink returns the wrapped function you then call:
const analyze = traceThink("analyze", () => "the answer"); // wrapped fn
const result = analyze(); // result = "the answer"JavaScript accepts both traceThink('name', fn) and traceThink(fn) (bare) forms — matching Python's @trace_think("name") and @trace_think. This applies to traceThink, traceDecide, traceAct, traceObserve, traceMessage, traceDelegate, and traceCoordinate. The agent() and session() wrappers follow the same return-a-callable pattern in both SDKs.
Context Propagation
| Feature | Python | JavaScript |
|---|---|---|
| Mechanism | contextvars | AsyncLocalStorage |
| Thread propagation | auto_patch=True patches ThreadPoolExecutor | Automatic via Node.js |
| Async propagation | auto_patch=True patches asyncio.create_task | Automatic via AsyncLocalStorage |
session_context() | Sync + async variants | withSession() (single API) |
agent_context() | Sync + async variants | withAgent() (single API) |
phase_context() | phase_context(SemanticPhase.THINK) | withPhase(SemanticPhase.THINK, fn) |
W3C inject_trace_context() | inject_trace_context(headers) | injectTraceContext(headers) |
W3C extract_trace_context() | extract_trace_context(headers) | extractTraceContext(headers) |
get_current_session() | Returns SessionContext | None | Returns SessionContext | undefined |
get_current_agent() | Returns AgentContext | None | Returns AgentContext | undefined |
get_current_span() | Returns Span | None | Returns Span | undefined |
| Span registry | register_span(), get_span_by_id(), unregister_span() | registerSpan(), getSpanById(), unregisterSpan() |
LLM Provider Support
| Provider | Python | JavaScript |
|---|---|---|
| OpenAI | Auto-instrumented | patchOpenAI() |
| Anthropic | Auto-instrumented | patchAnthropic() |
| Vercel AI SDK | — | patchVercelAI() |
| Google Gemini | Auto-instrumented | patchGoogleAI() |
| Cohere | Auto-instrumented | patchCohere() |
| Mistral | Chat completions (sync and async); streaming calls are not traced | patchMistral() |
| Groq | Auto-instrumented | patchGroq() |
| Together AI | Auto-instrumented | patchTogether() |
| Ollama | Auto-instrumented | patchOllama() |
| Amazon Bedrock | Auto-instrumented | patchBedrock() |
| Google Vertex AI | Auto-instrumented | Via patchGoogleAI() |
| Cerebras | Auto-instrumented | patchCerebras() |
| HuggingFace | Auto-instrumented | patchHuggingFace() |
| OpenAI-compatible (base_url) | Host detection (8 providers) | Host detection via patchOpenAI() |
Both SDKs support 12 native providers plus 8 OpenAI-compatible hosts detected by base URL (DeepSeek, Together AI, Groq, xAI, Fireworks, Baseten, Novita, BytePlus). Any other OpenAI-compatible endpoint (for example a self-hosted vLLM server) is still traced through patchOpenAI() / the OpenAI import hook, but is reported generically rather than as a named provider. Python auto-patches on import; JavaScript requires an explicit patchX() call.
Instrumentation Style
| Python | JavaScript | |
|---|---|---|
| Method | Import hooks (a wrapper around builtins.__import__) — automatic | ES Proxy wrapping — manual patchX() call |
| When | At init() for a library that is already imported, and on the first import openai after init(). A library that is imported after init() is patched too | After calling patchOpenAI(new OpenAI()) |
| Control | install_import_hooks() / remove_import_hooks() | Call or skip patchX() |
| Check | is_instrumented("openai") | — |
| List | get_supported_modules() | — |
Framework Support
| Framework | Python | JavaScript |
|---|---|---|
| LangChain | Callback + patches | RisicareCallbackHandler |
| LangGraph | Patches | instrumentLangGraph() |
| Instructor | Patches | patchInstructor() |
| LlamaIndex | Span handler | RisicareLlamaIndexHandler |
| CrewAI | Patches | — (Python only) |
| AutoGen | Patches (v0.2 + v0.4) | — (Python only) |
| OpenAI Agents SDK | Patches | — (Python only) |
| LiteLLM | Callback | — (Python only) |
| DSPy | Callback | — (Python only) |
| Pydantic AI | Patches | — (Python only) |
Python supports 10 framework integrations. JavaScript supports 4 (LangChain.js, LangGraph.js, Instructor, LlamaIndex.TS). The remaining 6 frameworks have no JavaScript integration in the Risicare SDK: CrewAI, AutoGen, OpenAI Agents, LiteLLM, DSPy, or Pydantic AI.
Exporters
| Exporter | Python | JavaScript |
|---|---|---|
HttpExporter | HTTP/2 via httpx | Native fetch |
ConsoleExporter | To stdout (debug=True) | — (npm 0.8.0 and earlier attached one with debug: true and no key; 0.9.0 does not) |
BatchSpanProcessor | Timer + count threshold | setInterval + count threshold |
OTLPExporter | OTLP/HTTP JSON + circuit breaker | — |
SpanExporter base | Abstract class | Interface |
Fix Runtime
| Feature | Python | JavaScript |
|---|---|---|
FixRuntime | Full implementation | Full implementation |
init_runtime() | init_runtime(config) | initFixRuntime() (called by init() only when fixRuntime is true) |
get_runtime() | get_runtime() | getFixRuntime() |
shutdown_runtime() | shutdown_runtime() | shutdownFixRuntime() |
FixLoader | Loads fixes from API | Loads fixes from API |
FixApplier | Applies fixes to functions | Applies fixes to functions |
FixCache | Local fix caching | Local fix caching |
FixInterceptor | Routes calls via A/B test | preCall / postCall in runtime/interceptors.ts |
Neither SDK starts the Fix Runtime by default: init() starts it only with fix_runtime=True (Python) or fixRuntime: true (JavaScript). In JS the interceptor logic lives in runtime/interceptors.ts (preCall / postCall); the OpenAI provider patch consults the Fix Runtime only when it is enabled and has active fixes. Fix deployment is not enabled in this release, and the route that the runtime reads (GET /v1/fixes/active on the API host) is not reachable with an API key during the beta — so no fix can reach the runtime. See Fix Runtime.
OpenTelemetry
| Feature | Python | JavaScript |
|---|---|---|
| OTel bridge | otel_bridge=True in init() | — |
RisicareSpanExporter | Plugs into OTel SDK | — |
RisicareSpanProcessor | Plugs into OTel SDK | — |
| OTLP export | OTLPExporter class | — |
| OTLP ingestion | Gateway accepts OTLP/HTTP | Gateway accepts OTLP/HTTP |
Configuration
| Option | Python | JavaScript |
|---|---|---|
api_key | api_key | apiKey |
endpoint | endpoint | endpoint |
api_endpoint | api_endpoint | apiEndpoint |
environment | environment | environment |
service_name | service_name | serviceName |
service_version | service_version | serviceVersion |
enabled | enabled | enabled |
trace_content | trace_content | traceContent |
sample_rate | sample_rate | sampleRate |
batch_size | batch_size | batchSize |
batch_timeout_ms | batch_timeout_ms | batchTimeoutMs |
max_queue_size | — | maxQueueSize |
auto_patch | auto_patch (default True) | — |
debug | debug | debug |
compress | — | compress |
metadata | metadata | metadata |
exporters | exporters | — |
otlp_endpoint | otlp_endpoint | — |
otlp_headers | otlp_headers | — |
otel_bridge | otel_bridge | — |
mask | mask | mask |
fix_runtime | fix_runtime (default False) | fixRuntime (default false) |
project_id (deprecated) | Emits DeprecationWarning | Emits console.warn |
Enums
SpanKind, SpanStatus, and SemanticPhase have identical values in both SDKs. AgentRole and MessageType differ:
| Enum | Python | JavaScript |
|---|---|---|
SpanKind | 17 values (identical) | 17 values (identical) |
SpanStatus | UNSET, OK, ERROR | UNSET, OK, ERROR |
SemanticPhase | THINK, DECIDE, ACT, OBSERVE, REFLECT, COMMUNICATE, COORDINATE | Same 7 values |
AgentRole | 12 values | 14 values (superset — adds REVIEWER, CUSTOM) |
MessageType | 15 values | 18 values (superset — adds REQUEST, DELEGATE, COORDINATE) |
Wire Format
Both SDKs send spans to POST /v1/spans in the same JSON format. The gateway does not distinguish between Python and JavaScript origins. A few span fields are sent by one SDK only.