feat: Add plugin support to Client SDK#229
Merged
Merged
Conversation
tanderson-ld
self-requested a review
February 26, 2026 17:10
…ailable before registration
tanderson-ld
requested changes
Feb 27, 2026
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
tanderson-ld
approved these changes
Mar 3, 2026
abelonogov-ld
pushed a commit
that referenced
this pull request
Mar 3, 2026
🤖 I have created a release *beep* *boop* --- ## [5.6.0](LaunchDarkly.ClientSdk-v5.5.4...LaunchDarkly.ClientSdk-v5.6.0) (2026-03-03) ### Features * Add plugin support to Client SDK ([#229](#229)) ([ce5cdd9](ce5cdd9)) --- 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] > <sup>[Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) is generating a summary for commit 2c47172. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
evgenygunko
pushed a commit
to evgenygunko/CopyWordsDA
that referenced
this pull request
Mar 4, 2026
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [LaunchDarkly.ClientSdk](https://github.com/launchdarkly/dotnet-core) | nuget | minor | `5.5.4` -> `5.6.0` | --- ### Release Notes <details> <summary>launchdarkly/dotnet-core (LaunchDarkly.ClientSdk)</summary> ### [`v5.6.0`](https://github.com/launchdarkly/dotnet-core/releases/tag/LaunchDarkly.ClientSdk-v5.6.0): LaunchDarkly.ClientSdk: v5.6.0 [Compare Source](launchdarkly/dotnet-core@LaunchDarkly.ClientSdk-v5.5.4...LaunchDarkly.ClientSdk-v5.6.0) ##### Features - Add plugin support to Client SDK ([#​229](launchdarkly/dotnet-core#229)) ([ce5cdd9](launchdarkly/dotnet-core@ce5cdd9)) *** 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 --> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or PR is renamed to start with "rebase!". 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
abelonogov-ld
added a commit
that referenced
this pull request
Mar 5, 2026
* main: chore(main): release LaunchDarkly.ServerSdk.Ai 0.9.3 (#233) fix: Make defaultValue optional with a disabled default (#232) chore(main): release LaunchDarkly.ClientSdk 5.6.0 (#231) feat: Add plugin support to Client SDK (#229) # Conflicts: # pkgs/sdk/client/src/Internal/Hooks/Executor/Executor.cs
jsonbailey
pushed a commit
that referenced
this pull request
Jun 5, 2026
🤖 I have created a release *beep* *boop* --- ## [0.10.0](LaunchDarkly.ServerSdk.Ai-v0.9.4...LaunchDarkly.ServerSdk.Ai-v0.10.0) (2026-06-05) ### ⚠ BREAKING CHANGES * Move Role from LaunchDarkly.Sdk.Server.Ai.DataModel into LdAiConfigTypes * Rename LdAiConfig.ModelConfiguration to LdAiConfigTypes.ModelConfig and LdAiConfig.ModelProvider to LdAiConfigTypes.ProviderConfig * Move shared types nested in LdAiConfig into static LdAiConfigTypes — Message, ModelConfiguration, and ModelProvider * Enforce at-most-once tracking — each metric type (duration, tokens, feedback, success/error, time-to-first-token) records once per tracker; duplicates are dropped with a warning * Add ResumptionToken on ILdAiConfigTracker and ILdAiClient.CreateTracker(resumptionToken, context) for cross-process tracker reconstruction with the original runId * Add per-execution runId on all AI track event payloads for billing isolation * Change CompletionConfig to return LdAiCompletionConfig instead of ILdAiConfigTracker — obtain a tracker via config.CreateTracker() * Default LdAiCompletionConfigDefault.Enabled to true per AISDK spec (was false on the LdAiConfig builder in 0.9.x) * Remove ILdAiConfigTracker.Config property — read config fields from the LdAiCompletionConfig the caller already holds * Make LdAiConfigTracker SDK-constructed only — the public constructor is removed; obtain trackers via config.CreateTracker() or ILdAiClient.CreateTracker(resumptionToken, context) * Convert LdAiCompletionConfig and LdAiCompletionConfigDefault from records to classes — equality is reference-based instead of structural * Remove LaunchDarkly.Sdk.Server.Ai.DataModel namespace — delete unused AiConfig, Meta, Model, Provider, and Message JSON DTO classes * Split unified LdAiConfig into LdAiCompletionConfig (SDK output) and LdAiCompletionConfigDefault (user input) — Builder, New(), and Disabled move to the Default type; introduce abstract LdAiConfig and LdAiConfigDefault base types for future agent/judge modes ### Features * Add MetricSummary property on ILdAiConfigTracker summarizing recorded metrics ([44ff485](44ff485)) * Add per-execution runId on all AI track event payloads for billing isolation ([44ff485](44ff485)) * Add ResumptionToken on ILdAiConfigTracker and ILdAiClient.CreateTracker(resumptionToken, context) for cross-process tracker reconstruction with the original runId ([44ff485](44ff485)) * Change CompletionConfig to return LdAiCompletionConfig instead of ILdAiConfigTracker — obtain a tracker via config.CreateTracker() ([92f799f](92f799f)) * Convert LdAiCompletionConfig and LdAiCompletionConfigDefault from records to classes — equality is reference-based instead of structural ([92f799f](92f799f)) * Default LdAiCompletionConfigDefault.Enabled to true per AISDK spec (was false on the LdAiConfig builder in 0.9.x) ([92f799f](92f799f)) * Enforce at-most-once tracking — each metric type (duration, tokens, feedback, success/error, time-to-first-token) records once per tracker; duplicates are dropped with a warning ([44ff485](44ff485)) * Make LdAiConfigTracker SDK-constructed only — the public constructor is removed; obtain trackers via config.CreateTracker() or ILdAiClient.CreateTracker(resumptionToken, context) ([92f799f](92f799f)) * Mode-mismatch detection — log a warning and return the caller's default when the flag's _ldMeta.mode does not match the requested mode (per sdk-specs[#229](#229)) ([92f799f](92f799f)) * Move Role from LaunchDarkly.Sdk.Server.Ai.DataModel into LdAiConfigTypes ([ac7fd06](ac7fd06)) * Move shared types nested in LdAiConfig into static LdAiConfigTypes — Message, ModelConfiguration, and ModelProvider ([ac7fd06](ac7fd06)) * Non-object variation handling — log an error and return the caller's default when the variation result is not an object ([92f799f](92f799f)) * Per-message interpolation fallback — a malformed Mustache template keeps raw content for that message rather than discarding the entire config ([92f799f](92f799f)) * Remove ILdAiConfigTracker.Config property — read config fields from the LdAiCompletionConfig the caller already holds ([92f799f](92f799f)) * Remove LaunchDarkly.Sdk.Server.Ai.DataModel namespace — delete unused AiConfig, Meta, Model, Provider, and Message JSON DTO classes ([92f799f](92f799f)) * Rename LdAiConfig.ModelConfiguration to LdAiConfigTypes.ModelConfig and LdAiConfig.ModelProvider to LdAiConfigTypes.ProviderConfig ([ac7fd06](ac7fd06)) * Split unified LdAiConfig into LdAiCompletionConfig (SDK output) and LdAiCompletionConfigDefault (user input) — Builder, New(), and Disabled move to the Default type; introduce abstract LdAiConfig and LdAiConfigDefault base types for future agent/judge modes ([92f799f](92f799f)) * Tolerant LdValue parsing — missing or wrong-typed fields degrade to typed defaults instead of discarding the whole config ([92f799f](92f799f)) --- 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] > **High Risk** > The tagged release ships extensive breaking public API and telemetry/billing semantics; this PR is low-risk mechanically but merging it publishes high consumer upgrade risk. > > **Overview** > **Release Please PR** that publishes **LaunchDarkly.ServerSdk.Ai 0.10.0** by bumping `0.9.4` → `0.10.0` in `.release-please-manifest.json`, `LaunchDarkly.ServerSdk.Ai.csproj`, `SdkInfo.Version`, and `PROVENANCE.md`, and adding the **0.10.0** section to `CHANGELOG.md`. > > The changelog documents a **breaking** AI SDK realignment with the AISDK spec: split `LdAiConfig` into **`LdAiCompletionConfig`** (evaluation output) vs **`LdAiCompletionConfigDefault`** (caller input); **`CompletionConfig` returns config, not a tracker** (`config.CreateTracker()` / `ILdAiClient.CreateTracker(resumptionToken, context)`); tracker construction is SDK-only with **`runId`**, **resumption tokens**, **at-most-once metrics**, and **`MetricSummary`**; shared types move to **`LdAiConfigTypes`** and **`DataModel` is removed**; completion defaults default **`Enabled` to true**; config/records become **classes** (reference equality). Non-breaking behavior called out includes tolerant **`LdValue` parsing**, mode-mismatch and non-object variation fallbacks, and per-message Mustache fallback. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 82deb10. 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>
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.
Summary
This PR adds plugin architecture and hook-based flag evaluation lifecycle support to the LaunchDarkly Client SDK for .NET, ported from the Server SDK implementation.
Plugin System
Pluginbase class (Plugins/Plugin.cs) that provides aGetHooks()method, allowing plugins to register hooks with the SDKPluginConfigurationandPluginConfigurationBuilderto configure plugins viaConfigurationBuilder.Plugins()LdClientto initialize plugins during startup, collect their hooks, and dispose of them properly during shutdownLdClientcleanup logic to ensure proper disposal order of resourcesHook Framework
Hookbase class with virtualBeforeEvaluation,AfterEvaluation,BeforeIdentify, andAfterIdentifystage methods, plusIDisposablesupportEvaluationSeriesContext- captures flag key, context, default value, method, and environment ID for evaluation hooksIdentifySeriesContext/IdentifySeriesResult- context and result types for identify hooksMethodconstants mapping to SDK variation methods (e.g.LdClient.BoolVariation)SeriesDataBuilder- fluent API for building immutable series data passed between hook stagesHook Execution Engine
Executor/NoopExecutorimplementingIHookExecutor- manages hook lifecycle with proper forward/reverse orderingBeforeEvaluation/AfterEvaluationstage executors with error isolation - exceptions in one hook do not prevent other hooks from executingLdClient.EvaluateInternal(), wrapping flag evaluations withBeforeEvaluationandAfterEvaluationcallsGetMethodName<T>()to resolve the correctMethodconstant based on variation type and detail modeEvaluateInternaldirectly, avoiding recursive hook invocationsTest Plan
EvaluationSeriesTest- hooks execute in LIFO order (forward before, reverse after)EvaluationSeriesTest- exceptions in hook stages don't prevent other hooks from runningEvaluationSeriesTest- stage failures log expected error messages with flag key and hook nameEvaluationSeriesTest- disposing executor disposes all registered hooksPluginConfigurationBuilderTest- plugin configuration builder tests passLdClientPluginTests- end-to-end plugin/hook integration tests passdotnet test --filter EvaluationSeriesTest)Note
Medium Risk
Touches core
LdClientevaluation flow by wrapping allVariation*calls with hook execution and adds plugin registration at startup, so regressions could affect flag evaluation behavior/performance despite safeguards like noop execution when unconfigured.Overview
Adds client-side plugin support by introducing
PluginConfigurationBuilder/PluginConfigurationand wiring a newPlugins(...)option throughComponents,ConfigurationBuilder, andConfiguration.Integrates plugin lifecycle into
LdClientinitialization: builds plugin config, createsEnvironmentMetadata, collects plugin-providedHooks, instantiates a hookExecutor(orNoopExecutorwhen none), then registers plugins; hook execution is disposed with the client and logs under a newLogNames.HooksSubLog.Introduces a hook API and execution pipeline for evaluations (
Hook,EvaluationSeriesContext, stage executors) and wrapsVariationInternalso hooks run before/after evaluations; prerequisite evaluations are refactored to callEvaluateInternaldirectly to avoid triggering hooks. Adds unit tests covering hook ordering/error isolation/disposal and plugin builder/registration behavior.Written by Cursor Bugbot for commit e5d9dc0. This will update automatically on new commits. Configure here.
Example of API