feat: Agent graph support#181
Draft
mattrmc1 wants to merge 4 commits into
Draft
Conversation
…b.com:launchdarkly/java-core into mmccarthy/AIC-2837/java-ai-sdk-agent-graph
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
Adds agent graph support — flag evaluation, graph validation, BFS traversal, graph-level tracking, and resumption tokens. Callers fetch a graph definition via
agentGraph(graphKey, context, variables), inspect or traverse the node topology, and track graph-level metrics (invocation success/failure, duration, tokens, path) plus edge-level events (redirect, handoff) throughAIGraphTracker.New types
GraphEdge— immutable edge holding targetkeyand optionalhandoffmetadata map (unmodifiable).AgentGraphNode— wraps a node key, its resolvedAIAgentConfig, and outgoingGraphEdgelist.isTerminal()returns true when edges are empty.AgentGraphFlagValue(internal) — parses the graph flag JSON protocol:root,edgesadjacency map, and_ldMeta(enabled, variationKey, version). Defensively handles malformed input without throwing.AgentGraphDefinition— the resolved graph. Key API:isEnabled(),rootNode(),getNode(key),getChildNodes(key),getParentNodes(key),terminalNodes(),createTracker(). BFS traversal viatraverse(fn, ctx)(root-to-leaves) andreverseTraverse(fn, ctx)(terminals-to-root, root always processed last). Both are cycle-safe. Node building and key collection via static helpers.AIGraphTracker— graph-level at-most-once tracking (trackInvocationSuccess/trackInvocationFailuresharing one guard,trackDuration,trackTotalTokens,trackPath) and multi-fire edge-level events (trackRedirect,trackHandoffSuccess,trackHandoffFailure). UsesAtomicReference.compareAndSet(null, value)for at-most-once. Empty token usage doesn't burn the slot. Resumption token viagetResumptionToken()/fromResumptionToken(token, client, context)with version clamping.AIGraphMetricSummary— immutable snapshot of graph tracker state (success, durationMs, tokens, path, resumptionToken). All nullable except resumptionToken.Client methods
agentGraph(graphKey, context, variables)onLDAIClient— evaluates the graph flag, validates (enabled -> root present -> all nodes reachable from root -> all child configs enabled), fetches each node'sAIAgentConfigpassinggraphKeyfor tracker correlation. Returns disabled definition on any validation failure. Emits$ld:ai:usage:agent-graphusage event.createGraphTracker(resumptionToken, context)— reconstructs anAIGraphTrackerfrom a resumption token.Other changes
NoOpAIConfigTrackerdeleted — no longer needed since all configs get real trackers.ResumptionTokensextended withencodeGraph/decodeGraphfor graph-specific tokens (fields:runId,graphKey,variationKey,version). Madepublicfor access fromAIGraphTracker.LDAIConfigTrackerImplupdates: null-arg logs downgraded warn -> debug,trackJudgeResultguards blankmetricKeyand non-finitescore,trackMetricsOfstops clock before extractor (duration tracked even if extractor throws).trackerFactorydefault version changed from0to1.agentConfigs()reordered to emit usage count before fetching configs.evaluate/buildConfig/buildConfigFromDefaultgaingraphKeyparameter for node-level tracker correlation.Test plan
./gradlew :lib:sdk:server-ai:testpassesAIGraphTrackerTest(480 lines): invocation success/failure + shared guard, duration, total tokens (including zero-usage skip), path, redirect/handoff multi-fire, base data correctness, variationKey omission, getSummary, resumption token round-trip, concurrency (20-thread contention for invocation and duration)AgentGraphDefinitionTest(445 lines): buildNodes, collectAllKeys, traverse/reverseTraverse (including cycles, single-node, diamond), rootNode/getNode/getChildNodes/getParentNodes/terminalNodes, disabled graph behavior, createTrackerLDAIClientImplTestadditions (192 lines): agentGraph usage event, enabled/disabled graph, unreachable node validation, non-enabled child config validation, graphKey threading to child trackers, createGraphTracker delegationAgentGraphFlagValueTest(276 lines): parse root/edges/meta, missing fields, disabled flag, malformed input, handoff metadata, edge with missing key skippedResumptionTokensTestadditions: graph token encode/decode round-tripsLDAIConfigTrackerImplTestadditions: trackMetricsOf extractor-failure duration tracking, null AIMetrics guard