@citysage/ai-sdk
v0.1.0
Published
Unified AI SDK provider that routes provider/model-id strings to the right underlying provider
Downloads
29
Readme
@citysage/ai-sdk
A tiny, type-safe routing provider for the Vercel AI SDK that unifies OpenAI, Anthropic, Azure OpenAI, and Google Vertex AI behind a single provider/model-id interface.
Quick Start
bun add @citysage/ai-sdk ai @ai-sdk/openai @ai-sdk/anthropicimport { generateText } from "ai";
import { citysage } from "@citysage/ai-sdk";
const result = await generateText({
model: citysage("openai/gpt-4.1"),
prompt: "Hello!",
});
console.log(result.text);Features
- Single interface — Route to any provider with
"provider/model-id"strings - Type-safe — Full TypeScript support with branded model ID type
- Lazy loading — Only loads provider packages you actually use
- Clear errors — Helpful env var validation with actionable messages
- Optional peers — Install only the providers you need
- Runtime-friendly — Works in Node.js and Bun with lazy peer loading
Supported Providers
| Prefix | Package | Auth |
|--------|---------|------|
| openai | @ai-sdk/openai | OPENAI_API_KEY |
| anthropic | @ai-sdk/anthropic | ANTHROPIC_API_KEY |
| azure | @ai-sdk/azure | AZURE_API_KEY + AZURE_RESOURCE_NAME |
| vertex | @ai-sdk/google-vertex | GOOGLE_VERTEX_PROJECT + GOOGLE_VERTEX_LOCATION |
| vertex-anthropic | @ai-sdk/google-vertex/anthropic | Same as vertex |
Usage Examples
Chat with OpenAI
import { streamText } from "ai";
import { citysage } from "@citysage/ai-sdk";
const { textStream } = await streamText({
model: citysage("openai/gpt-4.1"),
prompt: "Write a haiku about TypeScript",
});
for await (const chunk of textStream) {
process.stdout.write(chunk);
}Anthropic Claude
import { generateText } from "ai";
import { citysage } from "@citysage/ai-sdk";
const result = await generateText({
model: citysage("anthropic/claude-opus-4.6"),
messages: [
{ role: "user", content: "What is TypeScript?" },
],
});Azure OpenAI
import { citysage } from "@citysage/ai-sdk";
import { generateText } from "ai";
// deployment name as the model ID
const result = await generateText({
model: citysage("azure/my-gpt4-deployment"),
prompt: "Explain quantum computing",
});Google Vertex AI
import { citysage } from "@citysage/ai-sdk";
import { generateText } from "ai";
const result = await generateText({
model: citysage("vertex/gemini-2.5-pro"),
prompt: "List 5 AI breakthroughs in 2025",
});Anthropic on Vertex
import { citysage } from "@citysage/ai-sdk";
import { streamText } from "ai";
const { textStream } = await streamText({
model: citysage("vertex-anthropic/claude-sonnet-4.6"),
prompt: "Design a TypeScript API",
});With AI SDK Tools & Agents
import { citysage } from "@citysage/ai-sdk";
import { ToolLoopAgent } from "ai";
const agent = new ToolLoopAgent({
model: citysage("openai/gpt-4.1"),
tools: {
getCurrentWeather: {
description: "Get the current weather in a location",
parameters: {
type: "object",
properties: {
location: { type: "string" },
},
},
execute: async ({ location }) => `Sunny in ${location}`,
},
},
});
const response = await agent.run("What's the weather in SF?");Environment Variables
OpenAI
OPENAI_API_KEY=sk-...Anthropic
ANTHROPIC_API_KEY=sk-ant-...Azure OpenAI
AZURE_RESOURCE_NAME=your-resource-name
AZURE_API_KEY=...Google Vertex (all modes)
GOOGLE_VERTEX_PROJECT=your-project-id
GOOGLE_VERTEX_LOCATION=us-central1Choose one authentication method:
# Option 1: API key (simplest)
GOOGLE_VERTEX_API_KEY=...
# Option 2: Service account JSON
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
# Option 3: Direct service account (edge/Node.js)
[email protected]
GOOGLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----...
# Option 4: Default application credentials (local dev)
gcloud auth application-default loginAPI
citysage
Default provider instance. Callable function or use .languageModel().
import { citysage } from "@citysage/ai-sdk";
// These are equivalent:
const m1 = citysage("openai/gpt-4.1");
const m2 = citysage.languageModel("openai/gpt-4.1");createCitysage()
Factory function for creating independent provider instances (useful for tests, DI).
import { createCitysage } from "@citysage/ai-sdk";
const citysage = createCitysage();
const model = citysage("openai/gpt-4.1");Type Definitions
import type { CitysageModelId, CitysageProvider, SupportedProvider } from "@citysage/ai-sdk";
// Branded type for model IDs — TypeScript enforces the format at compile time
type MyModelId = CitysageModelId; // "openai/gpt-4.1" ✓, "openai" ✗
// Provider type
type MyProvider = CitysageProvider;
// Union of supported prefixes
type Prefix = SupportedProvider; // "openai" | "anthropic" | "azure" | ...Error Handling
Missing Environment Variables
try {
citysage("openai/gpt-4.1");
} catch (e) {
// [citysage] Missing environment variable for "openai" provider:
// OPENAI_API_KEY
// Set it in your environment or .env file.
}Invalid Model ID Format
citysage("openai"); // throws: Invalid model ID "openai". Expected format: "provider/model-id"Unknown Provider
citysage("groq/llama-3" as any);
// throws: Unknown provider "groq". Supported: openai | anthropic | azure | vertex | vertex-anthropicFine-Tuned Model IDs
Model IDs are passed through unchanged after the first /, so fine-tuned models work:
// OpenAI fine-tuned model
citysage("openai/ft:gpt-4.1:org:custom:id")
// Azure deployment (use your deployment name, not raw model name)
citysage("azure/my-finetuned-deployment")Contributing
Issues and PRs welcome. Run tests:
bun test
bun run typecheckLicense
MIT
