Downcity
Downcity City DocsGuides

Model Pathways

The two-pathway architecture of Provider + AIService.

Downcity's AI service has two independent pathways sharing a single model registry:

                    Provider.model({ id: "deepseek-v4-flash", ... })

                              ai.use(model)

              ┌─────────────────────┴─────────────────────┐
              │                                           │
         SDK Pathway                            OpenAI-Compatible Pathway
  For UserClient                              For downcity agent / curl / OpenAI SDK
              │                                           │
   POST /v1/ai/text               POST /v1/ai/chat/completions
   POST /v1/ai/stream                  ↑
              │                         OpenAI-format body
              ▼                        { model, messages, stream }
   provider.handlers.text                            │
   provider.handlers.stream                          ▼
      (ai-sdk wrapper)                  provider.handlers.openai
              │                         (passthrough or format conversion)
              ▼                                    │
         UIMessage                                ▼
      UIMessageStream                          Response
                                           (upstream raw response)

Provider Pattern

// OpenAI-compatible: no openai handler → auto-passthrough
const deepseek = new Provider("deepseek", {
  env: { DEEPSEEK_API_KEY: "DeepSeek API Key" },
  baseURL: "https://api.deepseek.com",
  envKey: "DEEPSEEK_API_KEY",
  text: deepseekTextHandler,
  stream: deepseekStreamHandler,
});

// Non-OpenAI format: must provide openai handler
const kimiCode = new Provider("kimi-code", {
  env: { KIMI_CODE_API_KEY: "Kimi Code API Key" },
  text: kimiTextHandler,
  stream: kimiStreamHandler,
  openai: kimiOpenAIHandler,  // Anthropic ↔ OpenAI conversion
});

Auto Passthrough

When no openai handler but baseURL + envKey exist, AIService auto-generates a passthrough:

POST /chat/completions → fetch(baseURL + body) → raw upstream Response

Zero adapter code. Fully OpenAI-compatible.

Format Conversion

For non-OpenAI providers (e.g., Anthropic), the openai handler does bidirectional conversion:

  • Downstream: OpenAI body → Provider format (e.g., Anthropic messages)
  • Upstream (non-streaming): Provider JSON → OpenAI JSON
  • Upstream (streaming): Provider SSE events → OpenAI SSE chunks (event by event)

Routes

RoutePathway
POST /v1/ai/textSDK
POST /v1/ai/streamSDK
POST /v1/ai/imageSDK
POST /v1/ai/videoSDK
POST /v1/ai/ttsSDK
POST /v1/ai/asrSDK
POST /v1/ai/chat/completionsOpenAI-Compatible
GET /v1/ai/modelsModel Catalog