> ## Documentation Index
> Fetch the complete documentation index at: https://launchdarkly.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from the legacy AI SDKs

<View title="Developer" />

<View title="Federal docs" />

<View title="EU docs" />

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](/docs/sdk/ai/python) and [Node.js (server-side) AI SDK reference](/docs/sdk/ai/node-js).

## How SDK versions differ

We recommend migrating from the legacy AI SDK version to the current version when possible. Recent SDK versions facilitate more efficient implementations by removing the requirement for custom coding in many parts of your application. The current SDKs remove the provider setup, message merging, and metrics wrapping you had to write by hand in the legacy SDKs.

For the full before-and-after examples, read [Migrate a manual completion call](#migrate-a-manual-completion-call).

<table>
  <thead>
    <tr>
      <th>Language</th>
      <th>Legacy AI SDK</th>
      <th>Current AI SDK</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>Python</td>
      <td>28 lines</td>
      <td>9 lines</td>
    </tr>

    <tr>
      <td>Node.js</td>
      <td>25 lines</td>
      <td>9 lines</td>
    </tr>
  </tbody>
</table>

To compare the code itself, switch between these tabs:

<CodeGroup>
  ```python title="Python (legacy): 28 lines" lines wrap theme={null}
  from ldclient import set_config, get
  from ldclient.config import Config
  from ldai import LDAIClient, AICompletionConfigDefault
  from ldai_openai import get_ai_metrics_from_response
  from openai import OpenAI

  set_config(Config("YOUR_SDK_KEY"))
  ai_client = LDAIClient(get())
  openai_client = OpenAI()

  config = ai_client.completion_config(
    "example-config-key",
    context,
    AICompletionConfigDefault(enabled=False),
  )
  tracker = config.create_tracker()

  if config.enabled:
    messages = [m.to_dict() for m in config.messages or []]
    messages.append({"role": "user", "content": user_input})
    completion = tracker.track_metrics_of(
        get_ai_metrics_from_response,
        lambda: openai_client.chat.completions.create(
            model=config.model.name,
            messages=messages,
        ),
    )
    print(completion.choices[0].message.content)
  ```

  ```python title="Python (current): 9 lines" lines wrap theme={null}
  from launchdarkly_ai_openai_messages import openai_messages

  result = await openai_messages(
    "example-config-key",
    user_input,
    {"kind": "user", "key": "user-123"},
  )

  print(result.response)
  ```

  ```typescript title="Node.js (legacy): 25 lines" lines wrap theme={null}
  import { init } from '@launchdarkly/node-server-sdk';
  import { initAi } from '@launchdarkly/server-sdk-ai';
  import { getAIMetricsFromResponse } from '@launchdarkly/server-sdk-ai-openai';
  import OpenAI from 'openai';

  const ldClient = init('YOUR_SDK_KEY');
  await ldClient.waitForInitialization({ timeout: 10 });
  const aiClient = initAi(ldClient);
  const openai = new OpenAI();

  const aiConfig = await aiClient.completionConfig('example-config-key', context, {
  enabled: false,
  });

  if (aiConfig.enabled) {
  const tracker = aiConfig.createTracker();
  const messages = [...(aiConfig.messages ?? []), { role: 'user', content: userInput }];
  const completion = await tracker.trackMetricsOf(getAIMetricsFromResponse, () =>
    openai.chat.completions.create({
      model: aiConfig.model?.name ?? 'gpt-4o',
      messages,
    }),
  );
  console.log(completion.choices[0].message.content);
  }
  ```

  ```typescript title="Node.js (current): 9 lines" lines wrap theme={null}
  import { openaiMessages } from '@launchdarkly/ai-openai-messages';

  const result = await openaiMessages(
  'example-config-key',
  userInput,
  { kind: 'user', key: 'user-123' },
  );

  console.log(result.response);
  ```
</CodeGroup>

Most of these responsibilities leave your application when you migrate. Fallback handling is the exception because the current SDKs raise an error rather than return a disabled config:

<table>
  <thead>
    <tr>
      <th>Responsibility</th>
      <th>What happens in the current AI SDK</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>Initialize the LaunchDarkly and AI clients</td>
      <td>The SDK initializes from the environment, or you call `init_client()` / `initClient()`</td>
    </tr>

    <tr>
      <td>Construct a provider client and choose a model</td>
      <td>A handler package resolves the provider and model from the AgentControl config</td>
    </tr>

    <tr>
      <td>Supply fallback values and check `enabled`</td>
      <td>There is no fallback argument. `invoke()` raises an error if the variation has targeting off or if LaunchDarkly is unreachable. Keep your own fallback response and check state with `inspect_config()` / `inspectConfig()`</td>
    </tr>

    <tr>
      <td>Merge config messages with user input</td>
      <td>The SDK merges messages before it calls the provider</td>
    </tr>

    <tr>
      <td>Create a tracker and wrap the provider call</td>
      <td>The SDK records metrics and OpenTelemetry spans on every invoke</td>
    </tr>
  </tbody>
</table>

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.

<table>
  <thead>
    <tr>
      <th>Step</th>
      <th>Legacy AI SDK</th>
      <th>Current AI SDK</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>Evaluate the config</td>
      <td>Call `completion_config()` / `completionConfig()`, `agent_config()` / `agentConfig()`, or related methods</td>
      <td>Call `config()`, `graph()`, or a provider convenience function</td>
    </tr>

    <tr>
      <td>Call the provider</td>
      <td>Build a provider client, merge messages, and call the provider yourself</td>
      <td>A handler package calls the provider</td>
    </tr>

    <tr>
      <td>Record metrics</td>
      <td>Create a tracker and wrap the provider call</td>
      <td>Automatic on every invoke</td>
    </tr>
  </tbody>
</table>

## 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()` or `resolve_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.

<table>
  <thead>
    <tr>
      <th>Language</th>
      <th>Legacy packages</th>
      <th>Current packages</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>Python</td>
      <td>`launchdarkly-server-sdk`, `launchdarkly-server-sdk-ai`, and optional provider helpers such as `ldai_openai`</td>
      <td>`launchdarkly-ai-server` and handler packages such as `launchdarkly-ai-openai-messages`</td>
    </tr>

    <tr>
      <td>Node.js</td>
      <td>`@launchdarkly/node-server-sdk`, `@launchdarkly/server-sdk-ai`, and optional provider packages such as `@launchdarkly/server-sdk-ai-openai`</td>
      <td>`@launchdarkly/ai-node` and handler packages such as `@launchdarkly/ai-openai-messages`</td>
    </tr>
  </tbody>
</table>

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](/docs/sdk/ai/python#install-the-sdk) in the Python AI SDK reference or [Install the SDK](/docs/sdk/ai/node-js#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:

<CodeGroup>
  ```python title="Python AI SDK" lines wrap theme={null}
  pip install launchdarkly-server-sdk launchdarkly-ai-server launchdarkly-ai-openai-messages
  ```

  ```typescript title="Node.js AI SDK" lines wrap theme={null}
  npm install @launchdarkly/ai-node @launchdarkly/ai-openai-messages
  ```
</CodeGroup>

## 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:

<CodeGroup>
  ```python title="Python AI SDK (legacy)" lines wrap theme={null}
  from ldclient import set_config, get
  from ldclient.config import Config
  from ldai import LDAIClient

  set_config(Config("YOUR_SDK_KEY"))
  aiclient = LDAIClient(get())
  ```

  ```typescript title="Node.js AI SDK (legacy)" lines wrap theme={null}
  import { init, LDClient } from '@launchdarkly/node-server-sdk';
  import { initAi, LDAIClient } from '@launchdarkly/server-sdk-ai';

  const ldClient: LDClient = init('YOUR_SDK_KEY');
  await ldClient.waitForInitialization({ timeout: 10 });

  const aiClient: LDAIClient = initAi(ldClient);
  ```
</CodeGroup>

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](/docs/sdk/ai/python#manage-the-client-lifecycle) or [Manage the client lifecycle](/docs/sdk/ai/node-js#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:

<CodeGroup>
  ```python title="Python AI SDK (legacy)" expandable lines wrap theme={null}
  from ldai import AICompletionConfigDefault
  from ldai_openai import get_ai_metrics_from_response

  fallback_value = AICompletionConfigDefault(enabled=False)

  config = aiclient.completion_config(
    'example-config-key',
    context,
    fallback_value,
    {'example_custom_variable': 'example_custom_value'},
  )

  tracker = config.create_tracker()

  if config.enabled:
    messages = [] if config.messages is None else config.messages
    completion = tracker.track_metrics_of(
        get_ai_metrics_from_response,
        lambda: openai_client.chat.completions.create(
            model=config.model.name,
            messages=[message.to_dict() for message in messages],
        ),
    )
  ```

  ```typescript title="Node.js AI SDK (legacy)" expandable lines wrap theme={null}
  import { getAIMetricsFromResponse } from '@launchdarkly/server-sdk-ai-openai';

  const fallbackConfig = { enabled: false };

  const aiConfig = await aiClient.completionConfig(
  'example-config-key',
  context,
  fallbackConfig,
  { exampleCustomVariable: 'exampleCustomValue' },
  );

  const tracker = aiConfig.createTracker();
  const configMessages = aiConfig.messages || [];
  const userMessage = { role: 'user', content: 'What is feature flagging?' };
  const allMessages = [...configMessages, userMessage];

  const response = await tracker.trackMetricsOf(
  getAIMetricsFromResponse,
  () => client.chat.completions.create({
    model: aiConfig.model?.name || 'gpt-4',
    messages: allMessages,
  }),
  );
  ```
</CodeGroup>

### Use a convenience function

If you call one provider in one mode, replace the evaluate-call-track sequence with that provider's convenience function. Call it with the config key, the user input, and an [LDContext](/docs/home/getting-started/vocabulary#context), in that order. The SDK evaluates the config, calls the provider, and records metrics.

Pass template variables as the `variables` keyword argument in Python or inside the options object in Node.js.

Here's the same call with a convenience function:

<CodeGroup>
  ```python title="Python AI SDK" lines wrap theme={null}
  from dotenv import load_dotenv
  from launchdarkly_ai_openai_messages import openai_messages

  load_dotenv()

  result = await openai_messages(
    "example-config-key",
    "What is feature flagging?",
    {"kind": "user", "key": "user-123"},
    variables={"example_custom_variable": "example_custom_value"},
  )

  print(result.response)
  ```

  ```typescript title="Node.js AI SDK" lines wrap theme={null}
  import 'dotenv/config';
  import { openaiMessages } from '@launchdarkly/ai-openai-messages';

  const result = await openaiMessages(
  'example-config-key',
  'What is feature flagging?',
  { kind: 'user', key: 'user-123' },
  { variables: { exampleCustomVariable: 'exampleCustomValue' } },
  );

  console.log(result.response);
  ```
</CodeGroup>

### 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 pass a fallback default object, merge UI messages with the user message, or create a tracker.

Because there is no fallback argument, `invoke()` raises an error instead of returning a disabled config. Wrap `invoke()` to return a fallback response if the variation has targeting off or if LaunchDarkly is unreachable. To read the state without raising, call `inspect_config()` / `inspectConfig()`. This returns the `enabled` flag, the config, and the variation metadata.

Here's how:

<CodeGroup>
  ```python title="Python AI SDK" expandable lines wrap theme={null}
  from dotenv import load_dotenv
  from launchdarkly_ai_server import config, shutdown
  from launchdarkly_ai_openai_messages import create_openai_messages_handler
  from launchdarkly_ai_claude_messages import create_claude_messages_handler

  load_dotenv()

  result = await config(
    key="example-config-key",
    handler=[
        create_openai_messages_handler(),
        create_claude_messages_handler(),
    ],
  ).invoke(
    "What is feature flagging?",
    {"kind": "user", "key": "user-123"},
    {"example_custom_variable": "example_custom_value"},
  )

  print(result.response)
  await shutdown()
  ```

  ```typescript title="Node.js AI SDK" expandable lines wrap theme={null}
  import 'dotenv/config';
  import { config, shutdown } from '@launchdarkly/ai-server';
  import { createOpenAIHandler } from '@launchdarkly/ai-openai-messages';
  import { createClaudeMessagesHandler } from '@launchdarkly/ai-claude-messages';

  const result = await config({
  key: 'example-config-key',
  handler: [
    createOpenAIHandler(),
    createClaudeMessagesHandler(),
  ],
  }).invoke(
  'What is feature flagging?',
  { kind: 'user', key: 'user-123' },
  { exampleCustomVariable: 'exampleCustomValue' },
  );

  console.log(result.response);
  await shutdown();
  ```
</CodeGroup>

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:

<CodeGroup>
  ```python title="Python AI SDK (legacy)" lines wrap theme={null}
  from ldai import AIAgentConfigDefault

  agent = aiclient.agent_config(
    'example-config-key',
    context,
    AIAgentConfigDefault(enabled=False),
    {'example_custom_variable': 'example_custom_value'},
  )

  if agent.enabled:
    tracker = agent.create_tracker()
    result = example_model_api(agent.instructions)
    tracker.track_success()
  ```

  ```typescript title="Node.js AI SDK (legacy)" lines wrap theme={null}
  const fallbackConfig = { enabled: false };

  const agent = await aiClient.agentConfig(
  'example-config-key',
  context,
  fallbackConfig,
  { exampleCustomVariable: 'exampleCustomValue' },
  );

  if (agent.enabled) {
  const tracker = agent.tracker;
  // Use agent.instructions with your provider and tool loop, then track metrics.
  }
  ```
</CodeGroup>

Here's the same call with handlers and tools:

<CodeGroup>
  ```python title="Python AI SDK" expandable lines wrap theme={null}
  from launchdarkly_ai_server import config
  from launchdarkly_ai_openai_agents import create_openai_agent_handler

  config_instance = config(
    key="example-config-key",
    handler=[create_openai_agent_handler()],
    tool_handlers={
        "get-prefs": get_preferences_fn,
    },
  )

  result = await config_instance.invoke(
    "What are my preferences?",
    {"kind": "user", "key": "user-123"},
    {"example_custom_variable": "example_custom_value"},
  )

  print(result.response)
  ```

  ```typescript title="Node.js AI SDK" expandable lines wrap theme={null}
  import { config } from '@launchdarkly/ai-server';
  import { createOpenAIAgentHandler } from '@launchdarkly/ai-openai-agents';

  const configInstance = config({
  key: 'example-config-key',
  handler: [createOpenAIAgentHandler()],
  toolHandlers: {
    'get-prefs': getPreferencesFn,
  },
  });

  const result = await configInstance.invoke(
  'What are my preferences?',
  { kind: 'user', key: 'user-123' },
  { exampleCustomVariable: 'exampleCustomValue' },
  );

  console.log(result.response);
  ```
</CodeGroup>

## 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:

<CodeGroup>
  ```python title="Python AI SDK" lines wrap theme={null}
  from launchdarkly_ai_server import Registry, config
  from launchdarkly_ai_openai_messages import create_openai_messages_handler
  from launchdarkly_ai_openai_agents import create_openai_agent_handler

  openai_registry = Registry(
    handlers=[create_openai_messages_handler(), create_openai_agent_handler()],
    tools={"get-prefs": get_preferences_fn},
  )

  # Reuse the same registry across call sites
  result = await config(key="example-config-key", registry=openai_registry).invoke(user_input, context)
  ```

  ```typescript title="Node.js AI SDK" lines wrap theme={null}
  import { Registry, config } from '@launchdarkly/ai-server';
  import { createOpenAIHandler } from '@launchdarkly/ai-openai-messages';
  import { createOpenAIAgentHandler } from '@launchdarkly/ai-openai-agents';

  const openaiRegistry = new Registry({
  handlers: [createOpenAIHandler(), createOpenAIAgentHandler()],
  tools: { 'get-prefs': getPreferencesFn },
  });

  // Reuse the same registry across call sites.
  const result = await config({ key: 'example-config-key', registry: openaiRegistry }).invoke(userInput, context);
  ```
</CodeGroup>

To learn more, read [Manage handlers and tools with a registry](/docs/sdk/ai/python#manage-handlers-and-tools-with-a-registry) or [Create a registry](/docs/sdk/ai/node-js#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:

<CodeGroup>
  ```python title="Python AI SDK" lines wrap theme={null}
  import asyncio
  from launchdarkly_ai_server import config, global_registry

  result = await config(
    key="example-config-key",
    registry=global_registry,
    skip_judges=True,
  ).invoke(user_input, context)

  for task in result.judge_tasks or []:
    asyncio.create_task(run_judge_worker(task))
  ```

  ```typescript title="Node.js AI SDK" lines wrap theme={null}
  import { config, globalRegistry } from '@launchdarkly/ai-server';
  import { Worker } from 'node:worker_threads';

  const { invoke } = config({
  key: 'example-config-key',
  registry: globalRegistry,
  skipJudges: true,
  });

  const { response, judgeTasks } = await invoke(userInput, context);

  for (const task of judgeTasks ?? []) {
  new Worker(new URL('./judge-worker.js', import.meta.url), { workerData: task });
  }
  ```
</CodeGroup>

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](/docs/sdk/ai/python#run-judges-asynchronously) or [Run judges asynchronously](/docs/sdk/ai/node-js#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:

<CodeGroup>
  ```python title="Python AI SDK" lines wrap theme={null}
  from launchdarkly_ai_server import graph, shutdown
  from launchdarkly_ai_claude_agents import create_claude_agents_handler
  from launchdarkly_ai_openai_agents import create_openai_agent_handler

  result = await graph(
    "support-graph",
    handlers=[create_claude_agents_handler(), create_openai_agent_handler()],
  ).invoke(
    "I was double charged",
    {"kind": "user", "key": "user-123"},
    {"account_tier": "pro"},
  )

  print(result["response"])
  await shutdown()
  ```

  ```typescript title="Node.js AI SDK" lines wrap theme={null}
  import { graph, shutdown } from '@launchdarkly/ai-server';
  import { createClaudeAgentsHandler } from '@launchdarkly/ai-claude-agents';

  const result = await graph('support-graph', {
  handlers: [createClaudeAgentsHandler()],
  }).invoke(
  'I was double charged',
  { kind: 'user', key: 'user-123' },
  { account_tier: 'pro' },
  );

  console.log(result.response);
  await shutdown();
  ```
</CodeGroup>

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](/docs/sdk/ai/python#run-an-agent-graph) or [Run an agent graph](/docs/sdk/ai/node-js#run-an-agent-graph).

## API mapping

<table>
  <thead>
    <tr>
      <th>Concern</th>
      <th>Legacy</th>
      <th>Current</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>Completion</td>
      <td>`completion_config()` / `completionConfig()`</td>
      <td>`config().invoke()` or a convenience function</td>
    </tr>

    <tr>
      <td>Agent</td>
      <td>`agent_config()` / `agentConfig()`</td>
      <td>`config().invoke()`; mode comes from the variation</td>
    </tr>

    <tr>
      <td>Multi-agent</td>
      <td>`agent_configs()` / `agentConfigs()`</td>
      <td>`graph()` or a native framework runner</td>
    </tr>

    <tr>
      <td>Metrics</td>
      <td>Tracker methods</td>
      <td>Automatic on every call</td>
    </tr>

    <tr>
      <td>Judges</td>
      <td>`create_judge()` / `createJudge()`</td>
      <td>Attached judges, plus `run_judge()` / `runJudge()` for async work</td>
    </tr>

    <tr>
      <td>Graphs</td>
      <td>`create_agent_graph()`, traverse helpers</td>
      <td>`graph()`, `resolve_graph()` / `resolveGraph()`</td>
    </tr>

    <tr>
      <td>Init</td>
      <td>`LDAIClient` / `initAi()`</td>
      <td>Lazy init, or `init_client()` / `initClient()`</td>
    </tr>
  </tbody>
</table>

## What's next

* [Python AI SDK reference](/docs/sdk/ai/python)
* [Node.js (server-side) AI SDK reference](/docs/sdk/ai/node-js)
* [AI SDKs](/docs/sdk/ai)
* [AgentControl](/docs/home/agentcontrol)
