Repository navigation
feat: add LiteLLM messages and agents integrations - #75
Conversation
Add proxy-backed messages and Agents SDK handlers so AI Configs can route across LiteLLM-supported providers without falling back to OpenAI.
Register catalog metadata, compose instruction-only history correctly, and keep proxy credentials out of chat-completion bodies.
Test files sit outside the package tsconfig include, so these mismatches never reached the build but showed up as editor diagnostics. Co-authored-by: Cursor <cursoragent@cursor.com>
jeffdupont
left a comment
There was a problem hiding this comment.
Parity note alongside my review of python #102 (launchdarkly/python-ai-sdk#102) and the spec in launchdarkly/ai-sdks-monorepo#18. Tests pass at 093e771: yarn test exit 0 (litellm-agents 33, litellm-messages 26, and every other workspace), and yarn typecheck and yarn build both exit 0. The branch is 30 commits behind main (d32bede) and conflicts in six files: .release-please-manifest.json, .sdk_metadata.json, AGENTS.md, README.md, release-please-config.json and tsconfig.json. They look like list conflicts with the Vercel packages that landed on main. Every client export these packages import is still on main, but I haven't built the merged tree.
Things I'd like settled before merge:
- Config parameters reach the proxy request. Since the Bugbot fix,
api_keyandbase_urlare stripped, but that's a denylist of two. I reproduced it with an injected client:api_base,user_config,extra_headersandmetadatafrommodel.parametersall show up in thechat.completions.createbody. Whether the proxy acts onapi_base/user_configdepends on its client-side credential settings. I haven't tested that against a real proxy. In Python there's no proxy in the way, and the same pattern sends the customer's provider key to whatever host the config names (reproduced on python #102). I'd suggest one allowlist rule in both languages, written into the spec, as on #73 / python #107. toLiteLLMAgentshas no telemetry. It opens no span, sends no$ld:ai:graph:*events, and has nocontextoption. Python's version emits both. The JS openai-agents native graph onmainemits$ld:ai:graph:node,handoff_success,generation:success,invocation_success/failure,duration:totalandtotal_tokens. Monorepo #18 says this adapter satisfies §2.2, and §2.2 requires those events.setTracingDisabled(true)turns off OpenAI Agents tracing for the whole process as soon as a handler is created (handler.ts:408,native-graph.ts:131). A customer who also uses@openai/agentsdirectly loses those traces when they add this package. The Runner's per-runtracingDisabledoption (agents-core/dist/run.d.ts:98) limits it to our runs.- The public names and data don't match Python.
createLiteLLMAgentHandlerhere,create_litellm_agents_handlerin Python. The other agent packages use the same name in both languages, and this one is about to freeze.- When the config has no provider,
gen_ai.provider.namedefaults toopenaihere andlitellmin Python. - Content capture in the messages handler writes only the legacy
gen_ai.prompt.N.*/gen_ai.completion.0.*attributes. Every other JS handler, and Python's litellm-messages, usesetInputContentAttributes/setOutputContentAttributes. - Agent parameters: snake_case
max_tokens/top_pare ignored here and honoured in Python.
- Experimental. Same as on python #102: I'd keep both LiteLLM packages at 0.x, out of the 1.0 cut, and marked experimental until the two languages match. Monorepo #33 needs a line saying how a whole provider package counts as experimental.
Smaller notes:
createLiteLLMAgentHandler()throws at construction when there's no base URL (handler.ts:55), but the messages handler throws at call time (handler.ts:197). It would help to pick one and write it into A.13.modelForpasses the parameter bag as the third constructor argument whenever the bag has amodelkey (handler.ts:79). That argument isOpenAIChatCompletionsModelOptions, whose only field isstrictFeatureValidation(agents-openai/dist/openaiChatCompletionsModel.d.ts:8), so the branch has no useful effect.- Streamed tool calls run one after another (
handler.ts:443), but invoke runs them withPromise.all. Python runs both in parallel. That's harmless, just worth knowing.
Allowlist model settings, rename the agents factory, emit native graph telemetry, and disable Agents SDK tracing for these runs only. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Addressed the review on
Left as-is: convenience wrappers still drop |
jeffdupont
left a comment
There was a problem hiding this comment.
Re-checked at 6f67463. Thanks, most of my 2 Oct points are fixed: parameter allowlist, per-run tracingDisabled, factory name, litellm provider default, content capture helpers, construct-time throw, and parallel streamed tools.
Still blocking:
- Merge conflict with main in
.release-please-manifest.json. verbosityis dropped.litellm-agents/src/handler.ts:45sets top-levelmodelSettings.verbosity, but the Agents SDK readstext.verbosity(seebuildModelSettingsinopenai-agents/src/model-parameters.ts). This also skips that function'sreasoningfiltering and thetext/truncation/contextManagementkeys. Spec §2.x.2 says settings follow the openai-agents contract, so reuse or mirrorbuildModelSettings.- Graph span leaks on setup failure. In
litellm-agents/src/native-graph.ts, the span opens at :116, butreverseTraverseandbuildInputrun before thetryat :196. A missing tool handler leaves the span open and sends noinvocation_failure. openai-agents wraps setup and run in onetry/finally. - No CommonJS export. Both packages export only
import. Other packages ship arequirecondition (e.g.openai-agents/package.json), so CJS consumers getERR_PACKAGE_PATH_NOT_EXPORTED. - New graph telemetry is untested.
native-graph.test.tsonly gainedon = vi.fn().
Worth settling with python-ai-sdk#102: graph-level events use makeNodeTrackData(root), while main's openai-agents uses makeGraphTrackData and doesn't call setLdSpanAttributes. The agents allowlists also differ: Python allows metadata/include_usage/top_logprobs, JS doesn't. The spec in ai-sdks-monorepo#18 still describes the old denylist and should record whichever list wins.
…-lite-llm Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # .release-please-manifest.json
…ative-graph telemetry, ship CJS - verbosity becomes text.verbosity and reasoning keeps effort/summary, via a copy of buildModelSettings narrowed to the shared allowlist. - toLiteLLMAgents: one try/finally over setup and run, graph-key track data, handoff target in the path. - Report $ld:ai:sdk:usage from every public factory. - Add require/CJS exports built with tsup. 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 3 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 c26968a. Configure here.
| if (!rootAgent) throw new Error(`Root agent "${root.key}" was not built`); | ||
| const rootModel = models[root.key]; | ||
| if (!rootModel) throw new Error(`Root model "${root.key}" was not built`); | ||
| const runner = new Runner({ modelProvider: { getModel: async () => rootModel }, tracingDisabled: true }); |
There was a problem hiding this comment.
Graph always uses the root model
High Severity
The native-graph Runner modelProvider.getModel ignores its argument and always returns the root node's model. The Agents SDK resolves every agent through that provider, so handoff targets run as the root model instead of their own evaluated model.
Reviewed by Cursor Bugbot for commit c26968a. Configure here.
There was a problem hiding this comment.
I think this is a false positive. Every agent is constructed with its own Model instance (modelFor(client, node)), and the Runner only calls modelProvider.getModel when an agent's model is a string (#resolveModelForAgent in agents-core run.js). Handoff targets therefore run on their own model, and getModel is effectively unused. Leaving this open in case you'd like the provider removed.
Co-authored-by: Cursor <cursoragent@cursor.com>


Summary
@launchdarkly/ai-litellm-messagesusing the LiteLLM proxy's OpenAI-compatible API@launchdarkly/ai-litellm-agentsusing the OpenAI Agents SDK over the same proxyLITELLM_BASE_URLand optionalLITELLM_API_KEY, while allowing explicit overridesUsage
Set
LITELLM_BASE_URLto the proxy's/v1endpoint.LITELLM_API_KEYis optional for unauthenticated proxies.Test plan
yarn typecheckyarn buildyarn code:checkyarn lint:pkgThe current
mainfull test command also runs an unrelated Claude native-graph suite whose module mock is missing the newly addedmakeNodeTrackDataexport. The LiteLLM workspaces and all static/build checks pass.Made with Cursor