Skip to Content
Self-hostingConfigurationModel providers

Model providers

On the hosted app the agent is Screenplay’s own loop, built on the Vercel AI SDK. It runs on whichever model providers you configure. Each provider turns on when its environment variables are set, and configured providers appear as sections in the chat’s model picker.

The desktop app doesn’t use these providers. Its chat runs through the coding CLIs installed on the machine. See Coding CLIs.

Providers

ProviderKeyVariablesModel list from
AnthropicanthropicANTHROPIC_API_KEY/v1/models
OpenAIopenaiOPENAI_API_KEY/v1/models, chat models only
Google GeminigoogleGOOGLE_GENERATIVE_AI_API_KEYmodels supporting generateContent
Vercel AI GatewayvercelAI_GATEWAY_API_KEYthe gateway’s language models
OpenAI-compatiblecompatOPENAI_COMPATIBLE_BASE_URL, optional OPENAI_COMPATIBLE_API_KEY${BASE_URL}/v1/models

At least one provider is required.

  • Model lists are live. Each provider queries its API for models, caches the list for an hour, and falls back to a small built-in list if the query fails (bad key, rate limit, a server without /v1/models). New upstream models show up without a Screenplay release.

  • Vercel AI Gateway gives you hundreds of models behind one key, plus budgets, analytics, and failover. Set AI_GATEWAY_API_KEY explicitly, even on Vercel: the gateway doesn’t accept the OIDC token Vercel injects.

  • OpenAI-compatible covers OpenRouter, Groq, Together, vLLM, LM Studio, Ollama, LiteLLM, and similar:

    OPENAI_COMPATIBLE_BASE_URL=https://openrouter.ai/api/v1 OPENAI_COMPATIBLE_API_KEY=sk-or-...

Model ids and the default

Model ids always name their provider: <provider>:<model>, for example anthropic:claude-sonnet-5-5, openai:gpt-6-astra, vercel:anthropic/claude-sonnet-5.5, or compat:llama-3.3-70b. Ids without a provider are rejected, so a request can never quietly reach a provider you didn’t intend.

AGENT_DEFAULT_MODEL sets the default for new chats. It defaults to anthropic:claude-sonnet-5-5. If you don’t run Anthropic, point it at a provider you do. If its provider isn’t configured, new chats use the first enabled model instead. Each user’s last-picked model is remembered in their browser.

A chat whose model belongs to a provider that’s no longer configured fails with that provider’s error. It’s never rerouted to a different provider.

Adding a provider

Providers live in apps/app/lib/agent/providers/, one file each, behind the ModelProvider interface in types.ts:

apps/app/lib/agent/providers/mistral.ts
import "server-only" import { mistral } from "@ai-sdk/mistral" import type { ModelProvider } from "./types" class MistralProvider implements ModelProvider { key = "mistral" label = "Mistral" isConfigured() { return Boolean(process.env.MISTRAL_API_KEY) } async listModels() { if (!this.isConfigured()) return [] return [{ id: "mistral:mistral-large-latest", label: "Mistral Large" }] } resolve(modelId: string) { return mistral(modelId) } // Lets terminal-tab CLIs reach this provider without seeing the key: // the sandbox firewall adds these headers for this host. Return null to opt out. egress() { const key = process.env.MISTRAL_API_KEY return key ? { host: "api.mistral.ai", headers: { authorization: `Bearer ${key}` } } : null } } export function getMistralProvider(): ModelProvider { return new MistralProvider() }

Install the adapter (pnpm add @ai-sdk/mistral in apps/app), then add the factory to the PROVIDERS array in providers/index.ts. The agent, the model picker, and /api/agent/models need no other changes. Use the existing providers’ discover() helper from cache.ts for a live, cached model list.

Last updated on