AIChannel 与模型注册
用 AIChannel 连接上游能力,并通过 AIModelSpec 注册 Federation 模型。
AIChannel 是 Federation 服务端的上游 AI 执行渠道。它只负责连接凭据、选择真实上游模型、设置 AI SDK providerOptions,以及执行标准 LanguageModelV3 流。
AIChannel 不是 AI SDK Provider。AI SDK Provider 是 createOpenAI()、createDeepSeek() 返回的厂商模型工厂;AIChannel 是 Downcity 在 Federation 服务端对这些厂商能力的编排边界。
最小示例
import { createDeepSeek } from "@ai-sdk/deepseek";
import {
AIChannel,
AIService,
read_required_env,
type AIChannelStreamInput,
type LanguageModelV3StreamResult,
} from "@downcity/federation";
class DeepSeekChannel extends AIChannel {
constructor() {
super({
id: "deepseek",
env_key: "DEEPSEEK_API_KEY",
base_url: "https://api.deepseek.com/v1",
ai_sdk_provider_id: "deepseek",
});
}
protected async stream(
input: AIChannelStreamInput,
): Promise<LanguageModelV3StreamResult> {
const deepseek = createDeepSeek({
apiKey: read_required_env(input, this.env_key ?? ""),
baseURL: this.base_url,
});
return deepseek(input.model.upstream_model).doStream(input.call);
}
}
const deepseek_channel = new DeepSeekChannel();
const ai = new AIService();
ai.use(deepseek_channel.model({
id: "deepseek-v4-flash",
upstream_model: "deepseek-chat",
name: "DeepSeek V4 Flash",
context_window: 128_000,
tags: ["text"],
}));
federation.use(ai);upstream_model 必须写在具体模型上。Channel 可以注册多个 Federation 模型,每个模型可以指向不同的真实上游 ID。
数据结构
AIChannelOptions
-> AIChannel
-> AIModelSpec
-> AIModelDefinition
-> CityModelDescriptor
-> CityModel + LanguageModelV3AIChannelOptions:Channel 级连接配置,包括id、env_key、base_url、ai_sdk_provider_id和默认ai_sdk_provider_options。AIModelSpec:传给channel.model()的模型声明,包括 Federation ID、upstream_model、展示信息、reasoning、fallback 和模型级选项。AIModelDefinition:AIService 内部完整模型,包含channel_id、统一的runtime字段和私有连接配置。CityModelDescriptor:/v1/ai/models返回的公开目录结构,不含密钥、上游 ID 和 providerOptions。CityModel:客户端可执行对象,同时包含公开 descriptor 字段并实现 AI SDKLanguageModelV3。
使用 AIChannel 默认 reasoning 映射时需要设置 ai_sdk_provider_id,其值必须对应 providerOptions 命名空间,例如 openai、deepseek 或 anthropic。私有协议可以改为覆盖 build_reasoning_provider_options(input)。
唯一语言入口
以下入口最终都调用同一个 AIChannel.stream(input):
city.ai.text() -> AI SDK generateText -> AIChannel.stream
city.ai.stream() -> CityModel.doStream -> AIChannel.stream
CityModel.doGenerate() -> CityModel.doStream -> AIChannel.stream
/v1/ai/chat/completions -> OpenAI adapter -> AIChannel.stream/chat/completions 不再 raw passthrough,也不存在 AIChannel.openai()。AIService 会把 OpenAI messages、tools、tool choice 和生成参数转换成 LanguageModelV3CallOptions,再把标准流转换回 OpenAI JSON 或 SSE。
AI SDK providerOptions
Channel 和模型都可以声明服务端私有选项:
const openai_channel = new OpenAIResponsesChannel({
id: "openai",
env_key: "OPENAI_API_KEY",
ai_sdk_provider_id: "openai",
ai_sdk_provider_options: {
openai: { store: true, serviceTier: "default" },
},
});
ai.use(openai_channel.model({
id: "gpt-5.6-luna",
upstream_model: "gpt-5.6-luna",
name: "GPT-5.6 Luna",
ai_sdk_provider_options: {
openai: { store: false, serviceTier: "priority" },
},
}));合并顺序是:
Channel 默认值 < Model 覆盖值 < AIService 已校验的 reasoning 选项这些选项不会进入模型目录,也不接受 CityModel 客户端传入的 providerOptions。store 是 OpenAI Responses 的服务端应用状态保留策略,不是 prompt cache 开关。
Reasoning 与 fallback
reasoning 和 fallback 都属于具体模型:
ai.use(channel.model({
id: "text-model",
upstream_model: "vendor-text-model",
name: "Text Model",
reasoning: {
efforts: [{ id: "high", name: "高" }],
default_effort: "high",
},
fallback: [{
match: (media) => media.media_type.startsWith("image/"),
model_id: "vision-model",
}],
}));AIService 先完成 fallback,再按最终模型校验 reasoning。可信结果通过 input.reasoning 传入;AIChannel 会在调用 stream() 前把映射后的 providerOptions 注入 input.call。
图片与其它模态
语言模型统一使用 stream()。图片、视频、TTS、ASR 仍是 Channel 的显式方法:image_create()、image_fetch()、video()、tts()、asr()。
图片 Channel 返回 AIImageCreateResult 和 AIImageResult。AIService 负责 async_jobs 存储、Queue 调度、状态落库和幂等计费;Channel 只负责上游协议与标准结果之间的转换。
字段与方法
| 成员 | 作用 |
|---|---|
id | Federation 内 Channel 唯一 ID |
env / env_key | 声明并读取 Federation env |
base_url | Channel 子类使用的上游 API 根地址 |
stream(input) | 唯一语言模型执行入口 |
input.call | 已清理并注入服务端 providerOptions 的 V3 调用 |
input.model | 最终 Federation 模型 ID 与上游模型 ID |
model(spec) | 将 AIModelSpec 变成可注册的 AIModelDefinition |
bill(input) | 从受限的 AIBillInput 生成可选账单草稿 |