Skip to content

feat: Add AgentGraph support to the AI SDK#292

Merged
mattrmc1 merged 22 commits into
mainfrom
mmccarthy/AIC-2723/agent-graph-infrastructure
Jun 22, 2026
Merged

feat: Add AgentGraph support to the AI SDK#292
mattrmc1 merged 22 commits into
mainfrom
mmccarthy/AIC-2723/agent-graph-infrastructure

Conversation

@mattrmc1

@mattrmc1 mattrmc1 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class support for agent graphs to LaunchDarkly.ServerSdk.Ai. An agent graph is a LaunchDarkly-managed directed graph whose nodes are existing LdAiAgentConfigs and whose edges describe handoffs between agents. The SDK fetches the graph flag, resolves every referenced agent config, validates connectivity, and hands the caller a typed object they can traverse and instrument — without exposing the underlying flag shape.

AgentGraph and CreateGraphTracker on ILdAiGraphClient

// New interface — does not modify ILdAiClient
public interface ILdAiGraphClient
{
    AgentGraphDefinition AgentGraph(
        string graphKey,
        Context context,
        IReadOnlyDictionary<string, object> variables = null);

    AiGraphTracker CreateGraphTracker(string resumptionToken, Context context);
}

LdAiClient now implements both ILdAiClient and the new ILdAiGraphClient. The existing ILdAiClient interface is unchanged — no members added, removed, or modified. Graph functionality lives entirely on the separate ILdAiGraphClient interface, making this a purely additive change.

AgentGraph fires a $ld:ai:usage:agent-graph usage event, evaluates the graph flag, fetches every child LdAiAgentConfig, runs connectivity validation, and returns an AgentGraphDefinition. The returned definition's Enabled property reflects the result of all validation checks combined: _ldMeta.enabled, root present, every node reachable from the root, and every child agent config fetchable and enabled. When any check fails, the SDK logs a warning and returns a disabled definition whose traversal/inspection methods are safe no-ops; only GetConfig() (raw flag value + _ldMeta) and CreateTracker() remain meaningful.

