@error-bar/tracing 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @error-bar/tracing
2
+
3
+ **Standard OpenTelemetry, curated.** One install, one line, and your LLM traffic streams to [errorbar](https://platform.omnia-voice.com) — where you grade it, calibrate a judge against your own standards, and find out **with confidence intervals** whether a cheaper model holds up on your production traffic.
4
+
5
+ This SDK contains **no instrumentation code of its own**. It pins and configures the ecosystem's standard OpenTelemetry instrumentations — which gives it a property no other tracing SDK offers: **you can uninstall it without losing your instrumentation.** The identical setup in vanilla OTel is documented below; your spans are byte-for-byte the same either way, and nothing proprietary ever goes on the wire.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @error-bar/tracing
11
+ ```
12
+
13
+ ## Use
14
+
15
+ Call once at startup, **before constructing any LLM client**:
16
+
17
+ ```ts
18
+ import { setup } from "@error-bar/tracing";
19
+
20
+ const tracing = setup(); // reads ERRORBAR_API_KEY and ERRORBAR_TAG
21
+ ```
22
+
23
+ Pure-ESM app? Skip the code entirely — start Node with the loader hook so imports are intercepted before your app runs:
24
+
25
+ ```bash
26
+ node --import @error-bar/tracing/register app.mjs
27
+ ```
28
+
29
+ Short-lived scripts should `await tracing.shutdown()` before exit to flush pending spans; long-running servers can skip it. The `register` entrypoint flushes automatically when the process exits normally — no code needed.
30
+
31
+ ## What gets captured
32
+
33
+ OpenAI (v4–v7), Anthropic, LangChain, and Gemini (via the Vertex AI SDK) calls — automatically, and only for libraries actually installed. Successful calls, **streamed** calls (content aggregated across chunks), and **failed** calls (stored as ERROR trace structure — the most valuable signal there is, and the one status-code dashboards can't see).
34
+
35
+ Your inference does **not** move: requests keep going to your current provider; only trace telemetry flows to errorbar.
36
+
37
+ ## Configuration
38
+
39
+ | Env var | Meaning | Default |
40
+ | --- | --- | --- |
41
+ | `ERRORBAR_API_KEY` | errorbar API key — **required**; `setup()` throws rather than exporting nowhere silently | — |
42
+ | `ERRORBAR_TAG` | Population tag: one tag = one evaluation population in errorbar | unset |
43
+ | `ERRORBAR_OTLP_ENDPOINT` | OTLP/HTTP traces endpoint | `https://gateway.errorbar.ai/v1/traces` |
44
+ | `OTEL_SERVICE_NAME` | Standard OTel service name | unset |
45
+
46
+ All options can also be passed to `setup()` directly; explicit options beat env vars.
47
+
48
+ ## The eject guarantee
49
+
50
+ Remove this package and wire the same standard pieces yourself — identical spans, same endpoint, nothing lost:
51
+
52
+ ```ts
53
+ import { NodeSDK } from "@opentelemetry/sdk-node";
54
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
55
+ import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
56
+ import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
57
+ import { LangChainInstrumentation } from "@traceloop/instrumentation-langchain";
58
+
59
+ process.env.OTEL_RESOURCE_ATTRIBUTES = "omnia.tag=my-agent";
60
+
61
+ new NodeSDK({
62
+ serviceName: "my-service",
63
+ traceExporter: new OTLPTraceExporter({
64
+ url: "https://gateway.errorbar.ai/v1/traces",
65
+ headers: { Authorization: `Bearer ${process.env.ERRORBAR_API_KEY}` },
66
+ }),
67
+ instrumentations: [
68
+ new OpenAIInstrumentation(),
69
+ new AnthropicInstrumentation(),
70
+ new LangChainInstrumentation(),
71
+ ],
72
+ }).start();
73
+ ```
74
+
75
+ Already emitting OpenTelemetry (Vercel AI SDK telemetry, an existing OTel setup)? You don't need this package at all — three env vars point your existing exporter at errorbar. See the [OTLP ingest reference](https://docs.omnia-voice.com/reference/otlp-ingest).
76
+
77
+ ## Privacy
78
+
79
+ Span **structure** is always stored. Model-call **content** (prompts/completions) is stored only if your errorbar workspace has request logging enabled, under your retention window, with the same scrubbing and size caps as gateway traffic.
80
+
81
+ ## Verify your setup — get a receipt, not a hope
82
+
83
+ ```bash
84
+ ERRORBAR_API_KEY=sk_... sh -c "$(curl -fsSL https://platform.omnia-voice.com/setup.sh)"
85
+ ```
86
+
87
+ Proves the key works, confirms traces are actually landing, and names your one next step. Instrumentation that fails silently is the industry default; this is the alternative.
88
+
89
+ ## Links
90
+
91
+ - [Docs](https://docs.omnia-voice.com/reference/tracing-sdk) · [Platform](https://platform.omnia-voice.com) · [Python package](https://pypi.org/project/omnia-tracing/)
92
+
93
+ Apache-2.0
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Pure configuration assembly — separated from setup() so every decision the
3
+ * SDK makes is unit-testable without starting an OpenTelemetry pipeline.
4
+ *
5
+ * This package deliberately contains NO instrumentation code. It pins and
6
+ * configures standard, ecosystem-maintained OpenTelemetry pieces. The same
7
+ * setup expressed without this package is documented in docs/eject.md — eject
8
+ * anytime; your spans do not change.
9
+ */
10
+ export declare const DEFAULT_ENDPOINT = "https://gateway.errorbar.ai/v1/traces";
11
+ /** Resource attribute that names the traffic population in errorbar. */
12
+ export declare const TAG_ATTRIBUTE = "omnia.tag";
13
+ export interface SetupOptions {
14
+ /** errorbar API key. Default: ERRORBAR_API_KEY env var (OMNIA_API_KEY still honoured). Required — setup throws
15
+ * rather than exporting nowhere silently. */
16
+ apiKey?: string;
17
+ /** Population tag (becomes the `omnia.tag` resource attribute — one tag =
18
+ * one gradeable population). Default: ERRORBAR_TAG env var (OMNIA_TAG still honoured). */
19
+ tag?: string;
20
+ /** Service name on the resource. Default: OTEL_SERVICE_NAME env var. */
21
+ serviceName?: string;
22
+ /** OTLP/HTTP traces endpoint. Default: ERRORBAR_OTLP_ENDPOINT env var (OMNIA_OTLP_ENDPOINT still honoured), else
23
+ * the errorbar gateway. Point it elsewhere and this package exports to any
24
+ * OTLP receiver — there is nothing Omnia-specific on the wire. */
25
+ endpoint?: string;
26
+ }
27
+ export interface ResolvedConfig {
28
+ endpoint: string;
29
+ headers: {
30
+ Authorization: string;
31
+ };
32
+ serviceName: string | undefined;
33
+ /** Attributes merged into OTEL_RESOURCE_ATTRIBUTES semantics. */
34
+ resourceAttributes: Record<string, string>;
35
+ }
36
+ export declare function resolveConfig(opts?: SetupOptions, env?: NodeJS.ProcessEnv): ResolvedConfig;
package/dist/config.js ADDED
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * Pure configuration assembly — separated from setup() so every decision the
4
+ * SDK makes is unit-testable without starting an OpenTelemetry pipeline.
5
+ *
6
+ * This package deliberately contains NO instrumentation code. It pins and
7
+ * configures standard, ecosystem-maintained OpenTelemetry pieces. The same
8
+ * setup expressed without this package is documented in docs/eject.md — eject
9
+ * anytime; your spans do not change.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TAG_ATTRIBUTE = exports.DEFAULT_ENDPOINT = void 0;
13
+ exports.resolveConfig = resolveConfig;
14
+ exports.DEFAULT_ENDPOINT = "https://gateway.errorbar.ai/v1/traces";
15
+ /** Resource attribute that names the traffic population in errorbar. */
16
+ exports.TAG_ATTRIBUTE = "omnia.tag";
17
+ function resolveConfig(opts = {}, env = process.env) {
18
+ const apiKey = opts.apiKey ?? env.ERRORBAR_API_KEY ?? env.OMNIA_API_KEY;
19
+ if (!apiKey) {
20
+ throw new Error("@error-bar/tracing: no API key. Pass setup({ apiKey }) or set ERRORBAR_API_KEY (OMNIA_API_KEY still works). " +
21
+ "Refusing to start a tracer that exports nowhere.");
22
+ }
23
+ const tag = opts.tag ?? env.ERRORBAR_TAG ?? env.OMNIA_TAG;
24
+ const resourceAttributes = {};
25
+ if (tag)
26
+ resourceAttributes[exports.TAG_ATTRIBUTE] = tag;
27
+ return {
28
+ endpoint: opts.endpoint ??
29
+ env.ERRORBAR_OTLP_ENDPOINT ??
30
+ env.OMNIA_OTLP_ENDPOINT ??
31
+ exports.DEFAULT_ENDPOINT,
32
+ headers: { Authorization: `Bearer ${apiKey}` },
33
+ serviceName: opts.serviceName ?? env.OTEL_SERVICE_NAME,
34
+ resourceAttributes,
35
+ };
36
+ }
@@ -0,0 +1,46 @@
1
+ import type { Instrumentation, InstrumentationNodeModuleDefinition } from "@opentelemetry/instrumentation";
2
+ import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
3
+ import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
4
+ import { LangChainInstrumentation } from "@traceloop/instrumentation-langchain";
5
+ import { VertexAIInstrumentation } from "@traceloop/instrumentation-vertexai";
6
+ import { type SetupOptions } from "./config";
7
+ /**
8
+ * Upstream pins openai support at ">=4 <7", but v7 kept the exact public
9
+ * class surface the patch wraps (Chat.Completions / Completions / Responses /
10
+ * Images — verified by live drill 2026-08-21, spans + content landed).
11
+ * Widen to <8 until upstream catches up; the weekly unpinned-upstream CI
12
+ * canary re-checks this assumption so the widening can't silently rot.
13
+ */
14
+ export declare class OpenAIInstrumentationWide extends OpenAIInstrumentation {
15
+ protected init(): InstrumentationNodeModuleDefinition;
16
+ }
17
+ export { resolveConfig, DEFAULT_ENDPOINT, TAG_ATTRIBUTE } from "./config";
18
+ export type { SetupOptions, ResolvedConfig } from "./config";
19
+ export interface Tracing {
20
+ /** Flush pending spans and stop. Call before process exit in short-lived
21
+ * scripts; long-running servers can skip it. */
22
+ shutdown(): Promise<void>;
23
+ /** The live instrumentation instances — escape hatch for ESM setups where
24
+ * auto-patching can't intercept an already-imported client:
25
+ * `tracing.instrumentations.openai.manuallyInstrument(client)`. */
26
+ instrumentations: {
27
+ openai: OpenAIInstrumentation;
28
+ anthropic: AnthropicInstrumentation;
29
+ langchain: LangChainInstrumentation;
30
+ /** Gemini via the Vertex AI SDK (@google-cloud/vertexai). */
31
+ vertexai: VertexAIInstrumentation;
32
+ };
33
+ }
34
+ /**
35
+ * Start standard OpenTelemetry tracing, exporting to errorbar.
36
+ *
37
+ * Call ONCE, before constructing any LLM client (the instrumentations patch
38
+ * module loading, so clients created earlier are not captured — use
39
+ * `tracing.instrumentations.<lib>.manuallyInstrument(client)` for those).
40
+ *
41
+ * Everything this function does is standard OTel configuration; see
42
+ * docs/eject.md for the identical setup without this package.
43
+ */
44
+ export declare function setup(opts?: SetupOptions & {
45
+ instrumentations?: Instrumentation[];
46
+ }): Tracing;
package/dist/index.js ADDED
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TAG_ATTRIBUTE = exports.DEFAULT_ENDPOINT = exports.resolveConfig = exports.OpenAIInstrumentationWide = void 0;
4
+ exports.setup = setup;
5
+ const sdk_node_1 = require("@opentelemetry/sdk-node");
6
+ const exporter_trace_otlp_proto_1 = require("@opentelemetry/exporter-trace-otlp-proto");
7
+ const instrumentation_openai_1 = require("@traceloop/instrumentation-openai");
8
+ const instrumentation_anthropic_1 = require("@traceloop/instrumentation-anthropic");
9
+ const instrumentation_langchain_1 = require("@traceloop/instrumentation-langchain");
10
+ const instrumentation_vertexai_1 = require("@traceloop/instrumentation-vertexai");
11
+ const config_1 = require("./config");
12
+ /**
13
+ * Upstream pins openai support at ">=4 <7", but v7 kept the exact public
14
+ * class surface the patch wraps (Chat.Completions / Completions / Responses /
15
+ * Images — verified by live drill 2026-08-21, spans + content landed).
16
+ * Widen to <8 until upstream catches up; the weekly unpinned-upstream CI
17
+ * canary re-checks this assumption so the widening can't silently rot.
18
+ */
19
+ class OpenAIInstrumentationWide extends instrumentation_openai_1.OpenAIInstrumentation {
20
+ init() {
21
+ const def = super.init();
22
+ if (def.name === "openai")
23
+ def.supportedVersions = [">=4 <8"];
24
+ return def;
25
+ }
26
+ }
27
+ exports.OpenAIInstrumentationWide = OpenAIInstrumentationWide;
28
+ var config_2 = require("./config");
29
+ Object.defineProperty(exports, "resolveConfig", { enumerable: true, get: function () { return config_2.resolveConfig; } });
30
+ Object.defineProperty(exports, "DEFAULT_ENDPOINT", { enumerable: true, get: function () { return config_2.DEFAULT_ENDPOINT; } });
31
+ Object.defineProperty(exports, "TAG_ATTRIBUTE", { enumerable: true, get: function () { return config_2.TAG_ATTRIBUTE; } });
32
+ /**
33
+ * Start standard OpenTelemetry tracing, exporting to errorbar.
34
+ *
35
+ * Call ONCE, before constructing any LLM client (the instrumentations patch
36
+ * module loading, so clients created earlier are not captured — use
37
+ * `tracing.instrumentations.<lib>.manuallyInstrument(client)` for those).
38
+ *
39
+ * Everything this function does is standard OTel configuration; see
40
+ * docs/eject.md for the identical setup without this package.
41
+ */
42
+ function setup(opts = {}) {
43
+ const config = (0, config_1.resolveConfig)(opts);
44
+ const openai = new OpenAIInstrumentationWide();
45
+ const anthropic = new instrumentation_anthropic_1.AnthropicInstrumentation();
46
+ const langchain = new instrumentation_langchain_1.LangChainInstrumentation();
47
+ const vertexai = new instrumentation_vertexai_1.VertexAIInstrumentation();
48
+ // The population tag rides the standard resource-attributes env var so the
49
+ // resource pipeline stays 100% stock OTel (no Resource construction here —
50
+ // that API has churned across SDK majors; the env contract hasn't).
51
+ const tagPairs = Object.entries(config.resourceAttributes)
52
+ .map(([k, v]) => `${k}=${v}`)
53
+ .join(",");
54
+ if (tagPairs) {
55
+ process.env.OTEL_RESOURCE_ATTRIBUTES = process.env.OTEL_RESOURCE_ATTRIBUTES
56
+ ? `${process.env.OTEL_RESOURCE_ATTRIBUTES},${tagPairs}`
57
+ : tagPairs;
58
+ }
59
+ const sdk = new sdk_node_1.NodeSDK({
60
+ serviceName: config.serviceName,
61
+ traceExporter: new exporter_trace_otlp_proto_1.OTLPTraceExporter({
62
+ url: config.endpoint,
63
+ headers: config.headers,
64
+ }),
65
+ instrumentations: [
66
+ openai,
67
+ anthropic,
68
+ langchain,
69
+ vertexai,
70
+ ...(opts.instrumentations ?? []),
71
+ ],
72
+ });
73
+ sdk.start();
74
+ return {
75
+ shutdown: () => sdk.shutdown(),
76
+ instrumentations: { openai, anthropic, langchain, vertexai },
77
+ };
78
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@error-bar/tracing",
3
+ "version": "0.2.0",
4
+ "description": "errorbar tracing \u2014 standard OpenTelemetry, curated. One install, one line; eject anytime, your spans don't change.",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/omnia-v/omnia-tracing.git",
9
+ "directory": "packages/typescript"
10
+ },
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "files": [
14
+ "dist",
15
+ "register.mjs",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "test": "vitest run"
21
+ },
22
+ "engines": {
23
+ "node": ">=20"
24
+ },
25
+ "dependencies": {
26
+ "@opentelemetry/api": "^1.9.0",
27
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.221.0",
28
+ "@opentelemetry/instrumentation": "^0.203.0",
29
+ "@opentelemetry/sdk-node": "^0.221.0",
30
+ "@traceloop/instrumentation-anthropic": "^0.27.0",
31
+ "@traceloop/instrumentation-langchain": "^0.27.0",
32
+ "@traceloop/instrumentation-openai": "^0.27.0",
33
+ "@traceloop/instrumentation-vertexai": "^0.27.0"
34
+ },
35
+ "devDependencies": {
36
+ "openai": "^7.0.0",
37
+ "typescript": "^5.6.0",
38
+ "vitest": "^3.0.0"
39
+ },
40
+ "exports": {
41
+ ".": {
42
+ "types": "./dist/index.d.ts",
43
+ "default": "./dist/index.js"
44
+ },
45
+ "./register": "./register.mjs"
46
+ }
47
+ }
package/register.mjs ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ESM auto-instrumentation entrypoint:
3
+ *
4
+ * node --import @error-bar/tracing/register app.mjs
5
+ *
6
+ * Pure-ESM apps import their LLM clients before any runtime call could patch
7
+ * them, so interception has to happen at the module loader. This registers
8
+ * OpenTelemetry's import-in-the-middle hook FIRST, then starts setup() from
9
+ * env (ERRORBAR_API_KEY, ERRORBAR_TAG, ERRORBAR_OTLP_ENDPOINT, OTEL_SERVICE_NAME; the OMNIA_* names still work).
10
+ *
11
+ * CommonJS apps don't need this file — calling setup() before creating
12
+ * clients is enough.
13
+ */
14
+ import { register, createRequire } from "node:module";
15
+ import { pathToFileURL } from "node:url";
16
+
17
+ const require = createRequire(import.meta.url);
18
+
19
+ // Register the hook from the SAME @opentelemetry/instrumentation copy the
20
+ // instrumentations extend. With two copies in the tree (ours vs the one
21
+ // nested under @traceloop/*), the loader hook and the instrumentations'
22
+ // Hook class hold separate registries and ESM interception silently
23
+ // no-ops — the exact bug this resolution path prevents.
24
+ const hookPath = require.resolve("@opentelemetry/instrumentation/hook.mjs", {
25
+ paths: [require.resolve("@traceloop/instrumentation-openai/package.json")],
26
+ });
27
+ register(pathToFileURL(hookPath).href, import.meta.url);
28
+
29
+ const { setup } = require("./dist/index.js");
30
+ const tracing = setup();
31
+
32
+ // This entrypoint keeps the only shutdown handle, so it owns the flush: the
33
+ // batch exporter holds spans up to 5s, and a short-lived script exits before
34
+ // that timer ever fires — dropping its spans silently (drilled 2026-08-22).
35
+ // beforeExit fires when the event loop drains; the async flush schedules
36
+ // work, which keeps the process alive until the export completes. Does not
37
+ // fire on process.exit() or fatal signals — servers killed by SIGTERM lose
38
+ // at most the final 5s window, same as any OTel batch exporter.
39
+ let flushed = false;
40
+ process.once("beforeExit", () => {
41
+ if (flushed) return;
42
+ flushed = true;
43
+ tracing.shutdown().catch(() => {});
44
+ });