Service 服务AI 模型服务

调用 AI

通过 User City 和第三方工具调用 AIService。

SDK 通路

import { City } from "@downcity/federation/legacy";

const city = new City({
  federation_url,
  user_token,
});

// 获取模型目录
const catalog = await city.ai.catalog();
const model = catalog.get("deepseek-v4-flash");

// 文本生成
const msg = await city.ai.text({ prompt: "hello", model });
console.log(msg.parts[0].text);

// 流式生成
const stream = await city.ai.stream({ prompt: "hello", model });
const reader = stream.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  if (value.type === "text-delta") process.stdout.write(value.textDelta);
}

city.ai.catalog()user_token 身份下只返回当前已经满足运行时 env 的模型;同一条路由在管理员会话 Token 身份下会返回全部代码注册模型和它们的 env_requirements

CityModel 和 provider 的关系

如果调用目标就是当前 Federation 里的模型,优先使用 CityModel。模型目录返回的 CityModel 自身实现 AI SDK LanguageModelV3,Downcity Agent 和 AI SDK 都可以直接使用它:

import { Agent } from "@downcity/agent";

const catalog = await city.ai.catalog();
const model = catalog.get("deepseek-v4-flash");
if (!model) throw new Error("model not found");

await city.ai.text({
  model,
  prompt: "hello",
});

const agent = new Agent({
  model,
});

这条路径不需要产品侧创建 AI SDK provider。Agent 直接调用 CityModel.doStream(), CityModel 请求 Federation 的 /v1/ai/streamcity.ai.stream() 也使用同一个 CityModel,只在客户端额外转换成 UIMessageChunk。Session 如有自己的 AgentModel 实例会优先使用。

OpenAI 兼容通路

当调用方需要接入 OpenAI SDK、curl 或 OpenAI-compatible 客户端时,再创建兼容 provider。provider 仍然请求 Federation,而不是直接请求真实上游模型:

import { createOpenAICompatible } from "@ai-sdk/openai-compatible";

const provider = createOpenAICompatible({
  name: "downcity",
  baseURL: city.ai.base_url,
  apiKey: user_token,
});

const model = provider.languageModel("deepseek-v4-flash");

请求体保持标准 OpenAI-compatible JSON。model 来自 languageModel(model_id)bureau_id 由服务端从 bearer user_token 中解析, 不需要放进请求体。

请求会进入 Federation 的 AIService:

POST /v1/ai/chat/completions
  -> 校验 user_token
  -> 从 token 解析 bureau_id / user_id
  -> 读取 body.model
  -> 找到模型绑定的 provider
  -> 由 provider 调用真实上游模型
  -> 返回 OpenAI-compatible response

前端只关心 model_id,不需要知道真实 provider 是 OpenAI、DeepSeek、Gemini 还是自定义 provider。

调用路径选择

场景推荐方式
产品代码直接请求 Fed 文本或流式生成city.ai.text() / city.ai.stream()
Downcity Agent 使用 Fed 模型直接传 CityModel
AI SDK 生态需要 LanguageModel直接传 CityModel
OpenAI SDK、curl 或兼容客户端调试/v1/ai/chat/completions
图片、TTS、ASR、videocity.ai.image_create() / city.ai.tts() / city.ai.asr() 等 City SDK 方法
使用非 Fed 模型 API产品侧自己创建对应 provider

curl

curl http://127.0.0.1:43127/v1/ai/chat/completions \
  -H "Authorization: Bearer <user_token>" \
  -d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"hello"}],"stream":true}'

OpenAI SDK

import OpenAI from "openai";

const openai = new OpenAI({
  baseURL: city.ai.base_url,
  apiKey: user_token,
});

const stream = await openai.chat.completions.create({
  model: "deepseek-v4-flash",
  messages: [{ role: "user", content: "你好" }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content || "");
}