Docs menu
Bring your model
An agent takes any AI SDK language model. This page shows the one line for OpenAI, Anthropic and OpenAI-compatible providers (NVIDIA, Groq, OpenRouter, a local server) — and the call that quietly sends your request to an API those servers do not have.
What it's for / when to use it#
GNL does not ship a model. An agent's model is an ordinary Vercel AI SDK language model, so you bring the provider package you already use and hand its model to the agent. The starter project runs on a mock model so that nothing needs a key; the day you want a real one, this is the step.
The step has one trap worth reading before you start, because it fails late and looks like a bug in your own code: which API a model object calls. It is described under The call that returns a 404 below.
Setup / import#
The quickest path is the CLI. It writes the model file, adds the dependency and prints the one import line to change. Nothing is written into gnl.config.ts: a model is chosen on the agent.
npx gnl add model nvidia # or: openai · anthropic · openai-compatible
pnpm installIt writes src/models/<provider>.ts and an .env.example naming the variables that file reads. Put the key in .env: gnl dev and gnl studio load it at startup, and anything already set in your shell wins.
Step-by-step usage#
OpenAI-compatible providers — NVIDIA NIM, Groq, OpenRouter, vLLM, Ollama, a gateway: any server that speaks the Chat Completions format. Use createOpenAICompatible and point baseURL at it. The bare call provider(id) already is a chat model; provider.chatModel(id) is the same thing, written out. There is no provider.chat(id).
// src/models/nvidia.ts — written by `gnl add model nvidia`.
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
const nim = createOpenAICompatible({
name: 'nvidia',
baseURL: 'https://integrate.api.nvidia.com/v1',
apiKey: process.env.NVIDIA_API_KEY ?? '',
});
// The bare call nim(id) is already a chat model; nim.chatModel(id) is the same thing, written out.
// (There is no nim.chat(id).)
export const model = nim.chatModel(process.env.NVIDIA_MODEL ?? 'poolside/laguna-xs-2.1');Other servers differ only in name, baseURL and the key: Groq is https://api.groq.com/openai/v1, OpenRouter https://openrouter.ai/api/v1, a local Ollama http://localhost:11434/v1. The model id is whatever that server serves, written exactly as it lists it.
OpenAI — the provider package's default call is right for api.openai.com. Anthropic — the bare call is right for Anthropic's own API.
// src/models/openai.ts — the bare call is right for api.openai.com
import { openai } from '@ai-sdk/openai';
export const model = openai(process.env.OPENAI_MODEL ?? 'gpt-5');
// src/models/anthropic.ts
import { anthropic } from '@ai-sdk/anthropic';
export const model = anthropic(process.env.ANTHROPIC_MODEL ?? 'claude-sonnet-5');
// @ai-sdk/openai pointed at an OpenAI-compatible server (baseURL): ask for Chat Completions
// createOpenAI({ baseURL, apiKey }).chat(id) // not the bare call, not .languageModel(id)Then hand the model to the agent. It is one field of the agent config:
import type { AgentConfig } from '@gnldev/durable';
import { model } from '../models/nvidia.js';
export const assistant: AgentConfig = {
model, // was: echoModel()
system: 'You are a helpful assistant.',
maxSteps: 4,
};@ai-sdk/openai sends a bare openai(id) — and openai.languageModel(id) too — to the Responses API (/v1/responses). That is right for OpenAI. But OpenAI-compatible providers (NVIDIA, Groq, OpenRouter and most others) serve Chat Completions, not Responses: pointed at one of them through baseURL, the request answers 404, with a message about a missing route or function that reads like a bug in your code. If you keep @ai-sdk/openai for such a server, call openai.chat(id); the cleaner choice is createOpenAICompatible, whose bare call and chatModel(id) are chat models already.assistant answers with an echo, and charge-demo uses toolCallingModel, which on its first turn calls the demo chargeOrder tool with a fixed order — whatever you ask, and never any other tool. It exists to show the exactly-once guarantee without a key. To run your own tools you need a real model: bind one with the step above.