Repository navigation
Conversation
hsadhvani
added a commit
that referenced
this pull request
Nov 3, 2026
## Why Shadow requests let a team try a candidate AI Config variation on real traffic without serving it, and compare it on the normal AI metrics. The SDK owners asked for this to be merged as experimental (TESTING.md §0). Earlier versions (#241, #247) either added an invocation hook to core or changed `config()` for everyone; this one is an opt-in wrapper, so core behavior is unchanged. ## Summary Adds `launchdarkly_ai_server.experimental.shadow_requests` with `shadow_config()`, `run_shadow()`, `ShadowTask` and `ShadowResult`. `shadow_config()` takes the same arguments as `config()`, evaluates the flag once with `variation_detail`, runs the served variation through the normal `config()` path, and starts the sampled shadows after it succeeds (in the background, or through `on_task`). Core gets three small generic seams: a private `ConfigInstance._evaluate`, a private `_parse_variation` split out of `extract_variation`, and optional `run_id`, `extra_track_data` and `span_attributes` on `execute_and_track`. Also adds the API report, a README section and the `shadow-requests` changelog scope. ## Testing approaches - Added tests that plain `config()` never calls `variation_detail` and runs no shadows, even when the flag has a shadow configuration. - Added tests that the served response, usage and events match `config()` for invoke, stream and the AG-UI stream. - Added tests for shadow selection by rule or fallthrough, expiry, malformed entries, disabled or missing variations, missing handlers and sticky sampling. - Added tests for background and deferred modes, timeouts, failures, shutdown cancellation, shadow event fields, span attributes and usage IDs. ## Feature flags N/A <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > Adds **experimental shadow requests** under `launchdarkly_ai_server.experimental.shadow_requests`: **`shadow_config()`** mirrors **`config()`** but, after the served variation succeeds, runs sampled candidate variations from flag **`shadowConfiguration`** (rule/fallthrough slot, expiry, sticky sampling). Responses are discarded; telemetry reuses the primary **`runId`** with **`isShadow`** and related fields. Shadows run in the background (30s default, cancelled on **`shutdown()`**) or via **`on_task`** + **`run_shadow()`** for worker queues. > > **Plain `config()` is unchanged**—no shadows and no **`variation_detail`**. Core changes are small shared hooks: **`_fetch_variation` / `_parse_variation`**, **`ConfigInstance._evaluate`** and **`_judge_stream`** (stream/AG-UI start shadows after judges), **`make_track_data`**, and optional **`run_id`**, **`extra_track_data`**, **`span_attributes`**, and **`feature_flag_event=False`** on **`execute_and_track`**. Docs, **`shadow-requests`** changelog scope, and a new API report accompany the module plus a large **`test_shadow_requests.py`** suite. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 133a542. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
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.
Why
Shadow requests let a team try a candidate variation on real traffic without serving it and compare it on the normal AI metrics. This replaces the closed #241, which shipped it as experimental behind a new core invocation hook. We are not shipping new features as experimental, and a judges-style core design is easier to read.
Summary
Adds a core
shadows.pymodule thatconfig().invoke()and.stream()call directly, the same way they call judges.config()evaluates once withvariation_detail, picks the shadows for the matched rule or fallthrough, and runs the sampled ones in the background withshadow_timeout_s(default 30).skip_shadows=Truereturns them as picklableShadowTasks forrun_shadow(task, handlers). Shadow events reuse A'srunIdand addisShadow,primaryVariationKey,shadowRequestId,ownerKindandownerId.shutdown()cancels running shadows.Testing approaches
Feature flags
N/A
Note
Overview
Adds shadow requests to
config().invoke()and.stream(): after the primary variation is chosen, the SDK can run other pinned variations on the same input in the background, emit normal$ld:ai:*metrics withisShadow: true, and never change the caller’s response, latency, or primary events.Evaluation and wiring.
extract_variation(..., with_reason=True)usesvariation_detailwhen available so shadows attach to the matched rule or fallthrough slot fromshadowConfiguration. Primary and shadow runs share onerunId.execute_and_track/ streaming gain optionalrun_id,extra_track_data, and handlerspan_attributes(via__ld_span) for shadow OTel tags.API surface.
config()addsskip_shadowsandshadow_timeout_s(default 30s). Withskip_shadows=True, responses expose picklableShadowTasklists (invoke) or on the streamdoneevent; workers run them withrun_shadow, mirroring deferred judges. New typesShadowTask/ShadowResultare exported; README and the API report are updated.shutdown()cancels in-flight shadow tasks.Shadow execution rules. Sampled shadows use a stripped config (no tools, judges, or nested shadow config), no tool calls, timeout-wrapped handlers, and errors/timeouts recorded on
ShadowResultwithout affecting the primary path. Extensivetest_shadows.pyplus test mocks that dropvariation_detailwhen simulating older clients.Reviewed by Cursor Bugbot for commit 378a915. Bugbot is set up for automated code reviews on this repo. Configure here.