Skip to content

OpenAI Clients

Configure clients with the server base URL, usually http://localhost:8000/v1. The api_key value is sent as Authorization: Bearer <key>; the default demo does not verify it.

Install A Client

pip install openai
npm install openai

Chat Completions

Do not expose real API keys in a browser

The JavaScript examples enable dangerouslyAllowBrowser because the local demo uses a dummy key. Keep production credentials in server-side code.

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="DUMMY")

response = client.chat.completions.create(
    model="custom-input-output-context",
    messages=[{"role": "user", "content": "Show me the custom adapter."}],
    user="demo-user",
)

print(response.choices[0].message.content)
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="DUMMY")

stream = client.chat.completions.create(
    model="simple-graph",
    messages=[{"role": "user", "content": "Write a short poem about graphs."}],
    stream=True,
)

for chunk in stream:
    content = chunk.choices[0].delta.content
    if content:
        print(content, end="")
import OpenAI from "openai";

const openai = new OpenAI({
  baseURL: "http://localhost:8000/v1",
  apiKey: "DUMMY",
  dangerouslyAllowBrowser: true,
});

const completion = await openai.chat.completions.create({
  model: "custom-input-output-context",
  messages: [{ role: "user", content: "Show me the custom adapter." }],
  user: "demo-user",
});

console.log(completion.choices[0].message.content);
import OpenAI from "openai";

const openai = new OpenAI({
  baseURL: "http://localhost:8000/v1",
  apiKey: "DUMMY",
  dangerouslyAllowBrowser: true,
});

const stream = await openai.chat.completions.create({
  model: "simple-graph",
  messages: [{ role: "user", content: "Write a short poem about graphs." }],
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content || "";
  process.stdout.write(content);
}

Client Stream Events

Request explicitly public graph events with the standard metadata field. The Python SDK keeps the namespaced extension in each chunk's model_extra:

stream = client.chat.completions.create(
    model="research-graph",
    messages=[{"role": "user", "content": "Research this topic."}],
    stream=True,
    metadata={"langgraph_stream_events": "v1"},
)

for chunk in stream:
    extension = (chunk.model_extra or {}).get("langgraph_openai_serve")
    if isinstance(extension, dict) and extension.get("schema_version") == 1:
        event = extension.get("event")
        if isinstance(event, dict):
            handle_client_event(event)

    for choice in chunk.choices:
        if choice.delta.content:
            print(choice.delta.content, end="")

When using the higher-level streaming helper, inspect its raw ChunkEvent:

with client.chat.completions.stream(
    model="research-graph",
    messages=[{"role": "user", "content": "Research this topic."}],
    metadata={"langgraph_stream_events": "v1"},
) as stream:
    for item in stream:
        if item.type != "chunk":
            continue
        extension = (item.chunk.model_extra or {}).get(
            "langgraph_openai_serve"
        )
        if isinstance(extension, dict) and extension.get("schema_version") == 1:
            event = extension.get("event")
            if isinstance(event, dict):
                handle_client_event(event)

The helper emits a raw chunk event for every Chat Completions chunk. Consume LGOS events during iteration; do not expect get_final_completion() to retain them. See the OpenAI SDK's Chat Completions event reference and the LGOS wire contract.

Model Discovery And Runtime Settings

List standard model summaries, then retrieve the selected model to discover its settings. Check both the LGOS extension version and the nested runtime-settings version. A missing or unsupported descriptor means the client should use server defaults.

models = client.models.list()
model_id = next(model.id for model in models.data if model.id == "simple-graph")
model = client.models.retrieve(model_id)

extension = (model.model_extra or {}).get("langgraph_openai_serve")
settings = (
    extension.get("client_settings")
    if isinstance(extension, dict) and extension.get("schema_version") == 1
    else None
)

if isinstance(settings, dict) and settings.get("schema_version") == 1:
    print(settings["json_schema"])
    print(settings["defaults"])
const models = await openai.models.list();
const selectedModel = models.data.find((model) => model.id === "simple-graph");
if (!selectedModel) throw new Error("simple-graph is not registered");
const model = await openai.models.retrieve(selectedModel.id);

const extension = model.langgraph_openai_serve;
const settings =
  extension?.schema_version === 1 &&
  extension.client_settings?.schema_version === 1
    ? extension.client_settings
    : undefined;

if (settings) {
  console.log(settings.json_schema);
  console.log(settings.defaults);
}

metadata.langgraph_runtime_settings must be a JSON-encoded string, produced by json.dumps() or JSON.stringify(), rather than a nested metadata object. Send only values that differ from the discovered defaults; the encoded value must be 512 characters or fewer. See Client Request for the request shape.

Settings apply to one request. Resend non-default values whenever they are needed, including interrupt-resume requests. Omitting the metadata on a later request uses server defaults again.

Interrupt Resume

Interrupt-enabled graphs use OpenAI tool calls. Retrieve the selected model and check langgraph_openai_serve.features for interrupts before starting. Pass metadata.langgraph_thread_id, then resume langgraph_interrupt with a matching tool message. The thread ID restores checkpoint state only; include the same non-default langgraph_runtime_settings string on the resume request when the resumed run needs those settings. See OpenAI compatibility.

Diagnostics

Direct HTTP diagnostic

Use direct HTTP only to inspect behavior while debugging:

curl -X POST http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "custom-input-output-context",
    "messages": [{"role": "user", "content": "Show me the custom adapter."}],
    "user": "demo-user"
  }'

Notes

  • Use the registered graph name as model.
  • Set timeouts for long-running graphs.
  • Use streaming only for graphs configured to emit streamed chunks.
  • Add bearer-token authentication before exposing the API outside trusted development environments.