From c0cd85d70515275f90204cb690b3129c9e3835d4 Mon Sep 17 00:00:00 2001 From: Jonathan Haas Date: Sat, 2 May 2026 22:13:59 -0700 Subject: [PATCH] feat: stamp gateway attribution metadata --- src/evalops_openai/__init__.py | 37 +++++++++++++++++++++++++++++++++- src/index.ts | 32 +++++++++++++++++++++++++++++ test/index.test.ts | 14 +++++++++++-- tests/test_evalops_openai.py | 9 ++++++++- 4 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/evalops_openai/__init__.py b/src/evalops_openai/__init__.py index 083dce0..42bf87c 100644 --- a/src/evalops_openai/__init__.py +++ b/src/evalops_openai/__init__.py @@ -21,6 +21,10 @@ def build_evalops_headers( organization_id: str | None = None, principal: str | None = None, trace_id: str | None = None, + team_id: str | None = None, + workload: str | None = None, + session_id: str | None = None, + evidence_id: str | None = None, default_headers: Mapping[str, str] | None = None, ) -> dict[str, str]: org_id = _first_value( @@ -43,6 +47,22 @@ def build_evalops_headers( if resolved_trace_id: headers["X-EvalOps-Trace-ID"] = resolved_trace_id + resolved_team_id = _first_value(team_id, os.getenv("EVALOPS_TEAM_ID")) + if resolved_team_id: + headers["X-EvalOps-Team-ID"] = resolved_team_id + + resolved_workload = _first_value(workload, os.getenv("EVALOPS_WORKLOAD")) + if resolved_workload: + headers["X-EvalOps-Workload"] = resolved_workload + + resolved_session_id = _first_value(session_id, os.getenv("EVALOPS_SESSION_ID")) + if resolved_session_id: + headers["X-EvalOps-Session-ID"] = resolved_session_id + + resolved_evidence_id = _first_value(evidence_id, os.getenv("EVALOPS_EVIDENCE_ID")) + if resolved_evidence_id: + headers["X-EvalOps-Evidence-ID"] = resolved_evidence_id + return headers @@ -81,6 +101,10 @@ def __init__( organization_id: str | None = None, principal: str | None = None, trace_id: str | None = None, + team_id: str | None = None, + workload: str | None = None, + session_id: str | None = None, + evidence_id: str | None = None, evalops_base_url: str | None = None, provider_ref: Mapping[str, str] | None = None, default_headers: MutableMapping[str, str] | None = None, @@ -97,6 +121,10 @@ def __init__( organization_id=organization_id, principal=principal, trace_id=trace_id, + team_id=team_id, + workload=workload, + session_id=session_id, + evidence_id=evidence_id, default_headers=default_headers, ), **kwargs, @@ -110,6 +138,10 @@ def __init__( organization_id: str | None = None, principal: str | None = None, trace_id: str | None = None, + team_id: str | None = None, + workload: str | None = None, + session_id: str | None = None, + evidence_id: str | None = None, evalops_base_url: str | None = None, provider_ref: Mapping[str, str] | None = None, default_headers: MutableMapping[str, str] | None = None, @@ -126,6 +158,10 @@ def __init__( organization_id=organization_id, principal=principal, trace_id=trace_id, + team_id=team_id, + workload=workload, + session_id=session_id, + evidence_id=evidence_id, default_headers=default_headers, ), **kwargs, @@ -139,4 +175,3 @@ def __init__( "build_evalops_headers", "build_provider_ref", ] - diff --git a/src/index.ts b/src/index.ts index 7723846..fe01da3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,10 @@ export type EvalOpsHeaderOptions = { organizationId?: string; principal?: string; traceId?: string; + teamId?: string; + workload?: string; + sessionId?: string; + evidenceId?: string; defaultHeaders?: Record; }; @@ -57,6 +61,26 @@ export function buildEvalOpsHeaders(options: EvalOpsHeaderOptions = {}): Record< headers["X-EvalOps-Trace-ID"] = traceId; } + const teamId = firstValue(options.teamId, readEnv("EVALOPS_TEAM_ID")); + if (teamId) { + headers["X-EvalOps-Team-ID"] = teamId; + } + + const workload = firstValue(options.workload, readEnv("EVALOPS_WORKLOAD")); + if (workload) { + headers["X-EvalOps-Workload"] = workload; + } + + const sessionId = firstValue(options.sessionId, readEnv("EVALOPS_SESSION_ID")); + if (sessionId) { + headers["X-EvalOps-Session-ID"] = sessionId; + } + + const evidenceId = firstValue(options.evidenceId, readEnv("EVALOPS_EVIDENCE_ID")); + if (evidenceId) { + headers["X-EvalOps-Evidence-ID"] = evidenceId; + } + return headers; } @@ -79,6 +103,10 @@ export class EvalOpsOpenAI extends OpenAIBase { organizationId, principal, traceId, + teamId, + workload, + sessionId, + evidenceId, evalopsApiKey, evalopsBaseURL, providerRef: providerRefOption, @@ -91,6 +119,10 @@ export class EvalOpsOpenAI extends OpenAIBase { organizationId, principal, traceId, + teamId, + workload, + sessionId, + evidenceId, defaultHeaders: defaultHeaderOption, }); const providerRef = buildProviderRef(providerRefOption); diff --git a/test/index.test.ts b/test/index.test.ts index b109dab..9d063d0 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -3,10 +3,21 @@ import { buildEvalOpsHeaders, buildProviderRef, OpenAI } from "../src/index"; describe("evalops-openai", () => { it("stamps both current and future org headers", () => { - expect(buildEvalOpsHeaders({ organizationId: "org_123", principal: "user:ada" })).toMatchObject({ + expect(buildEvalOpsHeaders({ + organizationId: "org_123", + principal: "user:ada", + teamId: "team_platform", + workload: "maestro-coding-session", + sessionId: "sess_123", + evidenceId: "evt_456", + })).toMatchObject({ "X-Organization-ID": "org_123", "X-EvalOps-Organization-ID": "org_123", "X-EvalOps-Principal": "user:ada", + "X-EvalOps-Team-ID": "team_platform", + "X-EvalOps-Workload": "maestro-coding-session", + "X-EvalOps-Session-ID": "sess_123", + "X-EvalOps-Evidence-ID": "evt_456", }); }); @@ -29,4 +40,3 @@ describe("evalops-openai", () => { expect(body).not.toHaveProperty("provider_ref"); }); }); - diff --git a/tests/test_evalops_openai.py b/tests/test_evalops_openai.py index a4ddf29..bc968cb 100644 --- a/tests/test_evalops_openai.py +++ b/tests/test_evalops_openai.py @@ -7,10 +7,18 @@ def test_headers_stamp_org_and_principal() -> None: assert build_evalops_headers( organization_id="org_123", principal="user:ada", + team_id="team_platform", + workload="maestro-coding-session", + session_id="sess_123", + evidence_id="evt_456", ) == { "X-Organization-ID": "org_123", "X-EvalOps-Organization-ID": "org_123", "X-EvalOps-Principal": "user:ada", + "X-EvalOps-Team-ID": "team_platform", + "X-EvalOps-Workload": "maestro-coding-session", + "X-EvalOps-Session-ID": "sess_123", + "X-EvalOps-Evidence-ID": "evt_456", } @@ -33,4 +41,3 @@ def test_with_provider_ref_does_not_mutate_input(monkeypatch: pytest.MonkeyPatch "environment": "prod", } assert "provider_ref" not in body -