Skip to content

SDK 0.4.0: one config format, server-span enrichment in every SDK, Node ESM/CJS - #27

Merged
rrohitramsen merged 7 commits into
mainfrom
release-0.4.0
Oct 20, 2026
Merged

rrohitramsen merged 7 commits into
mainfrom
release-0.4.0

Conversation

@rrohitramsen

Copy link
Copy Markdown
Collaborator

Fixes the SDK setup problems found by verifying the documented setups against the published 0.3.x packages: only Spring Boot worked as documented. After this PR, every SDK reads the same agenttel.yml and puts topology, baselines and decision attributes on HTTP server spans.

What changed

One config format, shared by all SDKs. Python, Node and Go now read the Spring Boot format:

  • an agenttel: root and kebab-case keys
  • operations as a map keyed by "METHOD /route"; "[POST /x]" and POST /x are the same key, and :id matches {id}
  • latencies such as "45ms", or a plain number of milliseconds
  • profiles for shared decision defaults

Each SDK's old format still loads and logs a deprecation warning.

Python

  • FastAPI server spans now get baselines and decisions.
  • instrument_fastapi() reuses the app's existing TracerProvider instead of replacing it, adds topology to its resource, and instruments the app.
  • Decision attributes now use the attributes.json names: agenttel.decision.* (previously agenttel.operation.*) and topology.on_call_channel (previously oncall_channel). The old constant names remain as aliases.
  • Fixed the FastAPI example.

Node

  • The package now loads under plain Node 18, 20 and 22, as ESM and as CommonJS. Relative imports carry .js extensions (built with NodeNext), and dist/cjs is marked as CommonJS.
  • Express and Fastify middleware set decision attributes. Passing { engine } sets topology, baselines and decisions in one go.
  • Added AgentTelEngine.fromConfigFile() and engine.resourceAttributes().
  • The examples now register the span processor and HTTP instrumentation.

Go

  • YAML operations now become static baselines and decisions in Build().
  • Added WithEngine(engine) for the net/http, Gin and gRPC middleware.
  • net/http records http.route from Go 1.22+ ServeMux patterns.
  • Added engine.Resource().
  • go-service-example and gin-example compile and run against the real API.
  • The docs now give the real minimums: Go 1.24 and otel/sdk 1.39.0.

JVM javaagent

  • Server spans now get baselines and decisions. They previously had no agenttel.* attributes, because the span is named POST until routing finishes.
  • Bracketed keys work in the agent's YAML.
  • Topology is added to the resource through a resource customizer.
  • The root build no longer hard-codes version = "0.2.0-alpha". Maven coordinates were already correct; only jar names and javadoc titles were wrong.

Release prep

  • Versions: Maven 0.4.0-alpha, npm 0.4.0-alpha.0, PyPI 0.4.0a1.
  • CHANGELOG entry for 0.4.0-alpha.
  • README and quick-start setup snippets fixed.

Testing

Each SDK has a new real-server integration test, like the Spring Boot EnrichmentTest: send one request, then assert topology, agenttel.baseline.latency_p50_ms=45 and agenttel.decision.retryable on the server span.

  • Python: uvicorn and FastAPI.
  • Node: Express and Fastify, run against the built package under plain node.
  • Go: otelhttp with httptest.
  • JVM: a plain Spring Boot app started with -javaagent:<built jar>.

Test suites:

  • JVM: 326 tests pass.
  • Python: 122 pass.
  • Node: 89 pass.
  • Go: all pass, including the GenAI submodules.

Install checks in fresh Docker containers:

  • The Python wheel on 3.11 and 3.12.
  • The npm tarball on Node 18, 20 and 22 (npm run test:pack).
  • Go as a dependency of a separate module.
  • Spring Boot and the javaagent against 0.4.0-alpha published to a local Maven repo.

Not included

  • I haven't tested OpenTelemetry JS 2.x, so the Node peer range stays at ^1.25.0.
  • The Go tag agenttel-go/v0.4.0-alpha exists locally but is not pushed. Push it when releasing.
  • Publishing to Maven Central, npm and PyPI is still to do.

- Load the shared agenttel.yml format (agenttel: root, kebab-case keys,
  operations map keyed by METHOD /route with optional [brackets], latency
  as duration string or ms number); legacy dialects load with a warning
- AgentTelMiddleware (pure ASGI) resolves the route template and sets
  baselines and decisions on the active server span; the processor no
  longer depends on the pre-routing span name
- instrument_fastapi() attaches to an existing SDK TracerProvider instead
  of replacing it, merges topology into its resource, and instruments the
  app with FastAPIInstrumentor when available
- Decision attributes use agenttel.decision.* and topology uses
  on_call_channel as in attributes.json; old constants kept as aliases
- Fix ConsumptionPattern.ASYNC value, add EscalationLevel.AUTO_RESOLVE
- Fix fastapi-example (BatchSpanProcessor, valid enum values)
- Add real-uvicorn integration test and config dialect tests

