Repository navigation
fix(judges): a judge config's outputFormat must not reach the provider - #86
Conversation
|
bugbot run |
95c088d to
627f1fa
Compare
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 627f1fa. Configure here.
627f1fa to
7b252b5
Compare
A judge is an ordinary AI Config, so it can carry its own outputFormat (a JSON
Schema). Two readers disagreed about what to do with it: judge_scoring.py owns
the verdict contract and parses the reply as {score, reasoning}, while every
provider handler (openai/claude/langchain) honors config["outputFormat"] and
hard-constrains generation to the author's schema. The model ends up obeying a
shape the judge parser then rejects, so the judge silently produces no score
and tracks no metric.
This is live: a read-only sweep of 215 AI Config variations across four
LaunchDarkly connections (2026-09-11) found 30 with mode: judge, and one of
those, some-judge-config in staging project test-ai-config-project, has
outputFormat set to a schema with no score field at all. That judge can never
return a valid verdict.
Adds without_output_format() to judge_scoring.py, next to the rest of the
verdict contract. It returns a shallow copy of the config with outputFormat
removed (or the input itself when absent, never mutated) and logs a warning
naming the judge key when it actually removed something. Applied on every
path that hands a judge config to a handler: run_judges before
execute_and_track, build_judge_tasks before the config is stored on a
JudgeTask, run_judge defensively for older serialized tasks, and the offline
evaluations runner when it resolves each judge.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7b252b5 to
2eb0534
Compare
jeffdupont
left a comment
There was a problem hiding this comment.
This looks right to me, and it covers all four judge paths, including offline evaluations (evaluations/runner.py:373).
I'm holding off on approving only because the spec has no rule for this yet. §8.8.3 says a judge config has no tools but says nothing about outputFormat, and §1.9 says handlers enforce outputFormat whenever it is present. A monorepo PR adding the rule to §3.13 and §8.8.3, with a test sentence, would give js#56 (which misses the offline path) something to test against. I'll approve once that's up.
|
@jeffdupont the spec is up in ai-sdks-monorepo#82: §3.13 has the rule and required tests for the inline and deferred paths, §8.8.3 has the offline rule (one warning per judge per run) and its test, and §1.9 notes judges as the exception to handlers enforcing |
…e root (#217) #208 added `create_claude_agents_skills_handler` at `launchdarkly_ai_claude_agents.experimental.skills`. The spec now promotes it with Agent Skills (launchdarkly/ai-sdks-monorepo#59, following #86), and no release has shipped it, so it moves to the package root with no deprecated alias: - `create_claude_agents_skills_handler` is exported from `launchdarkly_ai_claude_agents`, from a new `skills` module (moved from `experimental/skills.py`). The `experimental` subpackage and its API report are deleted. - It reports `claude-agents.createClaudeAgentsSkillsHandler` instead of `experimental.createClaudeAgentsSkillsHandler`. - The README section loses its experimental notice, and the example imports from the package root. agents.md follows. - Tests: the fresh-interpreter test that the package root never loaded the experimental module is replaced by one that the root exports the handler. The usage test, and `tests/test_never_forwarded_parameters.py`'s module map, use the new module and ID. **Phantom usage events (review follow-up).** The handler called the public `skill_refs` and `write_skills`, so every run with skills also reported `client.skillRefs` and `client.writeSkills`, calls the customer never made. This was already on main from #208/#210; this PR makes the handler a core export, so it fixes it here: - Core gains a non-reporting `_skill_refs` in `launchdarkly_ai_server.skills`; `skill_refs` reports and then delegates to it. Not a public name, so no API report changes. - The handler uses `_skill_refs` and the existing `_write_skills` from `launchdarkly_ai_server.skills_fs`, as `skills_watch.py` already does. - `packages/claude-agents/tests/test_sdk_usage.py` gains a run with skills, over invoke and stream, that sends only `claude-agents.createClaudeAgentsSkillsHandler`. The test fails against the public helpers. The TypeScript twin is launchdarkly/js-ai-sdk#136, which makes the same move before it merges and likely has the same usage pattern to fix. **Tests:** rebased onto `main` after #210 squash-merged, so the branch carries only its own commits. `make api-report-check` reports every report current. `make lint`, `make format-check` and `make typecheck` pass, and the full suite passes locally. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > **`create_claude_agents_skills_handler`** graduates from `launchdarkly_ai_claude_agents.experimental.skills` to a top-level **`skills`** module and is re-exported from **`launchdarkly_ai_claude_agents`**. The experimental subpackage and its API report are removed; docs and examples import from the package root, and SDK usage is reported as **`claude-agents.createClaudeAgentsSkillsHandler`** instead of the experimental helper id. > > Runs that materialize variation skills no longer emit phantom **`client.skillRefs`** / **`client.writeSkills`** events. The handler calls internal **`_skill_refs`** and **`_write_skills`** (core adds **`_skill_refs`** as the non-reporting body behind public **`skill_refs`**). New usage tests assert invoke/stream with skills only records the factory helper. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d288473. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- <details><summary>launchdarkly-ai-server: 0.5.0</summary> ## [0.5.0](launchdarkly-ai-server-0.4.0...launchdarkly-ai-server-0.5.0) (2026-10-27) ### ⚠ BREAKING CHANGES * **skills:** promote Agent Skills to core ([#210](#210)) * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Experimental * **evaluations:** export each offline row as one trace ([#170](#170)) ([fc241df](fc241df)) * **evaluations:** normalize text behaviors ([7cee8c8](7cee8c8)) ### Features * **skills:** export the Claude Agents skills handler from the package root ([#217](#217)) ([5d46dbb](5d46dbb)) * **skills:** promote Agent Skills to core ([#210](#210)) ([715815f](715815f)) ### Bug Fixes * **client:** show a judge an output of 0 or false, and render non-string values as JSON ([7cee8c8](7cee8c8)) * **client:** strip whitespace from LD_BASE_URI, LD_STREAM_URI and LD_EVENTS_URI in init_client ([1629123](1629123)) * **judges:** a judge config's outputFormat must not reach the provider ([#86](#86)) ([d8b893d](d8b893d)) * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) * **skills:** coalesce a watcher's debounce window into one reconcile ([#215](#215)) ([b214bbd](b214bbd)) * **skills:** default FDv2SkillStore endpoints from LD_BASE_URI and LD_STREAM_URI ([1629123](1629123)) </details> <details><summary>launchdarkly-ai-python: 0.4.0</summary> ## [0.4.0](launchdarkly-ai-python-0.3.0...launchdarkly-ai-python-0.4.0) (2026-10-27) ### ⚠ BREAKING CHANGES * **skills:** promote Agent Skills to core ([#210](#210)) ### Features * **skills:** promote Agent Skills to core ([#210](#210)) ([715815f](715815f)) </details> <details><summary>launchdarkly-ai-claude-agents: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-claude-agents-0.2.6...launchdarkly-ai-claude-agents-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * **skills:** promote Agent Skills to core ([#210](#210)) * **claude-agents:** switch the built-ins off by default and load no host settings ([#201](#201)) * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Features * **skills:** export the Claude Agents skills handler from the package root ([#217](#217)) ([5d46dbb](5d46dbb)) * **skills:** promote Agent Skills to core ([#210](#210)) ([715815f](715815f)) ### Bug Fixes * **claude-agents:** switch the built-ins off by default and load no host settings ([#201](#201)) ([f99d538](f99d538)) * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-claude-messages: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-claude-messages-0.2.5...launchdarkly-ai-claude-messages-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-openai-agents: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-openai-agents-0.2.6...launchdarkly-ai-openai-agents-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-vercel-agents: 0.2.0</summary> ## [0.2.0](launchdarkly-ai-vercel-agents-0.1.1...launchdarkly-ai-vercel-agents-0.2.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-litellm-agents: 0.2.0</summary> ## [0.2.0](launchdarkly-ai-litellm-agents-0.1.1...launchdarkly-ai-litellm-agents-0.2.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-openai-messages: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-openai-messages-0.2.5...launchdarkly-ai-openai-messages-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-vercel-messages: 0.2.0</summary> ## [0.2.0](launchdarkly-ai-vercel-messages-0.1.1...launchdarkly-ai-vercel-messages-0.2.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-litellm-messages: 0.2.0</summary> ## [0.2.0](launchdarkly-ai-litellm-messages-0.1.1...launchdarkly-ai-litellm-messages-0.2.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-langchain-agents: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-langchain-agents-0.2.6...launchdarkly-ai-langchain-agents-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-langchain-messages: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-langchain-messages-0.2.5...launchdarkly-ai-langchain-messages-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-opencode-agents: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-opencode-agents-0.2.0...launchdarkly-ai-opencode-agents-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * **opencode-agents:** honor AI Config tools ([#216](#216)) * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Experimental * **evaluations:** normalize text behaviors ([7cee8c8](7cee8c8)) ### Features * **opencode-agents:** honor AI Config tools ([#216](#216)) ([b5a63ec](b5a63ec)) ### Bug Fixes * **client:** show a judge an output of 0 or false, and render non-string values as JSON ([7cee8c8](7cee8c8)) * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-typesafe: 0.2.0</summary> ## [0.2.0](launchdarkly-ai-typesafe-0.1.1...launchdarkly-ai-typesafe-0.2.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > This is a **Release Please** cut that bumps versions across the monorepo (notably `launchdarkly-ai-server` **0.5.0**, `launchdarkly-ai-python` **0.4.0**, and matching minor bumps on provider adapters). The edits are manifest entries, `pyproject.toml` / `__version__` strings, and new **CHANGELOG** sections for 2026-10-27. > > The release notes capture several **breaking** runtime changes already on `main`: **Agent Skills** move from experimental to core (`launchdarkly_ai_server.skills`), **AI Config model parameters** are now forwarded to every provider handler (configs that previously ignored them will behave differently), **Claude Agents** defaults built-in tools off and stops loading host settings, and **OpenCode Agents** now honor AI Config tools. The core client also documents judge fixes (`outputFormat` not sent to providers), URI trimming in `init_client`, skills watcher/store fixes, and experimental evaluation trace export. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 4138892. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Why
A judge is an ordinary AI Config, so it can carry
outputFormat(a JSON Schema). Two code paths read that one field with opposite intent:judge_scoring.pyignores it. It appends its formatting instructions and parses the verdict as{ score: number 0-1, reasoning: string }. The verdict contract belongs to the SDK.execute_and_track, which hands the whole judge config to the handler.openai-messagessetstext.format = json_schemafromconfig.outputFormat; claude and langchain handlers read it too.So the model is told to produce two different shapes, obeys the provider-level schema, and the judge parser then rejects the verdict. The judge produces no score and tracks no metric, silently.
This is live. A read-only sweep of 215 AI Config variations across four LaunchDarkly connections found 30 with
mode: judge, and one of those setsoutputFormatto{ message: string, script: string }. Nothing about a score. That judge can never return a valid verdict, and nothing warns its author.Fix
Strip
outputFormatfrom a judge config before it can reach a handler or be stored on aJudgeTask. One helper,without_output_format, lives injudge_scoring.pynext to the rest of the verdict contract, and is applied at the four places a judge config is handed off:run_judges, beforeexecute_and_trackbuild_judge_tasks, before the config is persisted on the taskrun_judge, defensively, so an older serialized task still behavesevaluations/runner.py), when it resolves each judge, soevals.run(...)judges are covered tooWhen the field was actually present, say so once per judge. The helper returns the same reference when the field is absent and never mutates the input, which may be a cached
extract_variationresult.Not in scope, deliberately: the score contract, the parser, the formatting instructions, and handler selection are untouched. Making the SDK honor a judge's schema, or substituting the SDK's own
{ score, reasoning }schema to get structured output for judges, are both follow-ups.🤖 Generated with Claude Code
Note
Overview
Fixes judges that silently produced no score when their AI Config defined
outputFormat: provider handlers enforced that schema while the SDK still parsed{score, reasoning}, so verdicts could never validate.Adds
without_output_formatinjudge_scoring.pyto dropoutputFormatfrom judge configs (non-mutating, one warning per judge when present) and wires it through inlinerun_judges, deferredbuild_judge_tasks/run_judge, and offline evaluations judge resolution so handlers never see a judge-owned schema.AGENTS.md now states that judge
outputFormatis ignored and belongs on the config being evaluated, not the judge. Tests cover handler visibility, task serialization, defensiverun_judge, and the evaluations path.Reviewed by Cursor Bugbot for commit 2eb0534. Bugbot is set up for automated code reviews on this repo. Configure here.