omp packages

@hachibox/genai-otel0.1.4

agent-harness observability: capture LLM/agent activity as OpenTelemetry spans with canonical gen_ai.* attributes and export over OTLP/HTTP JSON

hachibox Published 5 days ago MIT ~532 downloads/mo

Untested on omp

This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.

Did it work for you?

Result

One report per person. A new report replaces your old one.

Reports 0

No reports yet.

Readme

@hachibox/genai-otel

Capture agent-harness activity as OpenTelemetry spans with canonical gen_ai.* attributes and export them over OTLP/HTTP JSON. One trace is one agentic turn: everything the agent does between one user message and the next. A multi-turn session is a sequence of traces sharing a session.id attribute, never a single trace.

What it captures

For every user message (one OTel trace, session.id = pi session uuid on every span):

invoke_agent {pi}                     turn root: user prompt, final assistant text
├── chat {model}                      per LLM cycle: model, token+cost usage, input
│   │                                 system prompt, output text and thinking blocks
│   │                                 (as {type:"reasoning"} parts), reasoning token
│   │                                 count, response id/model
│   └── execute_tool {tool}           per tool call: name, call id, arguments, result
└── ...one chat/tool subtree per provider request of the turn

Multiple LLM cycles inside one user message (tool-call loops, retries) stay inside the same trace: the trace boundary is the user message, never the session.

Install

Install as a pi package from npm:

pi install npm:@hachibox/genai-otel

or, project-local (written to .pi/settings.json):

pi install -l npm:@hachibox/genai-otel

or for a single run without installing:

pi -e npm:@hachibox/genai-otel

Verify the package is loaded with pi list; uninstall with pi remove npm:@hachibox/genai-otel. npm-sourced pi packages have their dependencies installed automatically; this package has no runtime npm dependencies beyond the Node builtins.

What gets installed

The npm tarball contains only src/, README.md, and LICENSE.

Configuration

Precedence: environment variables override the credentials file.

Source Key Notes
credentials file ~/.pi/agent/genai-otel.json {"tracesUrl": "...", "apiKey": "..."}
env GENAI_OTEL_TRACES_URL export URL, e.g. https://host/v1/traces
env GENAI_OTEL_API_KEY sent as X-API-Key
env GENAI_OTEL_HEADERS JSON object of extra headers (e.g. basic auth)
env OTEL_SERVICE_NAME sets the service.name resource attribute
env OTEL_RESOURCE_ATTRIBUTES comma-separated key=value resource attributes (URL-decoded per the OTel spec)
env GENAI_OTEL_TRACING_ENABLED=false disables export only
env PI_TRACING_ENABLED=false pi-scoped kill switch
env PI_TRACE_CAPTURE_DIR debug: write raw pi events as JSONL (see below)

The credentials file is optional: without it (and without GENAI_OTEL_TRACES_URL) tracing stays unconfigured rather than erroring. If the file exists but is unreadable or malformed, startup fails loudly instead of silently dropping capture. Set PI_CREDENTIALS_FILE to use a different credentials-file path.

Resource attributes

The export's resource carries service.name and any extra attributes the OTel-standard environment variables provide, following OTel precedence (OTEL_RESOURCE_ATTRIBUTES pairs first, OTEL_SERVICE_NAME overrides a service.name pair):

OTEL_SERVICE_NAME=my-service \
OTEL_RESOURCE_ATTRIBUTES='workflow.phase=implement,deployment.environment.name=prod' \
  pi

Downstream consumers read these from resourceSpans[].resource.attributes (e.g. a collector transform keyed on resource.attributes["workflow.phase"], or Langfuse's service identity). Malformed OTEL_RESOURCE_ATTRIBUTES input (non-key=value pairs, invalid percent-encoding) fails loudly at startup instead of exporting a mislabeled resource.

The emitter is sink-agnostic: any endpoint that accepts OTLP/HTTP JSON works (Langfuse, an S3/R2 OTLP exporter, Datadog, a self-hosted collector, …). Each completed turn is exported as soon as it ends rather than buffered until shutdown, so a long run cannot lose its traces to an ungraceful exit. Export transport failures are fail-open: they warn once and drop the batch, never blocking pi.

Enable and disable tracing

For a single run:

PI_TRACING_ENABLED=false pi

For a shell session:

export PI_TRACING_ENABLED=false   # off until `unset PI_TRACING_ENABLED`

The two switches differ:

  • PI_TRACING_ENABLED=false short-circuits before the credentials file is read. Tracing is off, but debug capture (below) stays available via PI_TRACE_CAPTURE_DIR.
  • GENAI_OTEL_TRACING_ENABLED=false only stops the export; everything else behaves the same.

To disable entirely, remove the package (pi remove npm:@hachibox/genai-otel) or delete the credentials file.

Troubleshooting: no traces appearing

  1. Kill switch set — check PI_TRACING_ENABLED and GENAI_OTEL_TRACING_ENABLED in the current shell and in shell profiles (~/.bashrc, ~/.zshrc, …); an exported false wins over the credentials file.

  2. Plugin not loaded — pi list must show @hachibox/genai-otel.

  3. No sink URL — with neither GENAI_OTEL_TRACES_URL nor a tracesUrl in the credentials file, export is disabled, not errored.

  4. Credentials file malformed — a trailing comma or wrong value type makes startup fail loudly. Validate with:

    node -e "JSON.parse(require('fs').readFileSync(require('os').homedir() + '/.pi/agent/genai-otel.json', 'utf8'))"
    

    Remember env vars override the file: a stale GENAI_OTEL_TRACES_URL export can shadow a corrected file.

  5. Sink rejects or unreachable — export failures are fail-open (warn once, drop the batch). Look for the warning in pi's stderr and verify the endpoint URL, the X-API-Key value, and any GENAI_OTEL_HEADERS (e.g. proxy auth).

  6. Session ended ungracefully — traces flush per completed turn, so only the in-flight turn at kill time is lost; anything earlier should exist.

Debug capture (fixtures)

Set PI_TRACE_CAPTURE_DIR to have the plugin write the raw pi hook payloads it consumes as JSONL, one file per process. This is a development tool for keeping the committed replay fixtures honest when pi's SDK shapes change. It is independent of the emitter, so it works with PI_TRACING_ENABLED=false and needs no export endpoint:

PI_TRACE_CAPTURE_DIR=/tmp/pi-capture PI_TRACING_ENABLED=false \
  pi -e /path/to/public/plugins/genai-otel/src/pi/index.ts ...

The captured files contain raw prompts, tool arguments, and tool results. They are git-ignored and must be anonymized before committing. Regenerate the replay fixture after a pi upgrade:

pnpm exec vite-node public/plugins/genai-otel/scripts/anonymize-capture.ts \
  /tmp/pi-capture public/plugins/genai-otel/spec/pi/fixtures/pi-session.events.json

The plugin and server specs replay that fixture through the real adapter and OtlpEmitter (no pi process, model, or network). When a pi binary is on PATH, the replay spec compares the fixture's recorded piVersion to pi --version and fails on mismatch, so a stale fixture cannot pass silently.

Attribution

The usage-validation and attribute-clamping techniques in @hachibox/genai-otel are modeled on prior art (pi-otel, Apache-2.0; langfuse codex plugin, MIT). This package is an independent implementation; no upstream code is included.