CreateGraphTracker reconstructs an AiGraphTracker from a base64url resumption token, enabling cross-process continuation (e.g. emitting graph-level events from a worker that didn't run the original evaluation).

AgentGraphDefinition

public AgentGraphNode RootNode();
public AgentGraphNode GetNode(string nodeKey);
public IReadOnlyList<AgentGraphNode> GetChildNodes(string nodeKey);
public IReadOnlyList<AgentGraphNode> GetParentNodes(string nodeKey);
public IReadOnlyList<AgentGraphNode> TerminalNodes();
public AgentGraphFlagValue GetConfig();
public AiGraphTracker CreateTracker();

public void Traverse(
    Func<AgentGraphNode, Dictionary<string, object>, object> fn,
    Dictionary<string, object> initialContext = null);

public void ReverseTraverse(
    Func<AgentGraphNode, Dictionary<string, object>, object> fn,
    Dictionary<string, object> initialContext = null);

Traverse does a breadth-first walk from the root; ReverseTraverse walks from terminals back toward the root (the root is always processed last unless it is the only node). Both are cycle-safe — each node is visited at most once. The callback receives the current node and an accumulator dictionary; whatever the callback returns is stored in the accumulator under that node's key and is visible to subsequent visits.

Node and edge types

  • AgentGraphNode — wraps an LdAiAgentConfig plus its outgoing GraphEdges and exposes IsTerminal (true when there are no outgoing edges). Per-node metrics are recorded via the wrapped config's existing CreateTracker().
  • GraphEdgerecord carrying the target Key and an optional Handoff dictionary (IReadOnlyDictionary<string, LdValue>). Handoff maps are wrapped in ReadOnlyDictionary to enforce immutability; edge lists are frozen via AsReadOnly().
  • AgentGraphFlagValue / LdMeta — the parsed raw flag value, including _ldMeta. Always non-null on the returned definition, even when the graph is disabled.

AiGraphTracker

public void TrackInvocationSuccess();       // at-most-once (shares slot with TrackInvocationFailure)
public void TrackInvocationFailure();       // at-most-once
public void TrackDuration(double durationMs); // at-most-once
public void TrackTotalTokens(Usage tokens); // at-most-once (skips slot claim when usage is empty)
public void TrackPath(IReadOnlyList<string> path); // at-most-once

public void TrackRedirect(string sourceKey, string redirectedTarget); // multi-fire
public void TrackHandoffSuccess(string sourceKey, string targetKey);  // multi-fire
public void TrackHandoffFailure(string sourceKey, string targetKey);  // multi-fire

public string ResumptionToken { get; }
public AiGraphMetricSummary Summary { get; }
public AiGraphTrackData GetTrackData();
public static AiGraphTracker FromResumptionToken(string token, ILaunchDarklyClient client, Context context);

Graph-scoped tracker emitting $ld:ai:graph:* events. At-most-once methods use Interlocked.CompareExchange so the contract holds under racing callers — a second call logs a warning and is silently dropped. TrackTotalTokens short-circuits before claiming the slot when usage is empty, consistent with LdAiConfigTracker.TrackTokens. Multi-fire methods may be called once per edge traversal. Every event carries the standard runId + graphKey + version track data (plus variationKey when non-empty). ResumptionToken is a base64url-encoded JSON payload of those fields; FromResumptionToken validates and round-trips it, throwing ArgumentException on malformed or missing-required-fields input.

graphKey propagation through LdAiConfigTracker

When a per-node tracker is created via AgentGraph (rather than via the standalone AgentConfig path), the parent graph's key is threaded through to LdAiConfigTracker so every per-node event includes graphKey in its track data and resumption token. The wire format omits empty optional fields, so existing non-graph trackers continue to round-trip exactly.

Other touched files

  • ConfigFactory.BuildAgentConfig and TrackerFactoryFor accept an optional graphKey parameter (default null). The default-path helper BuildAgentFromDefault is now internal (was private) so the graph code path can reuse it. All existing call sites are unchanged.
  • Polyfills/IsExternalInit.cs enables init accessors and positional records on the net462/netstandard2.0 targets used by the new graph types.

Migration

None required. ILdAiClient is unchanged — no new members were added. Graph functionality is on the new ILdAiGraphClient interface, which LdAiClient implements alongside ILdAiClient. Existing consumers, including hand-rolled test doubles that implement ILdAiClient, will continue to compile and work without modification. The wire format is backward-compatible (the new graphKey field on config resumption tokens is omitted when empty).

Test plan

  • dotnet build succeeds across netstandard2.0, net462, net6.0, net8.0
  • dotnet test pkgs/sdk/server-ai/test/LaunchDarkly.ServerSdk.Ai.Tests.csproj --framework net8.0 passes
  • AgentGraphDefinitionTest covers node lookup, parent/child/terminal queries, BFS and reverse-BFS traversal, cycle safety, and disabled-graph no-op behavior
  • AiGraphTrackerTest covers track-data shape, at-most-once vs. multi-fire semantics, TrackTotalTokens empty-usage short-circuit, ResumptionToken round-trip, FromResumptionToken error handling, and the Summary snapshot
  • LdAiAgentGraphConfigTest covers graphKey propagation into per-node track data and resumption tokens
  • LdAiClientAgentGraphTest covers end-to-end AgentGraph retrieval and every validation path: disabled _ldMeta, missing root, unreachable node, and unfetchable/disabled child agent config — plus the $ld:ai:usage:agent-graph usage event
  • Reviewer confirms graph event names, at-most-once semantics, and resumption-token wire format match the cross-SDK contract

Note

Medium Risk
Large additive public API and shared telemetry/resumption-token contracts must stay aligned with other AI SDKs; optional ILogger.Debug is a compile-time break only for custom ILogger implementations outside the package.

Overview
Adds agent graph support to the server AI SDK: LdAiClient now also implements ILdAiGraphClient with AgentGraph and CreateGraphTracker, while ILdAiClient stays unchanged.

AgentGraph evaluates a graph flag, parses root / edges / _ldMeta, validates connectivity and that every referenced agent config is enabled, then returns an AgentGraphDefinition (or a disabled shell with empty nodes when validation fails). The definition exposes node lookup, BFS Traverse / terminal-up ReverseTraverse, and CreateTracker for graph-scoped metrics.

New types include AgentGraphNode, GraphEdge (optional handoff payload), AgentGraphFlagValue, AiGraphTracker ($ld:ai:graph:* events, at-most-once vs multi-fire handoff/redirect), and AiGraphMetricSummary. Graph node fetches thread an optional graphKey into LdAiConfigTracker track data and resumption tokens when absent from standalone AgentConfig calls.

Supporting tweaks: ILogger.Debug (+ adapter), optional graphKey on ConfigFactory.BuildAgentConfig, and an IsExternalInit polyfill for older TFMs. Broad xUnit coverage for definition, tracker, client validation paths, and graphKey propagation.

Reviewed by Cursor Bugbot for commit d64294c. Bugbot is set up for automated code reviews on this repo. Configure here.

@mattrmc1 mattrmc1 changed the title feat!: AgentGraph support feat!: Add AgentGraph support to the AI SDK Jun 16, 2026
@mattrmc1 mattrmc1 requested a review from jsonbailey June 16, 2026 19:38
@mattrmc1 mattrmc1 marked this pull request as ready for review June 16, 2026 19:38
@mattrmc1 mattrmc1 requested a review from a team as a code owner June 16, 2026 19:38
Comment thread pkgs/sdk/server-ai/src/Graph/AgentGraphDefinition.cs
Comment thread pkgs/sdk/server-ai/src/Graph/AiGraphTracker.cs
Comment thread pkgs/sdk/server-ai/src/LdAiClient.cs
Comment thread pkgs/sdk/server-ai/src/Graph/AiGraphTracker.cs
Comment thread pkgs/sdk/server-ai/src/LdAiClient.cs Outdated
Comment thread pkgs/sdk/server-ai/src/Graph/AiGraphTracker.cs
@mattrmc1 mattrmc1 changed the title feat!: Add AgentGraph support to the AI SDK feat: Add AgentGraph support to the AI SDK Jun 17, 2026
Comment thread pkgs/sdk/server-ai/src/Graph/AiGraphTracker.cs
Comment thread pkgs/sdk/server-ai/src/LdAiClient.cs

@jsonbailey jsonbailey 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.

Reviewed the graph implementation against the shipped js-core and python-server-sdk-ai SDKs and reconciled the differences against the AI SDK spec (sdk-specs, specs/AISDK-ai-sdk/). Security review came back clean (resumption-token decode, at-most-once concurrency, fail-closed validation, graphKey back-compat all verified).

Two spec-compliance items to fix (reverse-traverse no-terminal behavior, validation log level) plus an out-of-scope version-coercion change and a couple of low-severity items inline. Where js-core and python disagreed, the spec sided with js-core on forward-traverse ordering (BFS) and trackPath at-most-once, so .NET is correct on those — no change needed there.

Comment thread pkgs/sdk/server-ai/src/Graph/AgentGraphDefinition.cs Outdated
Comment thread pkgs/sdk/server-ai/src/LdAiClient.cs Outdated
Comment thread pkgs/sdk/server-ai/src/Config/ConfigFactory.cs Outdated
Comment thread pkgs/sdk/server-ai/src/Graph/AiGraphTracker.cs Outdated
Comment thread pkgs/sdk/server-ai/test/AiGraphTrackerTest.cs
Comment thread pkgs/sdk/server-ai/src/LdAiConfigTracker.cs Outdated
Comment thread pkgs/sdk/server-ai/test/LdAiClientAgentGraphTest.cs Outdated
@mattrmc1 mattrmc1 requested a review from jsonbailey June 18, 2026 18:28
Comment thread pkgs/sdk/server-ai/test/AgentGraphDefinitionTest.cs Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 08075f1. Configure here.

Comment thread pkgs/sdk/server-ai/src/Graph/AgentGraphDefinition.cs Outdated
@mattrmc1 mattrmc1 requested a review from jsonbailey June 22, 2026 21:30

@jsonbailey jsonbailey 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.

Follow-up on _ldMeta.version handling: it should be carried opaquely — if set, use the value as-is; if absent, default to 1; no other coercion. A few spots still clamp a non-positive value to 1. Details inline.

Comment thread pkgs/sdk/server-ai/src/LdAiClient.cs Outdated
Comment thread pkgs/sdk/server-ai/src/LdAiConfigTracker.cs Outdated
Comment thread pkgs/sdk/server-ai/src/Graph/AiGraphTracker.cs Outdated
@mattrmc1 mattrmc1 merged commit c81b28f into main Jun 22, 2026
17 of 18 checks passed
@mattrmc1 mattrmc1 deleted the mmccarthy/AIC-2723/agent-graph-infrastructure branch June 22, 2026 22:40
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.

2 participants