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
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 ?? "");
}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.
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 });
}
}Vercel AI SDK
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
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)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.
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.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.
# 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.
# 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# 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 zyraxonopenai/-prefixed model and the same base URL.Before you ship
- ▸Fetch
https://zyraxonai.lovable.app/api/public/zyraxon/v1/modelsat 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
typefield, not just the status:overloadedandupstream_gateare both 503 but need different actions. - ▸Treat
X-ZYRAXON-Capacity: spillas a success, and use it as a signal to slow down.