Migrate from the legacy AI SDKs
This topic explains how to migrate from the legacy Python and Node.js AI SDKs to the current AI SDKs.
The current SDKs are not drop-in replacements. You must change some workflows when you migrate. You stop evaluating a config and calling the provider yourself, and instead register handlers and invoke the model through the SDK.
The current Python and Node.js AI SDKs are in open beta. Their APIs are subject to change. To learn more about each SDK, read the Python AI SDK reference and Node.js (server-side) AI SDK reference.
How SDK versions differ
In the legacy SDKs, you evaluate an AgentControl config, call your model provider with the returned messages or instructions, and record metrics with a tracker.
In the current SDKs, you register provider handlers and tools one time, then call config(), graph(), or a convenience function. The SDK evaluates the config, routes to the matching handler, calls the provider, and records metrics and OpenTelemetry spans for you.
What changes when you migrate from a legacy SDK
You must change some parts of your application code when you migrate. They are:
- Packages: Replace the legacy AI SDK packages with the current core package, as well as provider and mode handler packages.
- Initialization: Replace explicit LaunchDarkly and AI client construction with automatic initialization from the environment, or call
init_client()/initClient()when you need control. - Model calls: Replace evaluate-then-call-provider-yourself with
config().invoke(),graph(), or a provider convenience function. - Tools and handlers: Register tool implementations and handlers one time, typically in a
Registry, instead of wiring them at every call site. - Metrics and traces: Remove tracker setup and manual span wrapping. The SDK records metrics and creates OpenTelemetry spans automatically.
- Judges and graphs: Attach judges to config variations instead of calling a standalone judge, and replace legacy graph helpers with
graph()orresolve_graph()/resolveGraph(), as well as a native runner.
The sections below contain examples for each of these changes.
Replace packages and install handlers
Uninstall the legacy AI SDK packages and install the current core package, as well as one handler package for each provider and mode you use.
Install the base LaunchDarkly server SDK alongside the AI SDK core and handler packages. Use launchdarkly-server-sdk for Python and @launchdarkly/node-server-sdk for Node.js. For edge or custom runtimes, install the core @launchdarkly/ai-server package instead and pass a pre-initialized client.
For the full handler package matrix, read Install the SDK in the Python AI SDK reference or Install the SDK in the Node.js AI SDK reference.
Here’s how to install the base LaunchDarkly server SDK, the AI SDK core package, and the OpenAI messages handler:
Update client initialization
The legacy SDKs require you to construct an explicit LaunchDarkly client and an AI client wrapper.
Here’s how the legacy SDKs initialize:
In the current SDKs, initialization is often automatic. The SDK derives its settings from the environment, reading LD_SDK_KEY and your provider API keys, and initializes the client on your first AI call. You no longer construct a LaunchDarkly client or an AI client wrapper yourself.
If you need to control initialization, such as to pass custom options or pre-warm the client, call init_client() / initClient() yourself. Call shutdown() when your application exits to flush events and spans.
To learn more, read Manage the client lifecycle or Manage the client lifecycle.
Migrate a manual completion call
This is the most common migration. In the legacy SDKs, you evaluate a completion-mode config, merge messages, call the provider, and wrap the call with a tracker.
Here’s the legacy pattern:
Use a convenience function
If you call one provider in one mode, replace the evaluate-call-track sequence with that provider’s convenience function. The SDK evaluates the config, calls the provider, and records metrics.
Here’s the same call with a convenience function:
Use handlers for multi-provider routing
If your config can serve more than one provider or mode, register handlers and call config().invoke(). Pass template variables as the third argument to invoke(). You no longer build a fallback default object, merge UI messages with the user message, or create a tracker.
Here’s how:
If you used Node.js legacy managed objects such as createModel().run(), map those call sites to the same convenience function or config().invoke() path. Managed objects were the closest precursor to the current SDK, but the packages and APIs still change.
Migrate agent mode and tools
In the legacy SDKs, agent mode uses a separate evaluation method, then your application runs the tool loop and records metrics. In the current SDKs, completion and agent mode share config(). The config variation’s mode selects the handler. Register tool implementations by name, or pass them inline.
Tool keys must match the tool names defined in the LaunchDarkly config variation exactly, including case.
Here’s the legacy agent-mode pattern:
Here’s the same call with handlers and tools:
Centralize configuration management
Registries are one of the larger cleanup opportunities in this migration. In the legacy SDKs, sharing handler wiring and tool implementations across call sites meant writing your own higher-level wrapper functions or duplicating handling code at each site. In the current SDKs, you register handlers and tools one time in a Registry, then pass that registry to any call. Provider routing, tool dispatch, and metrics all live in one place.
Use a scoped Registry when different parts of your application need different handlers or tools, or the global registry when a single set is appropriate for the whole application. Some providers also expose built-in tools, such as Claude web search, that you assign as tool implementations instead of writing a function.
Here’s how to register handlers and tools one time, then reuse them:
To learn more, read Manage handlers and tools with a registry or Create a registry.
Migrate judges
In the current SDKs, you attach judges to a config variation rather than calling a standalone judge in your application code. To keep response latency low, you can skip inline evaluation and run judges in the background instead.
Run attached judges asynchronously. If you don’t include skip_judges=true, judges are run inline by default. Here’s how:
Inside each worker, call run_judge(task, handlers) / runJudge(task, handlers) and track the evaluation metric when present. To learn more, read Run judges asynchronously or Run judges asynchronously.
Migrate agent graphs
Replace legacy graph creation and traversal helpers with graph().invoke(), or resolve the topology and hand it to a native framework runner.
Here’s how to run a graph with the current SDK router:
To run on a provider framework such as LangGraph or the OpenAI Agents SDK, call resolve_graph() / resolveGraph(), then pass the result to to_lang_graph / toLangGraph, to_openai_agents / toOpenAIAgents, or to_claude_agents / toClaudeAgents. To learn more, read Run an agent graph or Run an agent graph.