GNL 0.18: early and moving fast — APIs can change between releasesJoin us

GNL
Docs menu
Core · Free@gnldev/durable

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.

gnl add model
npx gnl add model nvidia      # or: openai · anthropic · openai-compatible
pnpm install

It 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
// 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.

OpenAI and Anthropic
// 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:

src/agents/assistant.ts
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,
};
The call that returns a 404
@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.
The starter's models are fakes
The scaffold's 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.