ElementsAI

Models

Declare ai.model bindings — known providers auto-resolve baseUrl; Anthropic and Google OpenAI-compat have documented limits.

An ai.model binding names a logical model (smart, local, …), the wire model id, and how to reach it (provider, optional baseUrl / apiKey / driverId).

For developers swapping OpenRouter for a self-hosted OpenAI-compatible /v1 — one logical name, different bindings per environment.

The one rule

Known OpenAI-compatible provider names resolve baseUrl automatically. Explicit baseUrl always wins. Unknown providers require baseUrl — they fail loud. Per-binding apiKey isolates tokens when several providers run together.

Smallest Example

Declare cloud + BYO bindings

src/core/ai.ts
import { ai } from "okengine";

export const smart = ai.model("smart", {
  provider: "openrouter",
  model: "openrouter/free",
  apiKey: process.env.OPENROUTER_API_KEY,
});

export const local = ai.model("local", {
  provider: "openai-compatible",
  model: process.env.OKE_AI_MODEL ?? "your-model-id",
  ...(process.env.OKE_AI_URL?.trim() ? { baseUrl: process.env.OKE_AI_URL.trim() } : {}),
});

OpenRouter fills https://openrouter.ai/api/v1 without typing it. Any OpenAI-compatible /v1 uses the same driver — set OKE_AI_URL yourself; Compose does not manage inference.

Attach a versioned prompt

export const triage = smart.prompt("ticket-triage", {
  version: 1,
  via: ["smart", "local"],
});

Ask from a Flow

src/flows/support/triage.ts
import { on, flow, http } from "okengine";
import { triage } from "@/core/ai";

export const classify = on(
  http.post(),
  flow({
    do: async (input, fx) => await fx.ask(triage, input),
  }),
);

fx.ask uses the prompt’s model chain. Recovery via via opens a separate client per binding — keys and base URLs do not mix.

Progressive Patterns

Zero Docker — registry fills the URL:

export const smart = ai.model("smart", {
  provider: "openrouter",
  model: "openrouter/free",
  apiKey: process.env.OPENROUTER_API_KEY,
});

See OpenRouter for router aliases (openrouter/auto, fusion, …).

Options

OptionTypeDefaultMeaning
providerstringRegistry name or exempt/local label
modelstringWire model id sent to the provider
baseUrlstringauto / omitOverride (always wins over registry)
apiKeystringPer-binding key (isolates multi-provider apps)
driverIdstringapp defaultProtocol driver (openai-compatible, anthropic, mock, …)
tierstringOptional app label (not the registry status below)

Empty name throws TypeError: ai.model: name is required.

Verified providers

These names auto-resolve a verified OpenAI-compatible baseUrl:

ProviderBase URL (auto)
openaihttps://api.openai.com/v1
openrouterhttps://openrouter.ai/api/v1
groqhttps://api.groq.com/openai/v1
togetherhttps://api.together.ai/v1
deepinfrahttps://api.deepinfra.com/v1/openai
xaihttps://api.x.ai/v1
mistralhttps://api.mistral.ai/v1
deepseekhttps://api.deepseek.com
vercelhttps://ai-gateway.vercel.sh/v1

Not registered (pass baseUrl if you still need them): Cloudflare Workers AI (account-scoped URL); Meta (retired Llama OpenAI-compat API / unverified Muse Spark host — do not guess a URL).

Exempt labels (no registry URL): mock, local, openai-compatible.

Limited compatibility

anthropic, google, and alias gemini also auto-resolve a URL, with documented limits. Prefer native Anthropic for production Claude.

ProviderBase URL (auto)
anthropichttps://api.anthropic.com/v1
google / geminihttps://generativelanguage.googleapis.com/v1beta/openai

Anthropic OpenAI-compat is evaluation-only

Anthropic’s OpenAI-compatible endpoint is for testing/comparison only. tools[].function.strict is ignored; n must be 1; no embeddings here. Production: driverId: "anthropic" (native Messages API).

Google OpenAI-compat tool schemas

Tool/parameter schemas are not full OpenAI JSON Schema fidelity. Complex Flow-as-tool schemas can fail — do not rely on this path for agent tool calling in production.

Declare-time and oke extract both warn when these providers auto-resolve.

Native vs OpenAI-compat Anthropic

PathWhen
driverId: "anthropic"Production Claude — native Messages API
provider: "anthropic" without native driverQuick eval via openai-compatible + registry URL

They are not interchangeable for agents that depend on reliable tool calling.

Streaming

fx.stream(model, { prompt, data?, via? }) records asks and yields token chunks. Drivers that do not support stream throw ai: model "…" (driver …) does not support stream.

for await (const chunk of fx.stream(smart, { prompt: "Say hello" })) {
  // chunk is a string token
}

Per-environment drivers

See AI Overview. create-oke does not pin drivers.ai by default — unset dev/test → mock; prod must declare.

Troubleshooting

Learn more

  • OpenRouter — free / auto / fusion routers
  • Promptsmodel.prompt and via
  • AI — element overview and fx surface

Next

On this page