Skip to content

[Feature]: Support API-key headers for OpenAI-compatible gateways #307

Description

Problem you're trying to solve

ASSERT uses LiteLLM's openai/* provider for OpenAI-compatible endpoints. LiteLLM sends OPENAI_API_KEY as standard Bearer authentication, but some API-management gateways require the same credential in an additional provider-specific header such as api-key or Ocp-Apim-Subscription-Key.

Today ASSERT has no supported configuration for this header. Model configuration covers the model name and generation parameters, but not gateway authentication headers. As a result, a gateway can successfully expose /v1/models and /v1/chat/completions while ASSERT receives 401 Unauthorized because its request contains only Bearer authentication.

This affects more than one call site:

  • pipeline Chat Completions and Responses calls in the core model client
  • assert-ai init
  • ACS language-model generation

A caller cannot solve this once through eval_config.yaml or a CLI option, and embedding the secret directly in config would be unsafe.

Proposed solution or behavior

Add an opt-in way to send OPENAI_API_KEY under a configurable compatibility-header name. One possible environment contract is:

OPENAI_BASE_URL=https://gateway.example/openai/v1
OPENAI_API_KEY=<secret>
ASSERT_OPENAI_API_KEY_HEADER=api-key

Expected behavior:

  • when the header-name setting is absent, preserve current behavior exactly
  • when configured, add {<configured-name>: OPENAI_API_KEY} to LiteLLM extra_headers
  • apply it consistently to Chat Completions, Responses, assert-ai init, and ACS calls
  • scope automatic injection to OpenAI-compatible model routes so gateway credentials are not sent to Azure, Anthropic, or other providers
  • let explicit per-call headers take precedence
  • redact the configured header value from logs, traces, request payloads, and generated artifacts
  • fail clearly if an invalid header configuration is provided
  • document the setting in .env.example and provider setup guidance
  • add regression tests covering disabled-by-default behavior, provider scoping, header precedence, and each LiteLLM call path

The exact public name/API is open to maintainer preference; the important requirement is one safe, reusable configuration point for OpenAI-compatible gateways.

Alternatives considered

  • Change the gateway to accept Bearer authentication: ideal when the gateway is under the ASSERT user's control, but unavailable for many enterprise or third-party gateways.
  • Use the Azure provider route instead: only works when the gateway exposes Azure-native /openai/deployments/{deployment} routes. Some gateways expose models exclusively through the OpenAI-compatible /v1 router.
  • Patch LiteLLM from the embedding application: works as a local workaround but depends on ASSERT's private payload builders and has to be maintained by every integrator.
  • Put headers or credentials directly in evaluation YAML: risks leaking secrets into configs and artifacts and duplicates configuration across stages.
  • Run a separate LiteLLM proxy: adds an operational component solely to translate one authentication header.

Use case context

This applies to production, spec-driven evaluations using hosted models for systematization, test generation, user simulation, and judging. In a verified OpenAI-compatible gateway setup, model discovery, normal chat, and structured JSON generation all succeeded when the required api-key header was present; Bearer-only requests returned 401.

The evaluated target can still be a callable or traced agent. This request concerns the infrastructure models ASSERT itself uses to build and judge the evaluation.

Acknowledgements

  • I searched existing issues and discussions and did not find a duplicate.
  • I'd be willing to contribute a PR for this (optional).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions