Repository navigation
feat(AIC-3408): add Vercel AI SDK messages and agents adapters - #103
Conversation
Route LaunchDarkly AI Configs through the official Python ai runtime and AI Gateway, including native graph execution and a direct-model opt-out. Co-authored-by: Cursor <cursoragent@cursor.com>
Use the shared span helpers in production, trace tool execution, and emit provider-compatible strict schemas for messages output.
…cel-adapter # Conflicts: # .release-please-manifest.json
CancelledError is not an Exception, so those spans were ending unmarked or as abandoned. Handoff tools now ignore extra model arguments instead of raising TypeError. Co-authored-by: Cursor <cursoragent@cursor.com>
Tool loops were collapsing every inference into one span and forcing the final schema onto intermediate tool calls, and the native graph span omitted the token totals the other adapters write.
jeffdupont
left a comment
There was a problem hiding this comment.
Reviewed with the 1.0 freeze in mind. Despite the name, this isn't a wire format or a JS bridge. It wraps Vercel's own Python runtime, the ai package on PyPI (vercel-labs/ai-python, 0.7.0, classifier "Development Status :: 4 - Beta"), and it's the Python half of launchdarkly/js-ai-sdk#83, which merged on 29 Sep. The public names match JS one for one (vercel_messages, create_vercel_messages_handler, vercel_agents, create_vercel_agents_handler, vercel_graph, to_vercel_agents), and model_id.py is a line-for-line port of JS model-id.ts. Tests pass at 89c52d5: make test 1523 passed, 15 skipped; make typecheck and make lint clean, all exit 0. The branch conflicts with main only in .release-please-manifest.json, because main has since released 0.2.4.
Three things I'd like settled before GA:
- An AI Config can replace the Gateway API key and the model.
_request_paramsforwardsextra_headersandextra_queryfrommodel.parametersand puts every other unknown key intoextra_body. The Gateway client applies those headers after its own (ai/providers/ai_gateway/client/_client.py,request_headers.update(headers)). I reproduced it at 89c52d5 with the HTTP call stubbed. A config withextra_headers: {Authorization: "Bearer attacker-key", ai-language-model-id: "openai/gpt-5-pro"}sends thatAuthorizationheader in place of the customer'sAI_GATEWAY_API_KEY, and theai-language-model-idheader switches the model even thoughmodelis on the owned list.providerOptions.gateway.onlyalso reaches the request body, so a config can change which upstream provider serves the call. This is the same class of problem as #107: anyone who can edit an AI Config can redirect the customer's traffic. The agents handler (build_request_params) and the native graph share the code. Inline comment below. - The native graph doesn't send the per-node events the spec now requires. Since monorepo #26 (AIC-3211, merged into this branch through #114),
TESTING.md§2.2 has every native adapter send$ld:ai:graph:nodewithnodeKey/indexwhen it enters a node. The other three Python adapters do.to_vercel_agentsdoesn't, and it sends onlygeneration:successper node. JStoVercelAgentssends per-nodeduration:totalandtokens:*, plus$ld:ai:graph:path, which §2.2 now says not to send. So one graph run gives Monitoring three different event sets: Python Vercel, JS Vercel, and the other adapters. Event names are a data contract, so this should be fixed in both languages before 1.0. Inline comment below. - Should these packages ship as 1.0? The
aidependency is a 0.x public beta, and the bound isai>=0.7with no upper limit, so any 0.8 release can break it. The experimental lifecycle draft (TESTING.md §0) covers experimental names insidelaunchdarkly_ai_server. It doesn't say what to do with a whole provider package. I'd suggest keeping both Vercel packages on 0.x in both languages when the rest go to 1.0, writing that rule into §0 (a provider package built on a pre-1.0 runtime stays 0.x), and cappingai<0.8until then. The GA plan's package count doesn't include these two Python packages yet.
Smaller notes:
- The spec PR, launchdarkly/ai-sdks-monorepo#19, has no reviews, hasn't been updated since 22 Sep, and still points the python submodule at e83fa11. Its A.14 says
experimental_evaluateis "Not available in officialai0.7", and it skipsvercel_evaluatefor Python on that basis. Butai0.7.0 does exportai.ops.experimental_evaluate(model, state, questions, *, params=None). So either Python gets avercel_evaluate, or the spec should give a different reason for leaving it out. Since it wraps an upstreamexperimental_API, JSvercelEvaluateprobably belongs on an experimental entry point and not the package root. #19 also needs the$ld:ai:graph:nodeline in §2.x.3. - The last Bugbot comment (step limit,
handler.py:422) has no reply yet. With tools andoutputFormatboth set, a final answer that arrives on step 10 hitscontinueand then theRuntimeError. The structured follow-up also costs an extra model call and discards the first answer. - Graph-level events use
make_track_data(definition.root, ...), the same as the other adapters. Whatever #121 decides about whichconfigKeygraph events carry applies here too. - JS #83 spreads the whole
model.parametersbag intogenerateText/streamText(modelSettings), soheadersandproviderOptionsprobably reach the request there as well. I haven't checked this at runtime in JS. Since #83 has merged, it likely needs a follow-up there.
… release (#155) ## Summary - `launchdarkly-ai-vercel-agents` and `launchdarkly-ai-vercel-messages` are registered with release-please on #103, which would tag them and open GitHub releases without publishing to PyPI. - This adds the same PyPI publish jobs and manual-publish choices the other handler packages use. The jobs stay idle until that adapter PR lands the packages and the release-please config. ## Test plan - [ ] Confirm the workflow lists `packages/vercel-agents` and `packages/vercel-messages` in the dispatch choices, release outputs, and publish jobs. - [ ] After #103 merges, the next release-please release of those packages publishes to PyPI. Made with [Cursor](https://cursor.com)
Drop header, query, and extra-body passthrough, keep the structured follow-up off the tool-step budget, emit graph node events, and cap ai below 0.8. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Addressed the open review notes in 1389d04:
|
jeffdupont
left a comment
There was a problem hiding this comment.
Re-checked at 1389d04. The fix commit covers my 2 Oct points: the parameter allowlist with attacker-key tests, $ld:ai:graph:node with no path event, 0.x with ai>=0.7,<0.8, and the extra structured turn for the step limit. Tests, ruff and mypy pass locally.
Still blocking:
- Merge conflicts in 6 files:
.release-please-manifest.json,.sdk_metadata.json,AGENTS.md,pyproject.toml,release-please-config.jsonandtests/test_cross_handler_parity.py. CI hasn't run on this head. - Spec says Vercel is JS-only. ai-sdks-monorepo
TESTING.mdA.14 (from #51) saysvercel-messagesandvercel-agentsare not Python packages, andapi-surface/python/has no Vercel lists. I'll settle that line in the monorepo. Either the spec gains Python rows or this doesn't ship. - Adopt main's usage-reporting contract (#145). Other handlers keep
PACKAGE_NAMEin_version.py, callreport_usage(...)in each public helper, and point release-pleaseextra-filesat_version.py. This PR keeps__version__in__init__.pyand has noreport_usage, so Python Vercel would send no$ld:ai:sdk:usageevents while JS does.
Non-blocking: other Python handlers now emit AG-UI text and tool events (#161-#165). JS Vercel doesn't either, and the spec doesn't require it.
…cel-adapter Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # .release-please-manifest.json # .sdk_metadata.json # AGENTS.md # pyproject.toml # release-please-config.json # tests/test_cross_handler_parity.py
…the Vercel packages - _version.py holds PACKAGE_NAME and __version__; release-please bumps it. - Every public helper reports $ld:ai:sdk:usage; wrappers use the internal builders. - capture_content defaults to None so the process default applies. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ 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 1ab101a. Configure here.
…e graph key, and trace setup errors Co-authored-by: Cursor <cursoragent@cursor.com>
jeffdupont
left a comment
There was a problem hiding this comment.
Code looks good and lines up with the JS surface. One blocker: main's TESTING.md still says Vercel is JS-only (~L3013), so this needs ai-sdks-monorepo#19 merged first. Can you also add the Python api-surface lists for vercel-agents/messages and the python tags in usage-ids.txt? #19 doesn't include those, so api-surface-check will catch it later otherwise. Happy to approve once the spec side is in.
…cel-adapter Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # .release-please-manifest.json # .sdk_metadata.json # AGENTS.md # README.md # main.py # release-please-config.json # uv.lock
|
Merged On @jeffdupont's spec blocker: the "Vercel is JS-only" line at main's TESTING.md (~L3013) and the missing Python |

Summary
launchdarkly-ai-vercel-messagesandlaunchdarkly-ai-vercel-agentspackages that map LaunchDarkly AI Configs onto the official Pythonairuntime and AI Gatewaycreator/modelids.Test plan
make format-check && make lint && make typecheck && make testinpython/uv run python main.py vercel-agents launch-darkly-documentation-summarizer "..."and confirm a non-empty response, non-zero usage, and a judge scorevercel-messagesandvercel-directsuccess/failure keys fromintegration-config.jsonnative-graph-vercelfails cleanly if the Gateway free tier still blocks the graph modelMade with Cursor