Repository navigation
fix(evaluations): send criterion errors as errorMessage and skip criteria for rows that did not generate - #228
Merged
Conversation
…eria for rows that did not generate A criterion ERROR event now carries only a top-level errorMessage, with "Unknown error" as the fallback. The nested error object, whose code was a string cause name ingest never reads, is no longer sent, and errorCode stays omitted until integer codes are assigned. The cause names remain on the internal result, where the evaluate span still reports them as error.type. An error whose message is empty now falls back to "Unknown error" instead of sending the repr of the internal error mapping. A row whose generation did not complete is no longer scored: no judge or scorer runs for it, it takes no concurrency slot, and no criterion event is tracked for it. Its generation ERROR event already settles the row as failed, so a criterion event for it would be unaccounted for. The generation_incomplete cause is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aknight-ld
marked this pull request as ready for review
October 27, 2026 20:07
3 of 4 tasks
jeffdupont
approved these changes
Oct 28, 2026
donei003
added a commit
that referenced
this pull request
Oct 29, 2026
Promotes Evaluations from Code from `launchdarkly_ai_server.experimental.evaluations` to the package root. This follows the Agent Skills promotion in #210. > **Draft.** This PR releases together with launchdarkly/js-ai-sdk#181. A Jira ticket is not linked yet. ## Migration Import Evaluations from Code names from `launchdarkly_ai_server` instead of `launchdarkly_ai_server.experimental.evaluations`. The names, signatures and behavior do not change. ```python # before from launchdarkly_ai_server.experimental.evaluations import EvalTool, init_evaluations # after from launchdarkly_ai_server import EvalTool, init_evaluations ``` This is a breaking change (`!`). It bumps the minor version while the package is 0.x. ## What changed - **Root exports.** The 14 names that `experimental/evaluations.py` exported are now in `launchdarkly_ai_server.__all__`, with the same spelling. No name collides with a current root export. `launchdarkly-ai-python` gets the names through its existing `import *`. - **Experimental module removed.** `experimental/evaluations.py` is deleted. `experimental/__init__.py` stays as a docstring only, so a future experimental feature has a place to go. - **Usage ID.** `init_evaluations` now reports `client.initEvaluations`, not `experimental.initEvaluations`. Queries that count the old ID must also count the new one. - **API reports.** Regenerated with `make api-report`. The names are in `launchdarkly_ai_server.txt`, and `launchdarkly_ai_server.experimental.evaluations.txt` is deleted. - **Changelog.** The three `evaluations` rows are removed from `changelog-sections`, so `feat(evaluations):` lands under Features. The `AGENTS.md` scope examples now use a placeholder scope. - **Docs.** The client README, the `launchdarkly-ai-python` README, `packages/client/agents.md` and `AGENTS.md` no longer call Evaluations from Code experimental. The README heading anchor changed from `#run-an-evaluation-from-code-experimental` to `#run-an-evaluation-from-code`. ## Tests - `TestPackageExports` checks that every evaluations name is on the root and that `experimental.evaluations` no longer imports. - `test_public_surface.py` adds the 14 names to the root snapshot. - `test_reexports.py` checks that the barrel re-exports the names. - `test_core_does_not_import_experimental_code` now checks only `launchdarkly_ai_server.experimental`, because the root loads `evaluations`. A new test checks that `lifecycle` imports no evaluations module. - `test_sdk_usage.py` expects `client.initEvaluations`. ## Import time `import launchdarkly_ai_server` now loads the `evaluations` package. Measured with `python -X importtime`, it adds about 33 ms to a root import of about 130 ms. The largest part is `evaluations.api`. ## Checks - [x] `make lint`, `make format-check`, `make typecheck`, `make api-report-check`: pass - [x] `python -m scripts.api_breaking pr --base origin/main`: no breaking changes to the public API - [x] `make test`: 3640 passed, 18 skipped - [ ] Run one evaluation end to end against staging with the root import ## Conflicts The open evaluations PRs #228, #227, #143, #135 and #92 edit the same test files. Merge them first, or rebase them after this PR. ## Related PRs Releases together with: - launchdarkly/js-ai-sdk#181 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > **Evaluations from Code** moves from `launchdarkly_ai_server.experimental.evaluations` to the **`launchdarkly_ai_server` package root** (and through **`launchdarkly-ai-python`** re-exports). The experimental shim is **removed**; callers should import `init_evaluations`, `EvalTool`, `Judge`, `Scorer`, and the rest from the root instead. > > The **public API contract** shifts accordingly: generated **`launchdarkly_ai_server.txt`** now lists the evaluations surface, and **`launchdarkly_ai_server.experimental.evaluations.txt`** is deleted. **`init_evaluations`** usage telemetry changes from **`experimental.initEvaluations`** to **`client.initEvaluations`**. > > **Release/changelog policy** is updated: **`evaluations` is no longer an Experimental scope** in `release-please-config.json`, and **`AGENTS.md`** / READMEs describe Evaluations (and Skills) as core, with docs and examples using root imports and a non-experimental README anchor. > > Tests and snapshots (**`test_public_surface`**, **`TestPackageExports`**, **`test_reexports`**, lifecycle import guards) enforce the new export layout and that **`experimental.evaluations`** cannot be imported. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 3ee3612. 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Criterion
ERRORevents now report failures the way ingest reads them, and rows whose generation failed are no longer scored.Behavior change
ERRORevent carries a top-levelerrorMessage, which falls back to"Unknown error".error: {code, message}object is no longer sent. Itscodewas a string cause name that ingest ignores.errorCodestays omitted until integer codes are assigned.error.typeon theevaluatespan."Unknown error". Before, it sent the repr of the internal error mapping.ERRORevent already records the row as failed, so a criterion event for it was never accounted for. These rows no longer take a concurrency slot. Thegeneration_incompletecause is removed.The events module isn't part of the public API report, so there is no API change.
Tests
invalid_judge_output,scorer_raised,invalid_score) asserterrorMessageand the absence oferroranderrorCode. They tell causes apart through the internal results."Unknown error"fallback.make lint format-check typecheck api-report-checkanduv run pytestpass: 3636 passed, 18 skipped.🤖 Generated with Claude Code
Note
Overview
Criterion ERROR events now match what ingest expects: only a top-level
errorMessageis sent (with"Unknown error"when the message is empty). The nestederrorobject is dropped fromCriterionEventPayloadand tracking payloads; string cause codes stay on internal results anderror.typeon evaluate spans.Failed generation rows are no longer scored.
_run_criteria_for_resultsruns judges and scorers only for rows withstatus == "COMPLETE", so errored rows get no criterion events and no concurrency slots—the generation ERROR event alone accounts for the row. Thegeneration_incompletecriterion path is removed from scorer/judge helpers and docs.Tests assert wire shape (no
error/errorCode), cover scorer/judge failures via internal results, and replace the old “generation incomplete criterion event” case with a two-row test that proves only the successful row is judged/scored.Reviewed by Cursor Bugbot for commit cc12093. Bugbot is set up for automated code reviews on this repo. Configure here.