Quickstart

Add a Model

Register models with Provider + AIService. OpenAI-compatible providers auto-passthrough.

Scenario 1: OpenAI-Compatible Provider (Auto Passthrough)

For DeepSeek and other OpenAI-compatible providers:

import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { Provider, AIService, type OpenAICompatibleClientConfig } from "@downcity/city";

class DeepSeekProvider extends Provider {
  constructor() {
    super({
      id: "deepseek",
      env: { DEEPSEEK_API_KEY: "DeepSeek API Key" },
      baseURL: "https://api.deepseek.com",
      envKey: "DEEPSEEK_API_KEY",       // auto-passthrough uses this
    });
  }

  protected createClient({ apiKey, baseURL }: OpenAICompatibleClientConfig) {
    return createOpenAICompatible({ apiKey, baseURL, name: "deepseek" });
  }
}

const deepseek = new DeepSeekProvider();

const ai = new AIService();
ai.use(deepseek.model({
  id: "deepseek-v4-flash",
  name: "DeepSeek V4 Flash",
  description: "DeepSeek text model",
  default: true,
}));
base.use(ai);

Scenario 2: Custom OpenAI-Compatible Handler

Use an openai method when an OpenAI-compatible upstream needs request or response customization:

import { Provider, type Context, type AIProviderChargedResponse } from "@downcity/city";

class OpenAICustomProvider extends Provider {
  constructor() {
    super({
      id: "openai-custom",
      env: { OPENAI_API_KEY: "OpenAI API Key" },
    });
  }

  async openai(ctx: Context): Promise<AIProviderChargedResponse> {
    // Downstream: customize OpenAI body
    // Upstream: normalize response
    // (streaming: SSE event-by-event conversion)
  }
}

const openaiCustom = new OpenAICustomProvider();

Method Signatures

Pathwayctx.inputReturn
text{ prompt }{ output: UIMessage; charge? }
stream{ prompt }{ response: Response; charge? }
openai{ model, messages, stream, ... }{ response: Response; charge? }