Signed-off-by: Rohit Kumar <rrohit.ramsen@gmail.com>
- Build ESM with NodeNext so relative imports carry .js extensions
  (fixes ERR_MODULE_NOT_FOUND for ./auth); mark dist/cjs as CommonJS;
  types-first exports map; engines node >= 18
- Load the shared agenttel.yml format; the pre-0.4.0 flat/snake_case
  dialect and operations list still load with a warning
- Express/Fastify middleware set decision attributes; accept { engine };
  Express re-checks the matched route before the response ends, Fastify
  records http.route
- Add AgentTelEngine.fromConfigFile, engine.operations,
  engine.resourceAttributes(), parseConfig, normalizeOperationName,
  parseDurationMs, enrichOperation
- Integration test: real Express and Fastify servers with OTel
  instrumentation against the built package; test:pack installs the
  packed tarball in a fresh project (ESM import, CJS require, servers)
- Examples register engine.spanProcessor and HTTP instrumentation

Signed-off-by: Rohit Kumar <rrohit.ramsen@gmail.com>
- Builder.Build() registers static baselines and decision context from
  YAML operations (explicit values > profile > defaults)
- Load the shared agenttel.yml format; the pre-0.4.0 flat camelCase
  dialect still loads with a warning
- New operations package (Registry, Decision, Normalize, ParseDurationMs)
  and enrich package shared by the processor and middleware
- net/http, Gin and gRPC middleware set decision attributes and accept
  WithEngine(engine); net/http records http.route from Go 1.22+ ServeMux
  patterns and matches parameterized routes after routing
- Engine.Resource()/ResourceAttributes() put topology on the resource
- Fix go-service-example and gin-example to compile and run against the
  real API with otelhttp/otelgin
- Integration test: httptest server with otelhttp + AgentTel middleware

Signed-off-by: Rohit Kumar <rrohit.ramsen@gmail.com>
- Maven 0.4.0-alpha, npm 0.4.0-alpha.0, PyPI 0.4.0a1
- CHANGELOG entry for 0.4.0-alpha
- README and quick-start: real Go/Node/Python setup code, correct
  minimum versions (Go 1.24, otel/sdk 1.39.0)

Signed-off-by: Rohit Kumar <rrohit.ramsen@gmail.com>
Server spans from the OTel javaagent start with a provisional name
("POST") and get their route later, so AgentTelSpanProcessor's onStart
lookup missed them and -javaagent runs exported no agenttel.* attributes.

- AgentTelEnrichingSpanExporter.withOperationMetadata(): add static
  baselines and decisions at export time, resolved from the final span
  name or METHOD http.route, when the span did not get them at start
- Javaagent: enable it, strip [brackets] from operation keys, and add
  topology via a resource customizer (independent of SPI discovery)
- Project version comes from VERSION_NAME (jar names said 0.2.0-alpha)
- JavaagentIntegrationTest: plain Spring Boot app run with the built
  agent jar, one request, assert server span and resource

Signed-off-by: Rohit Kumar <rrohit.ramsen@gmail.com>
- node-test ran vitest without building, so the integration test (which
  loads dist/ under plain node) found no package; use npm test (pretest
  builds) and add the packed-tarball smoke test
- Newer ruff releases enable more rule families by default (main itself
  now fails with 84 findings); select E4/E7/E9/F explicitly and pin ruff

Signed-off-by: Rohit Kumar <rrohit.ramsen@gmail.com>
The Python SDK now passes scripts/validate-attributes.sh. It was missing
127 canonical keys and defined 121 non-canonical ones.

- Add the missing canonical constants with the Go/Node names
  (AGENTIC_*, CAUSE_*, SEVERITY_*, CORRELATION_*, CIRCUIT_BREAKER_*, ...).
- Agentic tracer, scopes, orchestrations and guardrail recorder emit
  agenttel.agentic.* instead of top-level agent/step/task/... keys.
  GenAI reasoning/cached token counts use agenttel.genai.* keys.
- Old constant names stay as deprecated aliases of the canonical keys.
  Never-emitted constants with no canonical equivalent are removed.
- Keys still emitted that attributes.json lacks move to
  agenttel.extension_attributes.
- Stop emitting duration_ms attributes that duplicate span duration;
  the invocation error message goes into the span status.
- Fix AgentInvocation: exiting `with tracer.invoke()` raised
  "generator didn't stop", complete() left status "running", and
  fail() did not mark the invocation failed.

Listed under Breaking in the 0.4.0-alpha CHANGELOG entry.

Signed-off-by: Rohit Kumar <rrohit.ramsen@gmail.com>
@rrohitramsen
rrohitramsen merged commit de83b6a into main Oct 20, 2026
9 checks passed
@rrohitramsen
rrohitramsen deleted the release-0.4.0 branch October 20, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant