The unified AI client provides a simple interface for interacting with OpenAI and Anthropic models.

Overview

The AI client (src/lib/ai/client.ts) abstracts away provider-specific details, allowing you to switch between OpenAI and Anthropic with minimal code changes.

Setup

1. Environment Variables

Add your API keys to .env.local:

# Required for OpenAI
OPENAI_API_KEY=sk-...

# Optional for Anthropic
ANTHROPIC_API_KEY=sk-ant-...

2. Basic Usage

import { generateChatCompletion } from "@/lib/ai/client";

const response = await generateChatCompletion({
  messages: [
    { role: "user", content: "What is TypeScript?" }
  ],
});

console.log(response.content); // AI response text
console.log(response.provider); // "openai" or "anthropic"
console.log(response.model); // Model used (e.g., "gpt-4")

API Reference

generateChatCompletion(options)

Generate a chat completion from an AI provider.

Parameters:

  • messages (required) - Array of chat messages

    type ChatMessage = {
      role: "user" | "assistant" | "system";
      content: string;
    };
    
  • model (optional) - Model to use (defaults based on provider)

    • OpenAI: "gpt-4", "gpt-3.5-turbo"
    • Anthropic: "claude-3-opus-20240229", "claude-3-sonnet-20240229"
  • temperature (optional) - Randomness (0-2, default: 0.7)

  • maxTokens (optional) - Maximum tokens in response (default: 1000)

  • provider (optional) - Force a specific provider: "openai" or "anthropic"

Returns:

interface ChatCompletionResult {
  content: string;
  provider: AIProvider;
  model: string;
  usage?: {
    promptTokens: number;
    completionTokens: number;
    totalTokens: number;
  };
}

Examples

Simple Chat

const response = await generateChatCompletion({
  messages: [
    { role: "user", content: "Explain React hooks in one sentence." }
  ],
});

With System Prompt

const response = await generateChatCompletion({
  messages: [
    { role: "system", content: "You are a helpful coding assistant." },
    { role: "user", content: "How do I use useEffect?" }
  ],
});

Conversation History

const messages = [
  { role: "user", content: "What is React?" },
  { role: "assistant", content: "React is a JavaScript library..." },
  { role: "user", content: "How does it differ from Vue?" }
];

const response = await generateChatCompletion({ messages });

Custom Model and Temperature

const response = await generateChatCompletion({
  messages: [{ role: "user", content: "Write a creative story." }],
  model: "gpt-4",
  temperature: 0.9, // More creative
  maxTokens: 2000,
});

Force Provider

// Use Anthropic even if OpenAI is default
const response = await generateChatCompletion({
  messages: [{ role: "user", content: "Hello!" }],
  provider: "anthropic",
});

Provider Selection

The client automatically selects a provider based on:

  1. Explicit provider parameter (if provided)
  2. Environment variables - Uses provider with available API key
  3. Default - Falls back to OpenAI if both are available

Error Handling

The client handles common errors:

  • Missing API keys - Throws clear error message
  • Rate limits - Returns error with retry information
  • Invalid requests - Validates input and returns helpful errors
  • Timeouts - Configurable timeout (default: 120 seconds)
try {
  const response = await generateChatCompletion({ messages });
} catch (error) {
  if (error.message.includes("API key")) {
    // Handle missing API key
  } else if (error.message.includes("rate limit")) {
    // Handle rate limit
  } else {
    // Handle other errors
  }
}

Best Practices

  1. Always check subscription before making requests
  2. Use system prompts for consistent behavior
  3. Set appropriate maxTokens to control costs
  4. Handle errors gracefully with try/catch
  5. Use streaming for better UX (see Streaming)

Next Steps