Skip to content

Fix bcal CAPI evals: send M365 LLM API taxonomy headers; default to gpt-55-chat - #756

Merged
martinsrui-msft merged 2 commits into
mainfrom
fix/bcal-capi-taxonomy-headers
Jul 28, 2026
Merged

Fix bcal CAPI evals: send M365 LLM API taxonomy headers; default to gpt-55-chat#756
martinsrui-msft merged 2 commits into
mainfrom
fix/bcal-capi-taxonomy-headers

Conversation

@martinsrui-msft

Copy link
Copy Markdown
Collaborator

Problem

All bcal CAPI evaluations have failed since ~Jul 19 (runs 29672964336, 30187493565, 30349142281).

Root cause

The M365 LLM API taxonomy/CoS rollout (aka.ms/llmapi/waves-timeline) began enforcing the
X-Taxonomy-Experience/Agent/InferenceStep/TrafficType headers between the last green run (Jul 12)
and Jul 19. Neither bc-eval's CapiModel nor the generated CAPI client sends them, so CAPI rejects
every call with InvalidInferenceInput. BC production is unaffected because it calls Azure OpenAI
directly (BC-Platform .../CopilotApi/AL/AzureOpenAIClient.cs), bypassing the enforcing gateway —
only BC-Bench's CAPI eval path hits it, so this was new and unfixed anywhere in BC.

Fix

Inject the four headers (env-overridable via CAPI_TAXONOMY_*, defaults baked in) at the one point
both code paths flow through — CapiModel._get_common_capi_parameters, whose dict is splatted onto
the generated operations that forward an unknown headers kwarg:

  • bcal eval path: bc_eval_capi_bridge.py patches it in the bridge subprocess.
  • nl2al judge/scoring path: evaluator/_capi_taxonomy.py, installed at import time from
    scores.py, which bceval exec_modules while resolving evaluators before the judge runs
    (the judge runs in an isolated uv tool env that has bc-eval but not bcbench, hence a second,
    self-contained shim).

Values validated directly against CAPI (for both gpt-55-chat-2026-04-29 and the gpt-41 judge):
Experience=AppCopilots (BC is an app copilot; the LLM API's allowed set is BizChat, WXPOAgents,
AppCopilots, Cowork, Scout, WorkIQ), Agent=bcal, InferenceStep=ChatCompletion, TrafficType=Test.

Also sets gpt-55-chat-2026-04-29 as the new default external-model (added to the choices and the
scheduled-run fallback).

Validation

  • +9 unit tests; full suite 634 pass; ruff + ty + pre-commit clean.
  • 4-entry test run: all green.
  • Full run on gpt-55-chat-2026-04-29: 122/122 eval jobs + summarize/judge succeeded.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

martinsrui-msft and others added 2 commits July 28, 2026 13:07
…pt-55-chat

The M365 LLM API's taxonomy/CoS rollout (aka.ms/llmapi/waves-timeline) began enforcing
X-Taxonomy-Experience/Agent/InferenceStep/TrafficType headers between Jul 12 and Jul 19,
breaking every bcal CAPI eval (InvalidInferenceInput "Required taxonomy data not provided").
Neither bc-eval's CapiModel nor the generated CAPI client sends them; BC production is
unaffected because it calls Azure OpenAI directly, so only the CAPI eval path hits the gateway.

Inject the four headers (env-overridable, defaults baked in) at the one shared point both
code paths flow through -- CapiModel._get_common_capi_parameters, whose dict is splatted onto
the generated operations that forward an unknown `headers` kwarg:
- bcal eval path: bc_eval_capi_bridge.py patches it in the bridge subprocess.
- nl2al lm_checklist judge path: evaluator/_capi_taxonomy.py, installed at import time from
  scores.py, which bceval exec_modules while resolving evaluators before running the judge.

Also set gpt-55-chat-2026-04-29 as the default external-model (added to the choices) so the
scheduled run and the new default both use it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The first test run confirmed the header injection works: the error advanced from
"taxonomy not provided" to "Invalid taxonomy value: 'DynamicsBusinessCentral' is not
allowed for X-Taxonomy-Experience. Allowed values: BizChat, WXPOAgents, AppCopilots,
Cowork, Scout, WorkIQ." BC is an app copilot -> AppCopilots. Verified locally against
CAPI for both the eval model (gpt-55-chat-2026-04-29) and the judge model (gpt-41);
the other three headers (bcal / ChatCompletion / Test) were accepted as-is.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 28, 2026 11:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds required M365 LLM API taxonomy headers to bcal and judge CAPI requests, while updating the default external model.

Changes:

  • Injects configurable taxonomy headers into both CAPI paths.
  • Defaults evaluations to gpt-55-chat-2026-04-29.
  • Adds taxonomy shim tests.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
.github/workflows/bcal-evaluation.yml Updates model defaults and taxonomy environment values.
evaluator/_capi_taxonomy.py Adds the judge-side taxonomy shim.
evaluator/scores.py Installs the judge-side shim.
src/bcbench/agent/bcal/bc_eval_capi_bridge.py Injects headers into bridge requests.
tests/test_bcal_capi_bridge.py Tests bridge taxonomy behavior.
tests/test_capi_taxonomy_evaluator.py Tests evaluator shim installation.


@pytest.fixture
def stub_capi_model(monkeypatch):
"""Stub bc_eval.capi.capi_model.CapiModel with the internal the taxonomy shim patches."""
@martinsrui-msft
martinsrui-msft merged commit e15641a into main Jul 28, 2026
392 checks passed
@martinsrui-msft
martinsrui-msft deleted the fix/bcal-capi-taxonomy-headers branch July 28, 2026 12:49
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.

3 participants