JSPM

@graphorin/observability

0.7.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 819
  • Score
    100M100P100Q89400F
  • License MIT

Observability primitives for the Graphorin framework: typed AISpan tracer over OpenTelemetry, mandatory withValidation wrapper for every exporter, sensitivity-aware RedactionValidator with built-in PII / secret patterns, GenAI Semantic Conventions conformance helpers, OpenInference span-kind emission, structured logger with span correlation, append-only JSONL exporter for replay, sanitized replay primitives, hierarchical CostTracker, and a minimal inline eval runner alongside a zero-default telemetry stub.

Package Exports

  • @graphorin/observability
  • @graphorin/observability/cost
  • @graphorin/observability/eval
  • @graphorin/observability/exporters
  • @graphorin/observability/gen-ai
  • @graphorin/observability/logger
  • @graphorin/observability/openinference
  • @graphorin/observability/package.json
  • @graphorin/observability/redaction
  • @graphorin/observability/redaction/imperative-patterns
  • @graphorin/observability/redaction/patterns
  • @graphorin/observability/replay
  • @graphorin/observability/telemetry
  • @graphorin/observability/tracer

Readme

@graphorin/observability

Observability primitives for the Graphorin framework.

@graphorin/observability ships every cross-cutting tracing, redaction, replay, cost, and structured-logging primitive every other @graphorin/* package builds on. The package is intentionally opinionated: every exporter must be wrapped through withValidation(...) before it can ship a single span, the validator defaults to default-deny non-public, and the framework makes zero outbound network calls without an explicit user action.

Highlights

  • Typed AISpan<T> tracer. createTracer({...}) returns a GraphorinTracer that emits AISpan<SpanType> records compatible with OpenTelemetry. Sampling rules + per-event sampling cover noisy span kinds (memory.embed, tool.execute.partial, …).

  • Mandatory withValidation(...) wrapper. Every exporter passed to createTracer({ exporters }) is wrapped through the configured RedactionValidator. Registering a raw exporter while validation: 'off' triggers UnvalidatedExporterError at startup - there is no silent path.

  • RedactionValidator with 14 built-in patterns. API key / JWT / PEM private key / GitHub PAT / AWS access key / Graphorin token / bearer header / basic-auth header / email / credit card / US SSN / E.164 phone / IBAN - all on by default. Three additional patterns (IPv4, IPv6, GCP service account) are opt-in.

  • OpenTelemetry GenAI semantic-conventions conformance. emitGenAIAttributes(span, {...}), emitGenAIMessageEvents(span, [...]), and deriveGenAISystem(...) ship the canonical gen_ai.* attribute family alongside the existing graphorin.* attributes - additive, never replacing. Span names follow the semconv {operation} {target} shape (chat <model>, execute_tool <tool>, invoke_agent <agent>) via the exported spanNameFor(type, attrs) helper, applied by the tracer automatically.

  • OpenInference span-kind layer. emitOpenInferenceKind(span) emits the openinference.span.kind attribute via the canonical per-SpanType mapping (agent.*AGENT, provider.*LLM, tool.executeTOOL, memory.*RETRIEVER / EMBEDDING, workflow.*CHAIN, agent.evaluator.iterationEVALUATOR).

  • ConsoleExporter, JSONLExporter, OTLPHttpExporter. The built-in exporters cover dev (console), replay (append-only JSONL with 0700 directories + 0600 files), and remote OTLP collectors (fetch-based reference implementation, swappable fetchImpl).

  • Sanitized-by-default Replay. createReplay({...}) exposes a run(...) async iterator that yields replay.start / replay.event / replay.skipped / replay.end markers. Raw mode requires the canReadRaw callback to return true and emits an audit-bridge entry on every invocation.

  • Hierarchical CostTracker. createCostTracker({...}) rolls up tokens + cost (including the prompt-cache read/write legs) across parent-child spans and supports per-run / per-session / per-agent / per-user budgets with an onExceed callback. Internal maps are bounded by default (retention: { maxSpanEntries, maxScopeEntries }, 10k each; oldest-first eviction with an onEviction observer - usage() for an evicted id reports zero; pass retention: false for the old unbounded behaviour). Feed it from the provider middleware with the shipped bridge:

    import { costTrackerUsageDelegate, createCostTracker } from '@graphorin/observability';
    import { withCostTracking } from '@graphorin/provider';
    
    const tracker = createCostTracker({ budgets: { perSession: 5 } });
    const provider = withCostTracking(base, {
      priceLookup: () => ({ inputPerMtok: 3, outputPerMtok: 15 }),
      onUsage: costTrackerUsageDelegate(tracker, () => ({
        spanId: currentSpanId(),
        sessionId: currentSessionId(),
      })),
    });
    // Later: tracker.usage('session', sessionId).cost
  • Structured logger. createLogger({...}) writes JSON or pretty records, automatically correlates with the current span via withCurrentSpan(...), and pipes every field through the validator.

  • Zero-default telemetry. getTelemetryStatus() and announceTelemetryPosture() expose the v0.1 promise: the framework performs zero outbound network calls, no version pings, no crash reports, no auto-updates. The GRAPHORIN_TELEMETRY and GRAPHORIN_NO_PHONE_HOME environment variables are reserved for forward compatibility and are acknowledged at startup.

Installation

pnpm add @graphorin/observability
# Optional peer deps for OTLP export - install only when you need them:
pnpm add @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http

Quick start

import {
  createTracer,
  createConsoleExporter,
  createJSONLExporter,
  withValidation,
} from '@graphorin/observability';

const tracer = createTracer({
  serviceName: 'my-assistant',
  exporters: [
    // Auto-wrapped via the tracer-managed validator.
    createConsoleExporter({ pretty: true }),
    // Manually wrapped - useful when each exporter needs its own policy.
    withValidation(createJSONLExporter({ path: './traces' }), {
      minTier: 'internal',
    }),
  ],
  validation: { minTier: 'public', failOnUnredactedSensitive: false },
  sampling: {
    rate: 1.0,
    rules: [{ type: 'memory.embed', rate: 0.1 }],
  },
});

await tracer.span(
  { type: 'agent.run', attrs: { 'graphorin.agent.id': 'support-bot' } },
  async (span) => {
    span.setAttributes({ 'graphorin.session.id': 'session-123' });
    return runAgent();
  },
);

await tracer.shutdown();

License

MIT © 2026 Oleksiy Stepurenko.


Project Graphorin · v0.7.0 · MIT License · © 2026 Oleksiy Stepurenko · https://github.com/o-stepper/graphorin