SDKs and Frameworks

The OpenAI SDK, the Vercel AI SDK and LangChain, with a working tool-calling loop in TypeScript and Python.

When you are writing the client rather than configuring one, these are the libraries to reach for. All three take a base URL and need nothing else, and all three support streaming and tool calls against the ZYRAXON line.

The OpenAI SDK

typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://zyraxonai.lovable.app/api/public/zyraxon/v1",
  apiKey: "zyraxon",
});

const stream = await client.chat.completions.create({
  model: "zyraxon-codex",
  messages: [{ role: "user", content: "Build a landing page" }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
python
from openai import OpenAI

client = OpenAI(base_url="https://zyraxonai.lovable.app/api/public/zyraxon/v1", api_key="zyraxon")

stream = client.chat.completions.create(
    model="zyraxon-codex",
    messages=[{"role": "user", "content": "Build a landing page"}],
    stream=True,
)

for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="", flush=True)

Tool calling, end to end

The provider returns the call and you run it — that is what keeps the model from touching anything you did not authorise. These two loops are complete: they send the tools, execute the calls and feed the results back until the model stops asking.

typescript
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://zyraxonai.lovable.app/api/public/zyraxon/v1", apiKey: "zyraxon" });

const tools = [
  {
    type: "function" as const,
    function: {
      name: "write_file",
      description: "Write text to a file on disk.",
      parameters: {
        type: "object",
        properties: { path: { type: "string" }, content: { type: "string" } },
        required: ["path", "content"],
      },
    },
  },
];

// You run the tools. The provider only returns the call.
async function runTool(name: string, args: any) {
  if (name === "write_file") {
    const { writeFile } = await import("node:fs/promises");
    await writeFile(args.path, args.content, "utf8");
    return `wrote ${args.path}`;
  }
  return `unknown tool ${name}`;
}

const messages: any[] = [{ role: "user", content: "Save a hello world file." }];

for (let turn = 0; turn < 8; turn++) {
  const res = await client.chat.completions.create({
    model: "zyraxon-codex",
    messages,
    tools,
  });
  const choice = res.choices[0];
  messages.push(choice.message);

  if (choice.finish_reason !== "tool_calls") break;

  for (const call of choice.message.tool_calls ?? []) {
    const result = await runTool(call.function.name, JSON.parse(call.function.arguments));
    messages.push({ role: "tool", tool_call_id: call.id, content: result });
  }
}
ℹ
The Python loop
The same loop in Python, in the Automation and HTTP clients page below or the Python tab on the provider page.

Vercel AI SDK

typescript
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { streamText } from "ai";

const zyraxon = createOpenAICompatible({
  name: "zyraxon",
  baseURL: "https://zyraxonai.lovable.app/api/public/zyraxon/v1",
  apiKey: "zyraxon",
});

const result = streamText({
  model: zyraxon("zyraxon-codex"),
  messages: [{ role: "user", content: "Explain a monad in one paragraph." }],
});

for await (const delta of result.textStream) process.stdout.write(delta);

LangChain

python
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="zyraxon-codex",
    base_url="https://zyraxonai.lovable.app/api/public/zyraxon/v1",
    api_key="zyraxon",
)

print(llm.invoke("Summarise this repo layout in three bullets.").content)
typescript
import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
  model: "zyraxon-codex",
  configuration: { baseURL: "https://zyraxonai.lovable.app/api/public/zyraxon/v1", apiKey: "zyraxon" },
});

console.log((await llm.invoke("Summarise this repo layout in three bullets.")).content);

LiteLLM and the gateways built on it

LiteLLM picks its handler from the text before the first / in the model string. A bare id like zyraxon-codex has no /, so LiteLLM stops with BadRequestError: LLM Provider NOT provided and never calls us. Write the model as openai/zyraxon-codex and it uses the generic OpenAI-compatible path.

