GlassKit

AI providers

Use Anthropic or OpenAI (or both) through the Vercel AI SDK — streaming, tools, and structured output.

GlassKit ships with the Vercel AI SDK wired to both Anthropic and OpenAI providers. The AI SDK is provider-agnostic, so swapping models is a one-line change.

Why the AI SDK over a provider's native SDK

The AI SDK gives you:

  • One API across providers — switch from Claude to GPT-4o by changing an import
  • First-class streamingstreamText() returns a ReadableStream that plays nicely with React Server Components and edge runtime
  • Structured outputgenerateObject() with a Zod schema, no JSON parsing
  • Tool use — same interface for Anthropic's tools and OpenAI's function_calling
  • React hooksuseChat, useCompletion, useObject for client UI

The trade is a thin abstraction layer. If you need a provider-specific feature that the AI SDK hasn't surfaced yet (vision details, extended thinking budget for Claude 4.7, etc.), you can drop down to the native SDK — both @anthropic-ai/sdk and openai are dependencies.

Setup

Pick one provider to start. Add the API key to .env.local:

ANTHROPIC_API_KEY=sk-ant-...
# or
OPENAI_API_KEY=sk-...

You don't need both. The AI SDK constructs provider clients lazily — if you only import the Anthropic provider, the OpenAI key is irrelevant.

Basic completion (server-side)

// app/some-route/page.tsx
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";

export default async function Page() {
  const { text } = await generateText({
    model: anthropic("claude-opus-4-7"),
    prompt: "Write a haiku about Next.js.",
  });

  return <p>{text}</p>;
}

Streaming completion (Route Handler)

// app/api/chat/route.ts
import { streamText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";

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

  const result = streamText({
    model: anthropic("claude-opus-4-7"),
    messages,
  });

  return result.toDataStreamResponse();
}
// app/chat/page.tsx — client component
"use client";

import { useChat } from "ai/react";

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat();

  return (
    <form onSubmit={handleSubmit}>
      {messages.map((m) => (
        <div key={m.id}>
          <b>{m.role}:</b> {m.content}
        </div>
      ))}
      <input value={input} onChange={handleInputChange} />
    </form>
  );
}

Switching providers

The single-line swap:

import { openai } from "@ai-sdk/openai";

const result = await generateText({
  model: openai("gpt-4o"),  // ← was anthropic("claude-opus-4-7")
  prompt: "...",
});

For an env-controlled swap:

// lib/ai.ts
import { anthropic } from "@ai-sdk/anthropic";
import { openai } from "@ai-sdk/openai";

export const defaultModel =
  process.env.AI_PROVIDER === "openai"
    ? openai("gpt-4o")
    : anthropic("claude-opus-4-7");

Structured output

import { generateObject } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";

const { object } = await generateObject({
  model: anthropic("claude-opus-4-7"),
  schema: z.object({
    title: z.string(),
    tags: z.array(z.string()).max(5),
    summary: z.string(),
  }),
  prompt: "Summarize this article: ...",
});

// object is fully typed as { title: string; tags: string[]; summary: string }

This is significantly more reliable than asking for JSON and parsing it manually. The AI SDK does retries on schema failures.

Tool use

import { streamText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";

const result = streamText({
  model: anthropic("claude-opus-4-7"),
  prompt: "What's the weather in Tokyo?",
  tools: {
    getWeather: tool({
      description: "Get the current weather for a city",
      parameters: z.object({
        city: z.string(),
      }),
      execute: async ({ city }) => {
        // your real API call here
        return { city, tempC: 22, conditions: "clear" };
      },
    }),
  },
  maxSteps: 5,
});

The model decides when to call the tool. The AI SDK handles the back-and-forth.

Cost considerations

Token costs add up fast. A few rules of thumb:

  • Use the smallest model that works. Haiku is 60× cheaper than Opus for most tasks. Try claude-haiku-4-7 first.
  • Cache prompts that don't change. Anthropic supports prompt caching via providerOptions — see the Claude API docs.
  • Stream wherever the user is waiting. Streaming is the same cost but feels 10× faster.
  • Limit max tokens. Set maxTokens to a hard ceiling so a runaway generation doesn't bankrupt you.

Production checklist

  • API keys in Vercel env vars (never in code, never in client bundles)
  • Rate limit per IP / per user — the AI SDK is happy to forward unbounded requests
  • Token budget tracking — log input/output tokens per call
  • Error handling — provider 429s and 500s happen; show a friendly message
  • PII filtering on prompts if you're handling user data

On this page