Skip to content

telemetry: validated cost-attribution tags (team, project, env, enduser.id) #196

Closed

Description

@amirkhan-ak-sf

Summary

Piece 6 of 6, second half. A small, fixed set of tags set once and injected on
every call.

donkey = Donkey.from_env(team="support", project="triage-v2", env="prod")

Why it matters — Scenario A

Finance asks "what did the support agent cost last month versus the HR bot?"
Both share one client_id, so without tags the answer is "we don't know." With
tags it is a group-by.

Acceptance criteria

  • The key set is fixed — team, project, env, enduser.id. An
    unknown key is a config error, not a silently-dropped header.
  • Values are length-limited and validated so nobody stuffs JSON into a header.
  • Tags appear both as request headers and as donkey.cost.* span attributes.
  • Until the gateway-side header names are verified, tags still carry full
    value in spans, which the SDK controls end to end.

Cross-surface impact

  • core/telemetry: the change.
  • Verification: the attribution header names are the single most important
    unverified value; keep the Unverified(...) placeholder and config override.

Out of scope

Any client-side cost calculation.

Activity

  1. added
    enhancementNew feature or request
    priority/p1Must-have: blocks a real adapter or CI gate
    size/s~half day, one file
    area:telemetryOTel GenAI spans, correlation IDs, cost tags (1.6, 1.7)
    six-piece-minimumPart of the minimum set that justifies installing the SDK
    on Sep 4, 2026
  2. self-assigned this
    on Sep 11, 2026
  3. tommasobolis commented on Sep 11, 2026

    @tommasobolis
    Collaborator

    Implementation plan (branch feat/196-cost-attribution-tags)

    Scope decision: config-level "set once" plus per-run overrides via
    donkey.run(...) — fulfilling the two docstrings (Donkey.run / RunScope)
    that already promised cost fields land on donkey.run() in #196, alongside the
    AC's Donkey.from_env(team=..., project=..., env=...) set-once surface.

    The fixed key set

    team, project, env, enduser.id. A new frozen CostTags dataclass
    (core/cost.py) with fields team/project/env/enduser_id. CostTags.from_mapping
    (toml path) rejects any key outside the fixed set → ConfigError (AC #1).
    Values are length-limited (≤256) and reject control chars / CR / LF so nothing
    can smuggle JSON or inject a header (AC #2).

    Two carriers (AC #3, #4)

    • Request headers — emitted with UNVERIFIED placeholder names (§0.3):
      four new Unverified(...) constants in core/_verify.py (X-Anypoint-Cost-*
      best-guess), each config-overridable (cost_team_header …) and resolved once
      per client like the correlation headers (telemetry: per-call and per-run correlation IDs via donkey.run() #195). §3 in docs/verified-apis.md
      confirms the cost-attribution header name is the single highest-priority
      unknown.
    • donkey.cost.* span attributes — always carry the full value (SDK
      controls the span end to end), so attribution works even before the header
      name is verified. donkey.cost.team exists; add .project / .env /
      .enduser.id.

    Resolution / precedence

    • DonkeyConfig.cost: CostTags, resolved in from_env() along the fixed
      precedence (kwarg → DONKEY_COST_* env → [donkey.cost] toml → default).
    • Donkey.from_env(*, team=, project=, env=, enduser_id=) builds the override.
    • donkey.run(id=, team=, project=, env=, enduser_id=) binds a CostTags to a
      contextvar for the block; per-send injection and span record merge
      run-scope over config (per field). The static connection_kwargs()
      snapshot carries config-level tags only — run-scope can't propagate through a
      frozen default_headers dict (documented degradation, same as correlation).

    Surfaces moving in lockstep

    core/cost.py (new), core/config.py, core/_verify.py, core/telemetry.py,
    core/transport.py (async + sync twin), donkey.py; tests under tests/unit;
    docs/verified-apis.md §3 rows; website telemetry/config page + README. The
    Verification worklist gains the four cost-header-name rows (blocked-on-verification).

  4. added a commit that references this issue on Sep 11, 2026
    2bd4c4f
  5. tommasobolis commented on Sep 11, 2026

    @tommasobolis
    Collaborator

    Landed on develop via #360 (squash-merged). Cost-attribution tags shipped: config-level (Donkey.from_env(team=…), DONKEY_COST_*, [donkey.cost]) + per-run overrides (donkey.run(team=…)), emitted as request headers and donkey.cost.* span attributes. Closes the six-piece minimum.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:telemetryOTel GenAI spans, correlation IDs, cost tags (1.6, 1.7)enhancementNew feature or requestpriority/p1Must-have: blocks a real adapter or CI gatesix-piece-minimumPart of the minimum set that justifies installing the SDKsize/s~half day, one file

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions