Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,69 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.6.0] - 2026-07-17

### Added

- **Reparent Children of Blocked Root Instruments**: When a root-instrument span is blocked, its children are no longer dropped along with the subtree. Instead they are reparented onto the blocked span's parent (a true root's children become the new roots), and the peel repeats for promoted spans that are themselves blocked.
### Changed

- **Standardized Attribute Serialization**: Introduced a single `SerializationSpanProcessor` that serializes and truncates every span attribute at write time, replacing the removed `AttributeSizeLimitProcessor`. Truncation uses a `jsonrepair`-based serializer for JSON values (and a `...[TRUNCATED]` suffix for plain strings) to stay aligned with backend behavior, and runs before the scrubbing layer so the regex scrubber sees values in string form. The per-attribute cap is read lazily from `NETRA_SPAN_ATTRIBUTE_MAX_SIZE` (default 30000). Serialization helpers were consolidated under `src/utils/serialization/`.

### Fixed

- **`addConversation` Persistence**: Fixed appending conversation entries to the active span's `conversation` attribute so existing entries are parsed and preserved (rather than overwritten) when a new entry is added, with defensive fallback when the stored value cannot be parsed.

## [1.5.0] - 2026-07-10

### Added

- **Google GenAI Instrumentation**: Added instrumentation support for the Google GenAI (`@google/genai`) SDK, with a shared `base-instrumentor` abstraction and a separate `google-generative-ai` module.

### Fixed

- **OpenAI Agents Cache Tokens**: Captured cache token details (`cached_tokens`, `cache_write_tokens`, `reasoning_tokens`) for Claude models in OpenAI Agents instrumentation.
- **Raw Chat Completions Response**: Added a helper to handle usage from the Chat Completions API naming (`prompt_tokens`/`completion_tokens` and their token details) alongside the Responses API naming, so `OpenAIChatCompletionsModel` usage is captured correctly.
- **Span Input/Output on Active and Root Spans**: Updated utility functions so input and output attributes are set correctly on both the active span and the root span.

## [1.4.0] - 2026-07-02

### Added

- **Anthropic Tool Runner Support**: Added span capture for the Anthropic tool-runner flow, so tool-execution turns within an agentic Anthropic run are traced.
- **Attribute Size Limit Processor**: New `AttributeSizeLimitProcessor` enforces a hard per-attribute size cap (default 32KB, configurable via `NETRA_SPAN_ATTRIBUTE_MAX_SIZE`) by wrapping `setAttribute` on span start, preventing "entity too large" errors during export.
- **Resource Attributes Propagation**: `Config` now sets `OTEL_RESOURCE_ATTRIBUTES` so the `TracerProvider` resource carries `service.name` and `deployment.environment`, with precedence order: pre-existing env value > config `resourceAttributes` > Netra defaults.

### Changed

- **SDK Version Source**: `Config.LIBRARY_VERSION` now derives from `SDK_VERSION` in `src/version.ts` instead of a hardcoded string, keeping the reported library version in sync with the package version.

## [1.3.0] - 2026-07-01

### Added

- **NativeTracingMode**: Replaced the boolean `disableNativeTracing` option with a `NativeTracingMode` (`"both" | "netra" | "netra-strict"`) for granular control over where OpenAI Agents traces are sent. Configurable per-instrument or via the `NATIVE_TRACING_MODE` environment variable. `"netra"` routes traces to Netra only (falling back to additive mode with a warning if SDK APIs are unavailable), `"netra-strict"` (default) never falls back to native, and `"both"` sends to Netra and native.

### Fixed

- **Netra-only OpenAI Agents Tracing**: Added `canSet` gating and a restore fallback so the OpenAI Agents processor can be swapped safely, and included `OPENAI_AGENTS` in the root-span instrument allowlist (`DEFAULT_INSTRUMENTS_FOR_ROOT`) so agent runs are captured as root spans.

## [1.2.0] - 2026-06-23

### Added

- **Simulation File Handling**: The simulation workflow now supports file attachments on user messages. The SDK parses `attachments` metadata from the API (`FileData`), downloads the referenced content via pre-signed URLs, and delivers it to user tasks as base64-encoded `ProcessedFile` objects. `ProcessedFile` is now exported from the package root.

## [1.1.0] - 2026-06-16

### Added

- **Context Propagation Helpers**: Exported `netraExpressMiddleware` and `runWithExtractedContext` for distributed tracing. These utilities extract incoming W3C Trace Context from HTTP headers and run code within that context, covering cases where auto-instrumentation is unavailable (ESM load-order issues, missing peer dependencies, or non-Express frameworks).

### Fixed

- **CommonJS (CJS) Support for LLM Provider Instrumentations**: Fixed instrumentation for Anthropic, Google GenAI, Groq, and Mistral AI so they patch correctly under CommonJS module resolution, resolving instrumentation failures in CJS applications.

## [1.5.0] - 2026-07-10

### Added
Expand Down
27 changes: 12 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "netra-sdk",
"version": "1.5.0",
"version": "1.6.0",
"description": "A comprehensive TypeScript/JavaScript SDK for AI application observability built on top of OpenTelemetry and Traceloop",
"type": "module",
"main": "./dist/index.cjs",
Expand Down Expand Up @@ -50,33 +50,33 @@
"dependencies": {
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/core": "^2.0.1",
"@opentelemetry/instrumentation-undici": "^0.6.0",
"@opentelemetry/instrumentation-express": "^0.67.0",
"@opentelemetry/instrumentation-http": "^0.219.0",
"@opentelemetry/instrumentation-undici": "^0.6.0",
"@opentelemetry/otlp-transformer": "^0.211.0",
"@opentelemetry/resources": "^2.0.1",
"@opentelemetry/sdk-trace-base": "^2.0.1",
"@traceloop/node-server-sdk": "^0.22.5",
"axios": "^1.13.4",
"dotenv": "^17.2.3",
"jsonrepair": "^3.15.0",
"p-limit": "^7.2.0",
"shimmer": "^1.2.1"
},
"peerDependencies": {
"@anthropic-ai/sdk": ">=0.60.0",
"@google/genai": ">=1.0.0",
"@google/generative-ai": "^0.24.1",
"@langchain/langgraph": "^1.1.1",
"@langchain/ollama": "^1.2.1",
"@mistralai/mistralai": ">=1.0.0",
"@google/generative-ai": "^0.24.1",
"@google/genai": ">=1.0.0",
"@openai/agents": ">=0.10.0 <1.0.0",
"@opentelemetry/instrumentation": ">=0.53.0",
"@opentelemetry/instrumentation-express": "^0.67.0",
"@opentelemetry/instrumentation-http": "^0.219.0",
"@opentelemetry/instrumentation-undici": "^0.6.0",
"@prisma/instrumentation": "^5.0.0",
"groq-sdk": ">=0.3.0",
"openai": "^4.0.0 || ^5.0.0 || ^6.0.0",
"@anthropic-ai/sdk": ">=0.60.0",
"@openai/agents": ">=0.10.0 <1.0.0"
"openai": "^4.0.0 || ^5.0.0 || ^6.0.0"
},
"peerDependenciesMeta": {
"@opentelemetry/instrumentation": {
Expand Down
45 changes: 36 additions & 9 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@ export interface NetraConfig {
blockedSpans?: string[];
instruments?: Set<NetraInstruments>;
blockInstruments?: Set<NetraInstruments>;
/**
* Set of instruments allowed to produce root-level spans. Independent of
* `instruments`. Defaults to `DEFAULT_INSTRUMENTS_FOR_ROOT` when omitted.
*
* When a root span comes from an instrumentation outside this set, only that
* span is dropped — its children are reparented onto its parent (a true
* root's children become the new roots). The peel then repeats: if a promoted
* child is itself from a non-root instrumentation it is dropped too,
* recursively, until a surviving span is reached (an allowed instrument, a
* Netra decorator / manual span, or any non-instrumentation span). This keeps
* LLM/vector spans that originate under an unwanted server root (e.g.
* Express/HTTP) without emitting the server span itself. A span whose parent
* link is remote (cross-process) is kept as a root so an upstream distributed
* trace is not severed.
*
* Note: when `enableRootSpan` is true, Netra attaches its own root span and
* every auto-instrumentation span becomes its child, so no reparenting
* occurs. Pass a set containing `NetraInstruments.ALL` to allow all
* instrumentations to produce root spans (legacy behaviour).
*/
rootInstruments?: Set<NetraInstruments>;
}

Expand Down Expand Up @@ -106,15 +126,22 @@ export class Config {
static readonly LIBRARY_NAME = "netra";
static readonly LIBRARY_VERSION = SDK_VERSION;
static readonly TRIAL_BLOCK_DURATION_SECONDS = 900; // 15 minutes
static readonly ATTRIBUTE_MAX_LEN = parseInt(
process.env.NETRA_ATTRIBUTE_MAX_LEN || "50000",
);
static readonly CONVERSATION_MAX_LEN = parseInt(
process.env.NETRA_CONVERSATION_CONTENT_MAX_LEN || "50000",
);
static readonly SPAN_ATTRIBUTE_MAX_SIZE = parseInt(
process.env.NETRA_SPAN_ATTRIBUTE_MAX_SIZE || "30000",
);

private static _spanAttributeMaxSize: number | undefined;

/**
* Lazily reads NETRA_SPAN_ATTRIBUTE_MAX_SIZE from process.env on first
* access so that env vars set before Netra.init() (via dotenv, shell
* export, Docker, etc.) are always respected regardless of import order.
*/
static get SPAN_ATTRIBUTE_MAX_SIZE(): number {
if (Config._spanAttributeMaxSize === undefined) {
Config._spanAttributeMaxSize = parseInt(
process.env.NETRA_SPAN_ATTRIBUTE_MAX_SIZE || "30000",
);
}
return Config._spanAttributeMaxSize;
}

appName: string;
otlpEndpoint?: string;
Expand Down
6 changes: 3 additions & 3 deletions src/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Logger } from "./logger";
import { SessionManager } from "./session-manager";
import { SpanType, DecoratorOptions } from "./types";
import { wrapResponse } from "./utils/response-handler";
import { safeStringify, serializeValue } from "./utils/serialization";
import { safeStringify } from "./utils/serialization";

type AnyFunction = (...args: any[]) => any;
type AsyncFunction = (...args: any[]) => Promise<any>;
Expand Down Expand Up @@ -41,15 +41,15 @@ function spanHasOutput(span: Span): boolean {
function addInputAttributes(span: Span, args: any[], entityType: string): void {
span.setAttribute(`${Config.LIBRARY_NAME}.entity.type`, entityType);
if (args.length > 0) {
span.setAttribute("input", safeStringify(args, Config.ATTRIBUTE_MAX_LEN));
span.setAttribute("input", safeStringify(args));
}
}

function addOutputAttributes(span: Span, result: any): void {
// Skip if the user already set output explicitly inside the decorated function
if (result === undefined || spanHasOutput(span)) return;
try {
span.setAttribute("output", serializeValue(result, Config.ATTRIBUTE_MAX_LEN));
span.setAttribute("output", safeStringify(result));
} catch (e) {
span.setAttribute("output_error", String(e));
}
Expand Down
Loading