Skip to content

Releases: Particle-Academy/prism-opentelemetry

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 14 Sep 08:15

v0.3.0 - Attachment bytes are withheld from captured content

What changed

With Prism's prism.telemetry.capture_content on, input.value, output.value and step content carry what a model was sent and said. That content can now hold media parts with their bytes. Each captured media part keeps its kind, mime type, file id, filename and url, and its base64 is replaced by omitted_bytes, the size of what was left out.

To export the bytes as well, set Prism's prism.telemetry.capture_media (PRISM_TELEMETRY_CAPTURE_MEDIA=true). This bridge reads that setting, so one switch decides for Prism and for the spans.

Media is recognised by its serialized shape: prism v0.120.0's kind, the kindless shape older prism releases produce, or a null kind. A tool's own payload is never rewritten, even if it has a base64 key.

Behaviour change: if you had content capture on and relied on media bytes in your traces, set capture_media.

Also

The README's Privacy section was wrong and is rewritten. It said this bridge never adds prompt or completion text to spans, but it does whenever capture_content is on.

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 05 Sep 08:34

v0.2.0 — quota headroom on the span, beside the latency

The constraint is now >=0.116 <1.0. That release carries the event fields this
bridge reads; keeping the old constraint while reading a field no published
version has would turn a version mismatch into a span that silently carries no
quota — the exact failure this release fixes.

A span now carries what the provider said about your quota, next to the latency
and token counts that were already there:

prism.rate_limit.buckets                  ["requests","tokens"]
prism.rate_limit.<bucket>.limit           int
prism.rate_limit.<bucket>.remaining       int
prism.rate_limit.<bucket>.resets_at_unix  int, Unix seconds

The names are custom and sit under prism. deliberately. The OpenTelemetry
GenAI conventions define no attribute for quota — checked against the gen_ai
and http registries, not assumed. gen_ai.error.type has a rate_limit
member, but that names a FAILURE rather than headroom. Squatting inside
gen_ai. would be worse than sitting beside it, because a real
gen_ai.rate_limit.* must be free to arrive later without two spellings meaning
subtly different things.

Flattened by bucket NAME rather than by index or as a JSON blob, so a backend can
express prism.rate_limit.tokens.remaining < 1000. A blob forbids that outright
and an index makes the query depend on the order the provider answered in.

The cost is that the attribute key space becomes provider-controlled, so a bucket
name is accepted only if every character is in an explicit ASCII set, at most 16
buckets reach a span, and a duplicate loses to the first. Names are DROPPED,
never repaired: normalising lets two distinct names collapse onto one key, at
which point a hostile bucket overwrites a real one.

Rate limits reached this bridge only on the response, and the response is handed
to telemetry only when content capture is on. Content capture is off by default,
for good reason — spans leave the application and carry user prompts. So quota
headroom was gated behind a privacy switch it has nothing to do with, and
appeared only on the 429 path: exactly when it had stopped being headroom.

They now travel on the telemetry events themselves, unconditionally, as token
usage already did. The content gate is unchanged, and a span with capture OFF
carries the buckets and neither the prompt nor the completion.

The tests in this package had never run. Its only workflow was Factcheck, so
32 tests and 80 assertions sat on disk while CI reported green — and v0.1.1 was
published on that basis. It now runs tests, PHPStan, formatting and a require
checker, and adding that fourth gate immediately found two real dependency
defects: mb_strcut used with no ext-mbstring declared, which fatals at
runtime on a PHP built without it, and OpenTelemetry\Context symbols used
directly while only open-telemetry/api was declared.

v0.1.1 — allow any 0.x Prism

Choose a tag to compare

@wishborn wishborn released this 17 Aug 05:47

^0.111 pins the minor on a 0.x version, so this bridge hard-blocked particle-academy/prism v0.112.0 and stalled every downstream update.

Widened to >=0.111 <1.0. The bridge consumes only Prism's telemetry contract (Events\Telemetry\*, Enums\FinishReason, Enums\TelemetryOperation), none of which v0.112.0 touched — verified by running the suite against it: 15 passed, 58 assertions.

Composer-only change; no source modified.

v0.1.0 — OpenTelemetry bridge for Prism

Choose a tag to compare

@wishborn wishborn released this 18 Jul 11:13

First release. Turns Prism's telemetry events (prism ^0.111) into OpenTelemetry spans for Arize Phoenix and any OTLP backend.

  • One root span per generation; step spans and tool spans nested under their step (works for streaming and non-streaming event orderings).
  • Dual-emits GenAI (gen_ai.*) and OpenInference (openinference.span.kind=CHAIN/LLM/TOOL, llm.model_name, token counts, input.value/output.value, messages, tool.parameters, session.id/user.id) so it renders richly in Phoenix.
  • Captured content (opt-in via prism) is byte-capped (PRISM_OTEL_CONTENT_MAX_LENGTH) and serialization is fail-safe — telemetry never throws into the generation.
  • Leakage-conscious: embedding vectors and image base64 are not exported; image URLs are stripped of query/token.

Install: composer require particle-academy/prism-opentelemetry + an OTLP exporter, then set PRISM_TELEMETRY_ENABLED=true.