Skip to main content

Vercel AI SDK

Standard Compute serves an OpenAI-compatible Chat Completions API, so the AI SDK works with it through the @ai-sdk/openai-compatible provider. createOpenAICompatible with our base URL is the whole setup.

Install​

npm install ai @ai-sdk/openai-compatible zod

Copy your API key from the dashboard and keep it in a server-side environment variable:

export STANDARDCOMPUTE_API_KEY=YOUR_API_KEY

Use the key only in server code, such as route handlers, server actions or scripts. Anyone with the key can use your budget.

Create the provider​

lib/standardcompute.ts
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';

export const standardcompute = createOpenAICompatible({
name: 'standardcompute',
baseURL: 'https://api.stdcmpt.com/v1',
apiKey: process.env.STANDARDCOMPUTE_API_KEY,
includeUsage: true,
supportsStructuredOutputs: true,
});
SettingValue
apiKeySent as Authorization: Bearer <key>.
includeUsageReports token usage for streamed responses.
supportsStructuredOutputsSends your schema as response_format: json_schema when you use structured output. It has no effect on other requests.

Choose a model​

Pass a model ID to the provider:

standardcompute('standardcompute'); // smart routing
standardcompute('anthropic/claude-sonnet-5.5'); // this model only
  • standardcompute uses smart routing: each request goes to one of the models you enabled on the Models page, with harder tasks going to stronger models.
  • An exact ID such as anthropic/claude-sonnet-5.5 pins that model. The gateway never switches a pinned request to a different model.

GET /v1/models lists every model ID your key can use.

Generate text​

import { generateText } from 'ai';
import { standardcompute } from './lib/standardcompute';

const { text, usage } = await generateText({
model: standardcompute('standardcompute'),
prompt: 'Explain what a mutex is in two sentences.',
});

console.log(text);
console.log(usage);

Stream text​

import { streamText } from 'ai';
import { standardcompute } from './lib/standardcompute';

const result = streamText({
model: standardcompute('standardcompute'),
prompt: 'Write a haiku about compilers.',
});

for await (const textPart of result.textStream) {
process.stdout.write(textPart);
}

console.log(await result.usage);

Usage arrives with the last chunk of the stream.

In a Next.js route handler​

app/api/chat/route.ts
import { convertToModelMessages, streamText, type UIMessage } from 'ai';
import { standardcompute } from '@/lib/standardcompute';

export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();

const result = streamText({
model: standardcompute('standardcompute'),
messages: await convertToModelMessages(messages),
});

return result.toUIMessageStreamResponse();
}

This works with useChat from @ai-sdk/react on the client. The API key stays on the server.

Tool calling​

Tools are sent as standard tools and tool_choice fields. Multi-step tool use with stopWhen works with smart routing and with pinned models.

import { generateText, isStepCount, tool } from 'ai';
import { z } from 'zod';
import { standardcompute } from './lib/standardcompute';

const { text, steps } = await generateText({
model: standardcompute('standardcompute'),
tools: {
weather: tool({
description: 'Get the current temperature in a city',
inputSchema: z.object({
city: z.string().describe('The city name'),
}),
execute: async ({ city }) => ({ city, temperatureC: 18 }),
}),
},
stopWhen: isStepCount(5),
prompt: 'Is it warmer than 15°C in Oslo right now?',
});

console.log(text);
console.log(steps.flatMap((step) => step.toolCalls));

Structured output​

With supportsStructuredOutputs: true, Output.object sends your schema as response_format: json_schema. The AI SDK then validates the response against the same schema.

import { generateText, Output } from 'ai';
import { z } from 'zod';
import { standardcompute } from './lib/standardcompute';

const { output } = await generateText({
model: standardcompute('openai/gpt-6-luna'),
output: Output.object({
schema: z.object({
title: z.string(),
severity: z.enum(['low', 'medium', 'high']),
steps: z.array(z.string()),
}),
}),
prompt: 'Turn this bug report into a ticket: the login button does nothing in Safari.',
});

console.log(output.title, output.severity, output.steps);

Pin a model for structured output, so every request goes to a model that follows JSON schemas. Smart routing can pick a different model per request. Claude models can wrap JSON in a Markdown code fence, so a GPT or Gemini model is the safer choice here.

Without supportsStructuredOutputs, the AI SDK sends response_format: json_object instead, and the model only sees the schema if your prompt describes it.

Reasoning​

Set reasoning on a call to control how much a model thinks before it answers. The provider sends it as reasoning_effort.

import { generateText } from 'ai';
import { standardcompute } from './lib/standardcompute';

const { text } = await generateText({
model: standardcompute('anthropic/claude-sonnet-5.5'),
instructions: 'You are a careful code reviewer.',
prompt: 'Review this function: function add(a, b) { return a - b }',
reasoning: 'high',
});

Supported levels depend on the model. When a request doesn't set reasoning, the model uses the default from your Models page. See Reasoning.

Monthly budget and errors​

Every plan has a fixed monthly compute budget. Requests run at full speed until it is used. After that, every request returns 402 with code budget_exhausted until the period renews, and nothing is billed on top of your plan. The x-sc-budget-renews-at response header says when the budget renews. See Budgets and limits.

The response body looks like this:

{
"error": {
"message": "Your monthly compute budget is used up. ...",
"type": "insufficient_quota",
"code": "budget_exhausted"
}
}

The AI SDK raises a 402 as an APICallError and does not retry it:

import { APICallError, generateText } from 'ai';
import { standardcompute } from './lib/standardcompute';

try {
const { text } = await generateText({
model: standardcompute('standardcompute'),
prompt: 'Summarize this changelog: ...',
});
console.log(text);
} catch (error) {
if (APICallError.isInstance(error) && error.statusCode === 402) {
const renewsAt = error.responseHeaders?.['x-sc-budget-renews-at'];
console.error(`Monthly budget used up. It renews at ${renewsAt}.`);
} else {
throw error;
}
}

With streamText, errors don't throw. Handle them in the onError callback.

StatusMeaningIn the AI SDK
401Missing or invalid API key.Thrown, not retried.
402Monthly budget or free trial credit used up.Thrown, not retried.
429Request-rate ceiling reached.Retried automatically (maxRetries, default 2).
503Model temporarily unavailable after gateway retries.Retried automatically.

All codes are listed on Errors.

Using the Anthropic provider​

Standard Compute also serves the Anthropic Messages API, so @ai-sdk/anthropic works with the same key. Set the base URL with /v1:

import { createAnthropic } from '@ai-sdk/anthropic';
import { generateText } from 'ai';

const anthropic = createAnthropic({
baseURL: 'https://api.stdcmpt.com/v1',
apiKey: process.env.STANDARDCOMPUTE_API_KEY,
});

const { text } = await generateText({
model: anthropic('anthropic/claude-sonnet-5.5'),
prompt: 'Explain what a race condition is in two sentences.',
});

Use anthropic/claude-standardcompute for smart routing. Streaming, tool use and images work. The thinking option is ignored and responses contain no reasoning, and Anthropic server tools such as web search are not run. For everything else, the OpenAI-compatible provider above is the better fit.

Limits​

Image inputs work on vision models; a text-only model returns 400 with unsupported_model_input. Embeddings, image generation and speech are not offered. Output is capped at 50,000 tokens per request on paid plans. See request limits.

The examples on this page are type-checked against ai 7.0.139, @ai-sdk/openai-compatible 3.0.68 and @ai-sdk/anthropic 4.0.79.