Observability
ThinkLang has optional OpenTelemetry integration. When @opentelemetry/api is installed, all core operations automatically emit spans.
Setup
bash
npm install @opentelemetry/api @opentelemetry/sdk-nodeNo code changes needed. ThinkLang detects @opentelemetry/api at runtime and wraps operations in spans.
Spans
| Operation | Span Name | Attributes |
|---|---|---|
think() | thinklang.think | thinklang.model, thinklang.input_tokens, thinklang.output_tokens, thinklang.duration_ms |
infer() | thinklang.infer | Same |
reason() | thinklang.reason | Same |
agent() | thinklang.agent | Same |
batch() | thinklang.batch | thinklang.batch_size, thinklang.batch_success, thinklang.batch_errors, thinklang.duration_ms |
Example
typescript
import { NodeSDK } from "@opentelemetry/sdk-node";
import { ConsoleSpanExporter } from "@opentelemetry/sdk-trace-node";
import { think, zodSchema } from "thinklang";
import { z } from "zod";
// Initialize OTel
const sdk = new NodeSDK({ traceExporter: new ConsoleSpanExporter() });
sdk.start();
// ThinkLang operations now emit spans automatically
const Sentiment = z.object({ label: z.string(), score: z.number() });
const result = await think({
prompt: "Analyze sentiment",
...zodSchema(Sentiment),
});
await sdk.shutdown();Programmatic API
You can also add custom attributes to the active span:
typescript
import { withSpan, addSpanAttributes } from "thinklang";
await withSpan("my.custom.span", { "my.attr": "value" }, async () => {
// your code here
addSpanAttributes({ "my.result": "success" });
return result;
});No-Op Behavior
When @opentelemetry/api is not installed, all telemetry functions are no-ops with zero overhead. You don't need to add conditional checks.