Service 服务AI 模型服务

Provider

Provider 封装第三方 AI 提供商的 action 实现和连接信息。

Provider 封装第三方 AI 的 action 实现和连接信息。通过 .model() 生成模型配置,传给 AIService.use() 注册。

创建 Provider

import { Provider } from "@downcity/city";

const deepseek = new Provider("deepseek", {
  // 连接信息
  baseURL: "https://api.deepseek.com/v1",
  envKey: "DEEPSEEK_API_KEY",
  passthroughModel: "deepseek-chat",

  // 环境变量声明
  env: { DEEPSEEK_API_KEY: "DeepSeek API Key" },

  // SDK 通路 action
  text: async (ctx) => { /* generateText */ },
  stream: async (ctx) => { /* streamText */ },
});

图片 Provider

@downcity/city 不内置 Luchi、302.ai、OpenAI、Gemini 这类具体图片 Provider 构造器。City 包只提供 Provider 抽象、AIService 注册机制,以及 client.ai.image() 的统一调用协议。

具体上游适配应放在具体 City 项目里实现,例如 cities/edge/src/image-provider.ts。这些 adapter 可以把不同上游的响应归一成 AI SDK UIMessage file parts,然后通过 Provider.model() 注册给 AIService

import { Provider } from "@downcity/city";

const imageProvider = new Provider("my-image", {
  env: { MY_IMAGE_API_KEY: "My Image API Key" },
  image: async (ctx) => {
    const response = await fetch("https://image.example.com/generate", {
      method: "POST",
      headers: {
        authorization: `Bearer ${ctx.env("MY_IMAGE_API_KEY")}`,
        "content-type": "application/json",
      },
      body: JSON.stringify(ctx.input),
    });
    const data = await response.json();

    return {
      id: crypto.randomUUID(),
      role: "assistant",
      parts: [
        {
          type: "file",
          mediaType: "image/png",
          url: data.image_url,
        },
      ],
    };
  },
});

ai.use(imageProvider.model({
  id: "image-basic",
  name: "Image Basic",
  default: ["image"],
}));

调用端仍然保持统一:

const msg = await client.ai.image({
  model: "image-basic",
  prompt: "A clean product photo of a ceramic mug",
  ratio: "1:1",
  quality: "standard",
  count: 1,
});

const file = msg.parts.find((part) => part.type === "file");

自动透传

baseURL + envKey 但没有 openai action 时,AIService 自动生成透传 action:

  • 第三方工具的 OpenAI body 原样转发到上游
  • 上游 Response 原样返回
  • passthroughModel 替换 body.model(有则替换)
// 这行不用写 — AIService 自动做
// openai: async (ctx) => fetch(`${baseURL}/chat/completions`, ...)

Anthropic 格式 Provider

非 OpenAI 格式的 Provider 需要写 openai action 做格式转换:

const kimiCode = new Provider("kimi-code", {
  env: { KIMI_CODE_API_KEY: "Kimi Code API Key" },
  text: myAnthropicTextAction,
  stream: myAnthropicStreamAction,

  // 必须写:Anthropic ↔ OpenAI 格式双向转换
  openai: async (ctx) => {
    const anthropicBody = openaiToAnthropic(ctx.input);
    const response = await fetch("https://api.kimi.com/coding/v1/messages", {
      headers: { "x-api-key": apiKey, "anthropic-version": "2023-06-01" },
      body: JSON.stringify(anthropicBody),
    });
    if (ctx.input.stream) {
      return anthropicStreamToOpenAIStream(response);
    }
    return anthropicToOpenAIResponse(await response.json());
  },
});

Provider 字段

字段必填说明
baseURL透传时必填上游 API 地址
envKey透传时必填API Key 环境变量名
passthroughModel透传时替换 body.model
textSDK 文本生成 action
streamSDK 流式生成 action
imageSDK 图片生成 action
videoSDK 视频生成 action
openai/chat/completions action(未提供则自动透传)