⚠
This is the most common connection error
If you see LLM Provider NOT provided. Pass in the LLM provider you are trying to call. You passed model=zyraxon-codex, the fix is the model string, not the URL or the key: change zyraxon-codex to openai/zyraxon-codex.
python
from litellm import completion

response = completion(
    # The provider prefix is required: LiteLLM reads it to pick a handler, and
    # without it you get "LLM Provider NOT provided". The endpoint strips it.
    model="openai/zyraxon-codex",
    api_base="https://zyraxonai.lovable.app/api/public/zyraxon/v1",
    api_key="zyraxon",
    messages=[{"role": "user", "content": "Explain what a reverse proxy does."}],
)

print(response.choices[0].message.content)

The endpoint is deliberately tolerant, so all of these resolve to the same model — zyraxon-codex, openai/zyraxon-codex, zyraxon/zyraxon-codex, zyraxon/codex and openai/zyraxon/codex.

text
# Why the model string needs a provider prefix, and what this endpoint accepts.
#
# LiteLLM reads the text before the first "/" to choose a handler. A bare id
# like "zyraxon-codex" has no "/", so LiteLLM stops with:
#     BadRequestError: LLM Provider NOT provided
# Write "openai/zyraxon-codex" and it uses the generic OpenAI-compatible path.
#
# This endpoint is tolerant: all of these resolve to the same ZYRAXON model.
#   zyraxon-codex
#   openai/zyraxon-codex
#   zyraxon/zyraxon-codex
#   zyraxon/codex
#   openai/zyraxon/codex

A LiteLLM proxy in front of the whole line

Running LiteLLM as a proxy gives you its routing, budgets, logging and virtual keys on top of ZYRAXON, and lets any other client point at one local URL.

yaml
# litellm-config.yaml — a LiteLLM proxy that fronts ZYRAXON.
#
# Start it with:  litellm --config litellm-config.yaml --port 4000
# Then any client pointed at http://localhost:4000/v1 gets the ZYRAXON models,
# with LiteLLM's own routing, budgets, logging and key management on top.
model_list:
  - model_name: zyraxon-apex
    litellm_params:
      model: openai/zyraxon-apex
      api_base: https://zyraxonai.lovable.app/api/public/zyraxon/v1
      api_key: zyraxon
  - model_name: zyraxon-codex
    litellm_params:
      model: openai/zyraxon-codex
      api_base: https://zyraxonai.lovable.app/api/public/zyraxon/v1
      api_key: zyraxon
  - model_name: zyraxon-cyber
    litellm_params:
      model: openai/zyraxon-cyber
      api_base: https://zyraxonai.lovable.app/api/public/zyraxon/v1
      api_key: zyraxon

litellm_settings:
  drop_params: true
bash
# Point LiteLLM at ZYRAXON with plain environment variables, no config file.
# The openai/ prefix in the model string is required by LiteLLM.
export OPENAI_API_BASE="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_API_KEY="zyraxon"

python - <<'PY'
from litellm import completion
r = completion(model="openai/zyraxon-codex",
               messages=[{"role": "user", "content": "hello"}])
print(r.choices[0].message.content)
PY

# Same thing through the LiteLLM CLI:
litellm --model openai/zyraxon-codex --api_base https://zyraxonai.lovable.app/api/public/zyraxon/v1 --api_key zyraxon
ℹ
The same settings work in every LiteLLM gateway
Open WebUI, LibreChat, Dify, Flowise and Langroid all use LiteLLM underneath, so they all take the same openai/-prefixed model and the same base URL.

Before you ship

  • ▸Fetch https://zyraxonai.lovable.app/api/public/zyraxon/v1/models at startup rather than hardcoding a context window; the pool changes.
  • ▸Retry 429, 502 and 503 with backoff and jitter; honour Retry-After. Do not retry 400, 401 or 413.
  • ▸Log the type field, not just the status: overloaded and upstream_gate are both 503 but need different actions.
  • ▸Treat X-ZYRAXON-Capacity: spill as a success, and use it as a signal to slow down.