Skip to main content
GitHub

JavaScript SDK

Complete JavaScript/TypeScript SDK reference.

Complete reference for the Risicare JavaScript/TypeScript SDK (v0.4.2).

Installation

npm install risicare

Core Functions

init

Initialize the SDK.

import { init } from 'risicare';
 
init(config?: RisicareConfig): void

enable / disable / isEnabled

Runtime control.

import { enable, disable, isEnabled } from 'risicare';
 
enable(): void
disable(): void
isEnabled(): boolean

flush / shutdown

Export control.

import { flush, shutdown } from 'risicare';
 
flush(): Promise<void>
shutdown(): Promise<void>

getMetrics

Returns processor metrics for monitoring export health.

import { getMetrics } from 'risicare';
 
getMetrics(): {
  exportedSpans: number;
  droppedSpans: number;
  failedExports: number;
  queueSize: number;
  queueCapacity: number;
  queueUtilization: number;
}

getTraceContent

Returns whether content tracing is enabled.

import { getTraceContent } from 'risicare';
 
getTraceContent(): boolean

reportError

Report a caught exception to the self-healing pipeline. Creates an error span that triggers diagnosis. Never throws.

import { reportError } from 'risicare';
 
try {
    const result = await llm.invoke(query);
} catch (error) {
    reportError(error);          // Triggers diagnosis → fix generation
    return fallbackResponse;
}
ParameterTypeDefaultDescription
errorError | string(required)The caught exception
options.namestringerror class nameCustom span name
options.attributesRecord{}Additional span attributes

score

Record a custom evaluation score on a trace. Non-blocking — sends in background via fetch(). Never throws.

import { score } from 'risicare';
 
score('trace-abc123', 'factual_accuracy', 0.92, {
    comment: 'Verified against source documents',
});
ParameterTypeDefaultDescription
traceIdstring(required)The trace to score
namestring(required)Score name
valuenumber(required)Score between 0.0 and 1.0
options.spanIdstringundefinedSpecific span within the trace
options.commentstringundefinedHuman-readable explanation

Values outside [0.0, 1.0] are rejected with a warning and not sent.

Provider Patches

12 native provider patches, each imported from a sub-path export. All client-based patches return a transparent ES Proxy wrapper — the original client is unchanged. patchVercelAI() is the exception — it takes no client and wraps top-level functions, returning { tracedGenerateText, tracedStreamText, tracedGenerateObject }.

ProviderImportPatch Function
OpenAIrisicare/openaipatchOpenAI(client)
Anthropicrisicare/anthropicpatchAnthropic(client)
Vercel AIrisicare/vercel-aipatchVercelAI()
Google Geminirisicare/googlepatchGoogleAI(client)
Mistralrisicare/mistralpatchMistral(client)
Groqrisicare/groqpatchGroq(client)
Cohererisicare/coherepatchCohere(client)
Together AIrisicare/togetherpatchTogether(client)
Ollamarisicare/ollamapatchOllama(client)
HuggingFacerisicare/huggingfacepatchHuggingFace(client)
Cerebrasrisicare/cerebraspatchCerebras(client)
AWS Bedrockrisicare/bedrockpatchBedrock(client)
import { patchOpenAI } from 'risicare/openai';
import { patchAnthropic } from 'risicare/anthropic';
import { patchGroq } from 'risicare/groq';
import OpenAI from 'openai';
 
const openai = patchOpenAI(new OpenAI());
// All chat.completions.create and embeddings.create calls are now traced

Host Detection (OpenAI-Compatible)

When using patchOpenAI() with a client pointing to an OpenAI-compatible API, the SDK automatically detects the real provider from the base URL and sets gen_ai.system correctly:

ProviderBase URL
DeepSeekapi.deepseek.com
Together AIapi.together.xyz
Groqapi.groq.com
xAI (Grok)api.x.ai
Fireworksapi.fireworks.ai
Baseteninference.baseten.co
Novitaapi.novita.ai
BytePlusapi.byteplus.com
import { patchOpenAI } from 'risicare/openai';
import OpenAI from 'openai';
 
// DeepSeek via OpenAI-compatible API — auto-detected as "deepseek"
const deepseek = patchOpenAI(new OpenAI({
    baseURL: 'https://api.deepseek.com/v1',
    apiKey: process.env.DEEPSEEK_API_KEY,
}));

patchVercelAI

Returns higher-order wrapper functions for Vercel AI SDK:

import { generateText, streamText, generateObject } from 'ai';
import { patchVercelAI } from 'risicare/vercel-ai';
 
const { tracedGenerateText, tracedStreamText, tracedGenerateObject } = patchVercelAI();
const generate = tracedGenerateText(generateText);
const stream = tracedStreamText(streamText);
const genObject = tracedGenerateObject(generateObject);

Framework Integrations

4 framework integrations, each imported from a sub-path export:

LangChain.js

import { RisicareCallbackHandler } from 'risicare/langchain';
 
const handler = new RisicareCallbackHandler();
const result = await chain.invoke(input, { callbacks: [handler] });

LangGraph.js

import { instrumentLangGraph } from 'risicare/langgraph';
 
const tracedGraph = instrumentLangGraph(compiledGraph);
const result = await tracedGraph.invoke(input);

Instructor

import { patchInstructor } from 'risicare/instructor';
 
const patchedClient = patchInstructor(instructorClient);
const result = await patchedClient.create({ /* ... */ });

LlamaIndex.TS

import { RisicareLlamaIndexHandler } from 'risicare/llamaindex';
 
const handler = new RisicareLlamaIndexHandler();
// Register with LlamaIndex's event system

Streaming

Trace async iterables with automatic span lifecycle management:

import { tracedStream } from 'risicare';
 
const stream = tracedStream(asyncIterable, { name: 'llm-stream' });
for await (const chunk of stream) {
    process(chunk);
}
// Span automatically records stream.chunk_count and stream.completed

Dedup

Framework integrations can suppress provider-level tracing to prevent double-tracing:

import { suppressProviderInstrumentation, isProviderInstrumentationSuppressed } from 'risicare';
 
suppressProviderInstrumentation(() => {
    // Provider patches are suppressed inside this scope
    await client.chat.completions.create(/* ... */);
});

Higher-Order Functions

agent

import { agent } from 'risicare';
 
agent<T extends (...args: any[]) => any>(
  options: {
    name?: string;
    role?: string;  // any string; canonical values are the AgentRole enum
    agentType?: string;
    version?: number;
    metadata?: Record<string, unknown>;
  },
  fn: T
): T

session

import { session } from 'risicare';
 
session<T extends (...args: any[]) => any>(
  optionsOrResolver: SessionOptions | ((...args: any[]) => SessionOptions),
  fn: T
): T
 
interface SessionOptions {
  sessionId: string;
  userId?: string;
}

Phase Functions

Phase decorators accept either a bare function or a name + function (added in 0.2.1):

These return a wrapped function — you must call it

traceThink / traceDecide / traceAct / traceObserve do not run your function. They return a wrapper, and a span is emitted only when you invoke that wrapper. Writing traceAct('execute-tool', fn); on its own emits zero spans — with no error and no warning, which reads as "tracing is broken" rather than "the wrapper was never called".

import { traceThink, traceDecide, traceAct, traceObserve } from 'risicare';
 
// Bare form — span named after the function
const analyzeBare = traceThink(async () => { /* ... */ });
 
// Named form — explicit span name
const analyze = traceThink('analyze-query', async () => { /* ... */ });
 
// All four phases support both forms
const choose = traceDecide('choose-action', async () => { /* ... */ });
const execute = traceAct('execute-tool', async () => { /* ... */ });
const check = traceObserve('check-result', async () => { /* ... */ });
 
// Nothing is traced until the wrappers are invoked:
await analyze();
await choose();
await execute();
await check();

Multi-Agent Functions

import { traceMessage, traceDelegate, traceCoordinate } from 'risicare';
 
traceMessage<T extends (...args: any[]) => any>(
  options: { to: string },
  fn: T
): T
 
traceDelegate<T extends (...args: any[]) => any>(
  options: { to: string },
  fn: T
): T
 
traceCoordinate<T extends (...args: any[]) => any>(
  options: { participants: string[] },
  fn: T
): T

Context Functions

getCurrentContext

import { getCurrentContext } from 'risicare';
 
// Returns a plain object snapshot. session/agent/span are either an
// object or null; phase is a SemanticPhase or null.
getCurrentContext(): {
  session: { sessionId: string; userId?: string } | null;
  agent: {
    agentId: string;
    agentName?: string;
    agentRole?: string;
    agentType?: string;
  } | null;
  span: { spanId: string; traceId: string } | null;
  phase: SemanticPhase | null;
}

Individual Getters

import {
  getCurrentTraceId,
  getCurrentSpanId,
  getCurrentAgentId,
  getCurrentSessionId,
} from 'risicare';
 
getCurrentTraceId(): string | undefined
getCurrentSpanId(): string | undefined
getCurrentAgentId(): string | undefined
getCurrentSessionId(): string | undefined

W3C Trace Context

import { injectTraceContext, extractTraceContext } from 'risicare';
 
injectTraceContext(headers: Record<string, string>): Record<string, string>
 
// Always returns a record (never undefined). When the incoming headers
// carry a Risicare tracestate it includes traceId, parentSpanId, flags,
// and any propagated sessionId / agentId.
extractTraceContext(headers: Record<string, string>): Record<string, string | undefined>

getTraceContext

Returns the current trace context as a serializable object. When called outside any active span it pre-allocates a root trace ID so the next span created in the same context inherits it.

import { getTraceContext } from 'risicare';
 
getTraceContext(): TraceContext   // { traceId, spanId, sessionId?, agentId? }

Context Scope Wrappers

Run a callback inside an explicit session, agent, or phase scope. Each returns the callback's result.

import { withSession, withAgent, withPhase } from 'risicare';
import { SemanticPhase } from 'risicare';
 
withSession(options: { sessionId: string; userId?: string }, fn: () => T): T
withAgent(options: { name?: string; role?: string; agentType?: string }, fn: () => T): T
withPhase(phase: SemanticPhase, fn: () => T): T

Webhook Verification

Verify the signature of an incoming Risicare webhook delivery before processing it. verifyWebhookSignature is a root export (import { verifyWebhookSignature } from 'risicare') — there is no risicare/webhooks subpath. It is dependency-light (Node crypto only) and ships in risicare@0.4.0.

import {
  verifyWebhookSignature,
  WebhookVerificationError,
  DEFAULT_TIMESTAMP_TOLERANCE_S,   // 300 (seconds)
} from 'risicare';
 
verifyWebhookSignature(
  payload: Uint8Array | ArrayBuffer | string,   // raw request body bytes
  headers: Headers | Map<string, string> | Record<string, string | string[] | undefined>,
  secret: string,
  opts?: { toleranceS?: number },               // default 300s skew window
): void   // returns undefined on success; throws on any failure

The verifier enforces three checks (all must pass): both X-Risicare-Signature and X-Risicare-Timestamp headers present and well-formed; timestamp within toleranceS of now (replay protection); and a constant-time HMAC-SHA256 match over the canonical <timestamp>.<payload_bytes> string. Any failure throws WebhookVerificationError — catch it specifically to return a 401.

Pass the raw body, not parsed JSON

The signature is computed over the EXACT bytes received. Parsing JSON and re-serializing changes key ordering and whitespace, so the signature will not match. Use your framework's raw-body option (e.g. express.raw()).

import express from 'express';
import { verifyWebhookSignature, WebhookVerificationError } from 'risicare';
 
const app = express();
app.post('/risicare-webhook', express.raw({ type: '*/*' }), (req, res) => {
  try {
    verifyWebhookSignature(req.body, req.headers, process.env.WEBHOOK_SECRET!);
  } catch (e) {
    if (e instanceof WebhookVerificationError) {
      return res.status(401).send(e.message);
    }
    throw e;
  }
  // ... process the verified event ...
  res.status(200).end();
});

Node runtime only

This verifier is synchronous and uses Node's crypto module. It does not run as-is on Cloudflare Workers / Vercel Edge / Deno Deploy. An async Web Crypto variant can be added on request.

Span Registry

import {
  registerSpan,
  getSpanById,
  unregisterSpan,
} from 'risicare';
 
registerSpan(span: Span, ttlMs?: number): void
getSpanById(spanId: string): Span | undefined
unregisterSpan(spanId: string): void

Types

RisicareConfig

interface RisicareConfig {
  apiKey?: string;
  projectId?: string;  // DEPRECATED — emits console.warn. Will be removed in v1.0.
  endpoint?: string;
  environment?: string;
  serviceName?: string;
  serviceVersion?: string;
  enabled?: boolean;
  traceContent?: boolean;
  compress?: boolean;
  sampleRate?: number;
  batchSize?: number;
  batchTimeoutMs?: number;
  maxQueueSize?: number;
  debug?: boolean;
  metadata?: Record<string, unknown>;
}

TraceContext

interface TraceContext {
  traceId: string;
  spanId: string;
  sessionId?: string;
  agentId?: string;
}

Span

class Span {
  traceId: string;
  spanId: string;
  name: string;
  kind: SpanKind;
  startTime: string;        // ISO 8601 timestamp
  endTime?: string;         // ISO 8601 timestamp, set on end()
  attributes: Record<string, unknown>;
  events: SpanEvent[];
  status: SpanStatus;
 
  setAttribute(key: string, value: unknown): this;
  addEvent(name: string, attributes?: Record<string, unknown>): this;
  setStatus(status: SpanStatus, message?: string): this;
  end(): void;
}

SpanKind

enum SpanKind {
  INTERNAL = 'internal',
  SERVER = 'server',
  CLIENT = 'client',
  PRODUCER = 'producer',
  CONSUMER = 'consumer',
  AGENT = 'agent',
  LLM_CALL = 'llm_call',
  TOOL_CALL = 'tool_call',
  RETRIEVAL = 'retrieval',
  DECISION = 'decision',
  DELEGATION = 'delegation',
  COORDINATION = 'coordination',
  MESSAGE = 'message',
  THINK = 'think',
  DECIDE = 'decide',
  OBSERVE = 'observe',
  REFLECT = 'reflect',
}

SpanStatus

enum SpanStatus {
  UNSET = 'unset',
  OK = 'ok',
  ERROR = 'error',
}

Next Steps