Skip to content

feat: add generic and OpenRouter attribution headers#542

Merged
eric-tramel merged 5 commits intomainfrom
boltzmann/feat/519-attribution-headers
Apr 14, 2026
Merged

feat: add generic and OpenRouter attribution headers#542
eric-tramel merged 5 commits intomainfrom
boltzmann/feat/519-attribution-headers

Conversation

@eric-tramel
Copy link
Copy Markdown
Contributor

@eric-tramel eric-tramel commented Apr 14, 2026

📋 Summary

Add outbound attribution headers to all model requests (X-Title: NeMo Data Designer) and OpenRouter-specific headers on the built-in OpenRouter provider, gated on the existing NEMO_TELEMETRY_ENABLED opt-out.

🔗 Related Issue

Closes #519

🔄 Changes

  • Add ATTRIBUTION_TITLE, ATTRIBUTION_REFERER, and OPENROUTER_ATTRIBUTION_HEADERS constants
  • Wire extra_headers into the predefined OpenRouter provider entry in PREDEFINED_PROVIDERS
  • Inject X-Title in ModelFacade.consolidate_kwargs() when telemetry is enabled, applied last so user-supplied or provider-level values take precedence
  • No framework headers injected when NEMO_TELEMETRY_ENABLED=false; user extra_headers still honored

🧪 Testing

  • make test passes
  • Unit tests added/updated
  • E2E tests added/updated (if applicable)

Added:

  • test_consolidate_kwargs_telemetry_disabled — no framework headers when opted out
  • test_consolidate_kwargs_user_x_title_override — user X-Title takes precedence

Updated:

  • test_consolidate_kwargs — verify X-Title injection under default telemetry-on
  • test_get_builtin_model_providers — verify OpenRouter extra_headers and confirm NVIDIA/OpenAI have none

✅ Checklist

  • Follows commit message conventions
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

@eric-tramel eric-tramel added the enhancement New feature or request label Apr 14, 2026
@eric-tramel eric-tramel requested a review from a team as a code owner April 14, 2026 01:59
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 14, 2026

All contributors have signed the DCO ✍️ ✅
Posted by the DCO Assistant Lite bot.

@github-actions
Copy link
Copy Markdown
Contributor

Code Review: PR #542 — feat: add generic and OpenRouter attribution headers

Summary

This PR adds outbound attribution headers to all model API requests made by DataDesigner. A generic X-Title: NeMo Data Designer header is injected on every request when telemetry is enabled (the default). Additionally, OpenRouter-specific attribution headers (HTTP-Referer, X-OpenRouter-Title, X-OpenRouter-Categories) are statically configured on the built-in OpenRouter provider. The feature respects the existing NEMO_TELEMETRY_ENABLED opt-out and allows user-supplied headers to take precedence.

Scope: 4 files changed, 59 additions, 4 deletions — a small, well-scoped feature.

Findings

Positive

  1. Clean separation of concerns. Static OpenRouter headers live in constants.py (config package) while the dynamic telemetry-gated X-Title injection lives in facade.py (engine package). This correctly respects the config → engine dependency direction.

  2. Precedence logic is correct. The X-Title injection in consolidate_kwargs() (facade.py:164-167) is applied after provider-level extra_headers are merged, and the if "X-Title" not in headers guard ensures user-supplied or provider-level values are never overwritten. The merge order {"X-Title": ..., **headers} correctly lets existing keys win.

  3. Telemetry opt-out is respected. The injection is gated on the existing TELEMETRY_ENABLED flag, which reads NEMO_TELEMETRY_ENABLED from the environment with a default of true. No new env vars or configuration surface needed.

  4. Good test coverage. Three test scenarios cover the key behaviors:

    • Default injection when telemetry is on (test_consolidate_kwargs)
    • No injection when telemetry is off (test_consolidate_kwargs_telemetry_disabled)
    • User override takes precedence (test_consolidate_kwargs_user_x_title_override)
    • OpenRouter provider has correct extra_headers and other providers have None (test_get_builtin_model_providers)
  5. Constants are well-structured. ATTRIBUTION_TITLE and ATTRIBUTION_REFERER are reused by OPENROUTER_ATTRIBUTION_HEADERS, avoiding duplication.

