Skip to content

feat(AIC-3210): support graph().stream() in the Python AI SDK - #104

Merged
jeffdupont merged 3 commits into
mainfrom
jdupont/AIC-3210/support-graph-streaming
Sep 25, 2026
Merged

jeffdupont merged 3 commits into
mainfrom
jdupont/AIC-3210/support-graph-streaming

Conversation

@jeffdupont

@jeffdupont jeffdupont commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Implements graph().stream() with TypeScript-parity event shape (node_start / chunk / node_done / handoff / done), shared handoff routing with invoke(), call-time conversation binding, and ld.ai.graph OTel parenting (including aligned invoke() span).
  • Adds §3.15a unit coverage (test_graph_stream.py) and the graph-streaming example wired through main.py.
  • Keeps __handoff_* tool wrappers sync so multi-edge routing can record the chosen edge without awaiting.

Follow-ups

  • Invoke span ERROR status on failure (parity with stream / A.4): AIC-3440
  • Per-event helper for graph().stream() callers, so the examples stop hand-rolling the type dispatch: AIC-3465
  • Appendix A.13 already requires Python. The spec change is ai-sdks-monorepo#20, which is merged. This pull request is the implementation that requirement points at.

Test plan

  • pytest packages/client/tests/ — 516 passed (the plan originally recorded 515; one more test is in the tree now)
  • mypy on graph.py and tracking.py — no issues
  • ruff check and ruff format --check on graph.py, tracking.py, examples/graph_streaming.py, and main.py — clean
  • Live python main.py graph-streaming travel-agent-flow "I was double charged for my flight": exit 0, model text on stdout, one [conversation] line, [node_start] / [node_done] for travel-agent-orchestrator, Usage input 892 / output 64 / total 956 matching that node. No handoff, which is valid for a single-node run. No JSON file. stderr had no RuntimeError or aclose
  • Live failure key travel-agent-flow-wrong-key: exit 1, one Error: Agent graph "travel-agent-flow-wrong-key" is disabled, no unhandled rejection, no JSON file
  • Monorepo integration-config.json graph-streaming uses those same keys (travel-agent-flow / travel-agent-flow-wrong-key). No integration-config-local.json override. main.py dispatches graph-streaming to examples.graph_streaming, and both keys were run against that example

Spec: TESTING.md §3.15a and Appendix A.13. TypeScript: launchdarkly/js-ai-sdk#84.

Jira: AIC-3210

jeffdupont and others added 2 commits September 23, 2026 14:15
…graph-streaming example

Co-authored-by: Cursor <cursoragent@cursor.com>
…top claiming the global tracer

CI on #104 failed three separate ways. All three are fixed here.

1. `stream_route` was added as a required keyword-only argument to
   `GraphDefinition.__init__`, which broke every direct construction of the
   type: 63 tests across claude-agents, langchain-agents and openai-agents
   failed with `TypeError: missing 1 required keyword-only argument`. Those
   packages consume a `GraphDefinition` but never build one outside tests, so
   the fix is at the source rather than in their helpers — none of their test
   files are touched. `_build_graph` now returns `stream_route` alongside the
   definition, mirroring `buildGraph`'s `{ def, graphTrackData, streamRoute }`
   in the JS SDK and keeping `resolve_graph()`'s contract stable. TESTING.md
   never lists stream_route on the GraphDefinition surface, and A.2's live-vs-
   disabled key parity still holds.

2. `test_graph_stream.py` called `trace.set_tracer_provider()` at import.
   That global is first-writer-wins, and claude-agents' `test_handler.py`
   already claims it at import and collects first alphabetically, so the new
   file's exporter received nothing and 6 OTel assertions failed on `0 >= 1`.
   The suite now patches `get_tracer` per test instead — `graph.py` imports
   `trace` inside its functions, so that is the seam. Both file orders now
   pass; previously each order broke whichever file lost the race.

3. The two new files were never formatted, failing `ruff format --check`.

Verified locally: uv lock, ruff check, ruff format --check, mypy
packages/*/src, uv run pytest (1328 passed, 11 skipped), and uv build for
every package all exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jeffdupont
jeffdupont marked this pull request as ready for review September 23, 2026 22:14

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

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 b03ceb1. Configure here.

Comment thread packages/client/src/launchdarkly_ai_server/graph.py Outdated
A timeout or task.cancel() was stamping launchdarkly.stream.abandoned on the graph span while handler spans recorded launchdarkly.run.cancelled.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread examples/graph_streaming.py
Comment thread packages/client/src/launchdarkly_ai_server/graph.py
@jeffdupont
jeffdupont merged commit 25a345a into main Sep 25, 2026
7 checks passed
@jeffdupont
jeffdupont deleted the jdupont/AIC-3210/support-graph-streaming branch September 25, 2026 21:25
@github-actions github-actions Bot mentioned this pull request Sep 25, 2026
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