Skip to main content
The Adaline SDK is the recommended way to integrate your AI application with Adaline. Beyond sending traces and spans, the SDK gives you deployment management with automatic caching, smart buffering with batched flushes, built-in retries with exponential backoff, and health monitoring — all production-ready out of the box.

What the SDK gives you

CapabilityDescription
Deployment managementFetch deployed prompts by ID or environment, with automatic background refresh and local caching.
Structured observabilityLog traces and spans with 8 content types covering LLM calls, tool executions, retrieval, embeddings, guardrails, and custom logic.
Smart bufferingTraces and spans are buffered in memory and flushed in configurable batches — no request-per-span overhead.
Automatic retriesFailed API calls are retried with exponential backoff (5xx and network errors). 4xx errors fail immediately.
Health monitoringInspect flush status and background refresh health at runtime to detect issues before they become incidents.
Nested spansModel complex workflows with parent-child span hierarchies — agents calling tools calling sub-agents.
Session trackingGroup related traces by session ID to follow multi-turn conversations or multi-request workflows.
Evaluation variablesAttach variable values to spans so they flow into continuous evaluations and datasets automatically.

Install

npm install @adaline/client

Initialize the client

import { Adaline } from "@adaline/client";

const adaline = new Adaline({ apiKey: "your-api-key" });
Set the ADALINE_API_KEY environment variable and omit the apiKey parameter to avoid hardcoding secrets. The SDK reads from this variable automatically.

Manage deployments

The SDK can fetch deployed prompt configurations — including the model, provider settings, messages, tools, and variables — so your application always uses the latest version without redeploying code.

Fetch a specific deployment

const deployment = await adaline.getDeployment({
  promptId: "your-prompt-id",
  deploymentId: "your-deployment-id",
});

const config = deployment.prompt.config;
console.log(config.providerName);  // "openai"
console.log(config.model);         // "gpt-4"
console.log(config.settings);      // { temperature: 0.7, maxTokens: 1000 }

const messages = deployment.prompt.messages;
const tools = deployment.prompt.tools;
const variables = deployment.prompt.variables;

Fetch the latest deployment for an environment

const deployment = await adaline.getLatestDeployment({
  promptId: "your-prompt-id",
  deploymentEnvironmentId: "your-environment-id",
});

Auto-refresh deployments in production

For long-running services, use initLatestDeployment to set up a cached deployment that refreshes automatically (default every 60 seconds) in the background. When you deploy a new prompt version in Adaline, your application picks it up without a restart.
const controller = await adaline.initLatestDeployment({
  promptId: "your-prompt-id",
  deploymentEnvironmentId: "your-environment-id",
});

// Use the cached deployment in request handlers
const deployment = await controller.get();

// Force a fresh fetch (bypasses cache)
const fresh = await controller.get(true);

// Check background refresh health
const status = controller.backgroundStatus();
// { stopped: false, consecutiveFailures: 0, lastError: null, lastRefreshed: Date }

// Stop the background refresh when shutting down
controller.stop();

Initialize the monitor

The monitor manages the lifecycle of traces and spans — buffering them in memory, batching them together, and flushing them to the Adaline API on a timer or when the buffer fills up.
const monitor = adaline.initMonitor({ projectId: "your-project-id" });

Create a trace

A trace represents a single end-to-end request flow — for example, one user message that triggers an LLM call, a tool execution, and a final response.
const trace = monitor.logTrace({
  name: "user-request",
  status: "unknown",
  sessionId: "session-abc123",
  referenceId: "custom-uuid",
  tags: ["production", "v2.1"],
  attributes: {
    userId: "user-456",
    region: "us-east-1",
    featureFlag: "new-prompt-v2",
  },
});

Add spans

Each operation inside a trace is a span. Spans carry a content type that tells Adaline what kind of operation it represents — an LLM call, a tool execution, a vector retrieval, and more.

Create a span

const span = trace.logSpan({
  name: "chat-completion",
  promptId: "your-prompt-id",
  deploymentId: "your-deployment-id",
  runEvaluation: true,
  tags: ["v2.1", "production"],
  attributes: { region: "us-east-1", customerId: "dd2-cew3d2-saf32" },
});

Update and end a span

After performing the operation, update the span with the result and end it:
const response = await openai.chat.completions.create({ ... });

span.update({
  status: "success",
  content: {
    type: "Model",
    provider: "openai",
    model: "gpt-4",
    input: JSON.stringify({ messages }),
    output: JSON.stringify(response),
  },
});

span.end();

Nested spans

Spans can contain child spans to model hierarchical workflows — an agent span containing tool call spans, or a RAG span containing embedding and retrieval sub-spans:
const agentSpan = trace.logSpan({ name: "agent-orchestrator" });

const toolSpan = agentSpan.logSpan({ name: "search-tool" });
// ... execute tool ...
toolSpan.update({ status: "success", content: { type: "Tool", ... } });
toolSpan.end();

const llmSpan = agentSpan.logSpan({ name: "final-response" });
// ... generate response ...
llmSpan.update({ status: "success", content: { type: "Model", ... } });
llmSpan.end();

agentSpan.end();

Span content types

The content.type field tells Adaline what kind of operation a span represents. Each type carries input and output as JSON strings, plus type-specific fields.

Model

LLM chat completions and text generation. Captures the provider, model, cost, and optionally expected output for evaluation. For the best experience, stringify the exact request payload you send to your AI provider as input and the full response as output. When you use a supported provider, Adaline automatically extracts token usage, calculates cost, and surfaces model metadata. See Span content: input and output for full details and examples. You can also use Adaline’s own content schema for input and output, although this is more advanced and requires custom transformations.
span.update({
  status: "success",
  content: {
    type: "Model",
    provider: "openai",
    model: "gpt-4",
    input: JSON.stringify(params),
    output: JSON.stringify(response),
    cost: 0.0032, // optional, override the cost calculated by Adaline
  },
});

ModelStream

Streaming LLM responses. Captures both the raw stream chunks and an aggregated output.
span.update({
  status: "success",
  content: {
    type: "ModelStream",
    provider: "openai",
    model: "gpt-4",
    input: JSON.stringify({ messages }),
    output: rawStreamChunks,
    aggregateOutput: JSON.stringify(aggregatedResponse),
    cost: 0.0028, // optional, override the cost calculated by Adaline
  },
});

Tool

Function or tool call execution.
span.update({
  status: "success",
  content: {
    type: "Tool",
    input: JSON.stringify({ index: 0, id: "tool-123", name: "get-weather", arguments: JSON.stringify({ city: "San Francisco" }) }),
    output: JSON.stringify({ index: 0, id: "tool-123", name: "get-weather", data: JSON.stringify({ temperature: 62, condition: "foggy" }) }),
  },
});

Retrieval

Vector search, document retrieval, or any RAG retrieval step.
span.update({
  status: "success",
  content: {
    type: "Retrieval",
    input: JSON.stringify({ query: "password reset steps" }),
    output: JSON.stringify({ documents: [
      { id: "doc1", score: 0.95, text: "..." },
      { id: "doc2", score: 0.89, text: "..." }
    ] }),
  },
});

Embeddings

Embedding generation.
span.update({
  status: "success",
  content: {
    type: "Embeddings",
    input: JSON.stringify({ texts: ["How do I reset my password?"] }),
    output: JSON.stringify({ embeddings: [[0.012, -0.034, ...]] }),
  },
});

Function

Custom business logic, data transforms, or any application-specific operation.
span.update({
  status: "success",
  content: {
    type: "Function",
    input: JSON.stringify({ operation: "get-user-plan", userId: "user-456" }),
    output: JSON.stringify({ operation: "get-user-plan", userId: "user-456", plan: "enterprise", features: [...] }),
  },
});

Guardrail

Safety checks, content filters, PII detection, or compliance rules.
span.update({
  status: "success",
  content: {
    type: "Guardrail",
    input: JSON.stringify({ text: userMessage, checks: ["toxicity", "pii"] }),
    output: JSON.stringify({ text: userMessage, checks: ["toxicity", "pii"], passed: true, flags: [] }),
  },
});

Other

Any operation that doesn’t fit the types above.
span.update({
  status: "success",
  content: {
    type: "Other",
    input: JSON.stringify({ custom: "input" }),
    output: JSON.stringify({ custom: "output" }),
  },
});
All content types share type, input (JSON string), and output (JSON string).

End the trace and flush

Always end the trace and flush remaining data. Calling end() on a trace recursively ends all child spans that haven’t been ended yet.
trace.update({ status: "success" });
trace.end();

await monitor.flush();
Traces and spans that are never ended will never be flushed. Always call end() — use try/finally blocks to guarantee it runs even when errors occur.

Attach variables for evaluation

Attach variable values to spans so they flow into continuous evaluations and can be captured into datasets:
const span = trace.logSpan({
  name: "chat-completion",
  variables: {
    user_question: { modality: "text", value: userInput },
    context: { modality: "text", value: retrievedContext },
    product_image: {
      modality: "image",
      detail: "auto",
      value: { type: "url", url: "https://example.com/product.jpg" },
    },
  },
});
Variables support text, image, and pdf modalities. See Log attachments for full details on attaching variables, attributes, and tags.

Buffering, batching, and retries

The SDK handles reliability so you don’t have to.

How the buffer works

  1. When you call logTrace() or logSpan(), entries are added to an in-memory buffer.
  2. A background timer flushes the buffer every flushInterval seconds.
  3. If the buffer reaches maxBufferSize, it flushes immediately.
  4. Each flush sends a batch of entries in a single API call.

Retry behavior

ScenarioBehavior
5xx server errorsRetried with exponential backoff (1s initial, 10s max, 20s total budget).
Network errorsRetried with exponential backoff (same as 5xx).
4xx client errorsFail immediately — no retry. Check your API key and request payload.
Consecutive failuresAfter maxContinuousFlushFailures consecutive failures, the background timer stops.

Health monitoring

Inspect the monitor’s flush status at runtime to detect issues:
const status = monitor.flushStatus;
// {
//   stopped: false,
//   consecutiveFailures: 0,
//   lastError: null,
//   lastFlushed: Date
// }

const buffer = monitor.buffer;
const failedEntries = monitor.failedFlushEntries;

Graceful shutdown

In production, handle process signals to flush remaining data before the process exits:
Serverless environments (AWS Lambda, Vercel Functions, Cloudflare Workers, etc.): The SDK flushes buffered traces and spans on a background interval, but serverless functions can exit before the next flush fires. Always call await monitor.flush() (TypeScript) or await monitor.flush() (Python) explicitly before your handler returns to ensure nothing is lost.
process.on("SIGTERM", async () => {
  await monitor.flush();
  monitor.stop();
  controller.stop();
  process.exit(0);
});

Next steps

Advanced Tracing Patterns

Multi-step workflows, tool-calling agents, session tracking, and error handling patterns.

Log User Feedback

Attach thumbs up/down, ratings, and comments to traces.

Log Attachments

Attach attributes, tags, variables, and metadata to traces and spans.

SDK Reference

Complete class and type reference for the TypeScript and Python SDKs.