Suggestions (Non-blocking)

  1. Module-level evaluation of TELEMETRY_ENABLED (telemetry.py:31). The TELEMETRY_ENABLED constant is evaluated once at import time. This is consistent with the existing codebase pattern and is fine for this feature — just noting that changing NEMO_TELEMETRY_ENABLED after import has no effect. The test correctly patches the symbol at the usage site (@patch("data_designer.engine.models.facade.TELEMETRY_ENABLED", False)).

  2. Header key casing. The PR uses X-Title (mixed case) for the generic header. HTTP headers are case-insensitive per RFC 7230, so this is technically fine. However, if a downstream proxy normalizes header casing, the if "X-Title" not in headers check could fail to detect a user-supplied x-title. This is an edge case and not specific to this PR — just worth being aware of.

  3. OpenRouter X-OpenRouter-Categories value. The value "programming-app" is used. This appears to be a valid OpenRouter category, but the available categories are not documented in the PR. A brief comment or link to OpenRouter docs would be helpful for future maintainers, but this is minor.

Issues

No blocking issues found.

Verdict

Approve. This is a clean, well-tested, narrowly scoped feature. The attribution headers follow the existing patterns for header injection, respect the telemetry opt-out, and correctly preserve user overrides. The test coverage is thorough. No architectural concerns.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 14, 2026

Greptile Summary

This PR adds outbound attribution headers (X-Title: NeMo Data Designer) to all model requests and OpenRouter-specific headers (HTTP-Referer, X-OpenRouter-Title, X-OpenRouter-Categories), both gated on NEMO_TELEMETRY_ENABLED. The key design decision — keeping PREDEFINED_PROVIDERS free of extra_headers and injecting all headers entirely inside the if TELEMETRY_ENABLED: guard in consolidate_kwargs() — correctly addresses the previously raised concern about provider-level headers bypassing the opt-out. Precedence (user/provider > framework defaults) is correctly implemented and thoroughly tested.

Confidence Score: 5/5

  • This PR is safe to merge — the implementation is correct, the previous thread concern is resolved, and test coverage is thorough.
  • No P0 or P1 issues found. The design correctly keeps PREDEFINED_PROVIDERS free of extra_headers and gates all attribution injection inside the TELEMETRY_ENABLED block. Precedence (caller/provider > framework X-Title > OpenRouter defaults) is correctly implemented across all paths. The or {} fix for explicit None extra_headers is a correctness improvement. Test suite covers all relevant scenarios including telemetry-off, provider override, and caller override cases.
  • No files require special attention.

Important Files Changed

Filename Overview
packages/data-designer-engine/src/data_designer/engine/models/facade.py Adds framework attribution header injection to consolidate_kwargs() under the TELEMETRY_ENABLED guard; precedence logic (caller → provider → framework X-Title → OpenRouter defaults) is correct and the or {} fix for explicit None extra_headers is a welcome correctness improvement.
packages/data-designer-config/src/data_designer/config/utils/constants.py Defines ATTRIBUTION_TITLE, ATTRIBUTION_REFERER, and OPENROUTER_ATTRIBUTION_HEADERS constants; PREDEFINED_PROVIDERS correctly left without extra_headers so OpenRouter headers are fully gated by the engine-side telemetry check.
packages/data-designer-engine/tests/engine/models/test_facade.py Comprehensive test coverage added for all header-injection scenarios: telemetry on/off, OpenRouter vs. non-OpenRouter, provider-level vs. caller-level precedence, and explicit None extra_headers. All cases are correctly asserted.
packages/data-designer-config/tests/config/test_default_model_settings.py Adds extra_headers is None assertions for all three builtin providers, correctly documenting the design choice that attribution headers are injected by the engine, not baked into provider configs.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant ModelFacade
    participant Constants

    Caller->>ModelFacade: "consolidate_kwargs(**kwargs)"
    ModelFacade->>ModelFacade: merge generate_kwargs + caller kwargs
    alt model_provider.extra_headers is truthy
        ModelFacade->>ModelFacade: merge provider extra_headers (provider wins over caller)
    end
    alt "TELEMETRY_ENABLED == True"
        ModelFacade->>Constants: ATTRIBUTION_TITLE
        alt "X-Title" not already in headers
            ModelFacade->>ModelFacade: prepend X-Title (framework default, lowest priority)
        end
        alt "provider.name == "openrouter""
            ModelFacade->>Constants: OPENROUTER_ATTRIBUTION_HEADERS
            ModelFacade->>ModelFacade: "merge {**OPENROUTER_DEFAULTS, **current_headers}<br/>(current_headers win → user/provider values preserved)"
        end
    end
    ModelFacade-->>Caller: kwargs with attribution headers
Loading

Reviews (7): Last reviewed commit: "fix telemetry gating for OpenRouter head..." | Re-trigger Greptile

Inject X-Title header on all provider requests when telemetry is
enabled, and add OpenRouter-specific attribution headers (HTTP-Referer,
X-OpenRouter-Title, X-OpenRouter-Categories) to the predefined
OpenRouter provider entry.

- Add ATTRIBUTION_TITLE, ATTRIBUTION_REFERER, and
  OPENROUTER_ATTRIBUTION_HEADERS constants
- Add extra_headers to the OpenRouter predefined provider
- Inject X-Title in ModelFacade.consolidate_kwargs() gated on
  TELEMETRY_ENABLED, with user/provider headers taking precedence
- Update and extend tests for both config and engine packages

Fixes #519

Signed-off-by: Eric W. Tramel <[email protected]>
@eric-tramel eric-tramel force-pushed the boltzmann/feat/519-attribution-headers branch from aa593c5 to a047ccf Compare April 14, 2026 02:09
andreatgretel
andreatgretel previously approved these changes Apr 14, 2026
Copy link
Copy Markdown
Contributor

@andreatgretel andreatgretel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, two small nits: kwargs.get("extra_headers", {}) doesn't guard against explicit None (or {} fixes it), and the X-Title override test only covers the provider path, not caller kwargs. neither blocking

andreatgretel
andreatgretel previously approved these changes Apr 14, 2026
Existing users have extra_headers: null in their model_providers.yaml
for the openrouter provider, which causes the OpenRouter-specific
attribution headers (HTTP-Referer, X-OpenRouter-Title,
X-OpenRouter-Categories) to be silently dropped.

This injects those headers programmatically in consolidate_kwargs()
when the provider is openrouter and telemetry is enabled, using the
same fill-missing-only pattern as X-Title. Provider-level and
caller-supplied headers still take precedence.

Adds 5 unit tests covering injection, precedence, telemetry opt-out,
and non-openrouter provider isolation.

Signed-off-by: Eric W. Tramel <[email protected]>
@eric-tramel
Copy link
Copy Markdown
Contributor Author

Hey @andreatgretel — your two nits from the last review are addressed:

  1. None guard → switched kwargs.get("extra_headers", {}) to kwargs.get("extra_headers") or {} throughout consolidate_kwargs (commit a7a7427)
  2. X-Title override test gap → added test_consolidate_kwargs_with_explicit_none_extra_headers covering the caller-kwargs path (same commit)

Also found a real bug during wire-level testing: existing users have extra_headers: null for the openrouter provider in their model_providers.yaml, so the OpenRouter-specific attribution headers (HTTP-Referer, X-OpenRouter-Title, X-OpenRouter-Categories) were silently dropped — only X-Title got through. Fixed this by injecting those headers programmatically in consolidate_kwargs when provider is openrouter + telemetry is enabled, same fill-missing-only pattern as X-Title (commit 24cb244). Five new tests for the openrouter injection logic.

- keep default OpenRouter provider extra_headers unset
- rely on facade injection so telemetry opt-out suppresses attribution
- align default provider test with the gated behavior
@eric-tramel eric-tramel merged commit 64f31bc into main Apr 14, 2026
49 checks passed
przemekboruta pushed a commit to przemekboruta/DataDesigner that referenced this pull request Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a generic client title header and OpenRouter attribution headers

2 participants