Service 服务AI 模型服务

调用 AI

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

SDK 通路

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

const client = new City({
  role: "user",
  federation_url,
  city_id,
  user_token,
});

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

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

// 流式生成
const stream = await client.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);
}

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

CityModel 和 provider 的关系

如果调用目标就是当前 Federation 里的模型,优先使用 CityModel。模型目录返回的 CityModel 已经带有隐藏的运行时连接信息,Downcity Agent 和 City SDK 都可以直接使用它:

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

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

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

const agent = new Agent({
  prepare_session: async (session) => {
    await session.set({ model: normalizeAgentModel(model) });
  },
});

这条路径不需要产品侧创建 AI SDK provider。宿主先把 CityModel 转换为运行时模型, 再注入本地 Session。

OpenAI 兼容通路

当调用方必须使用 AI SDK 标准 LanguageModel,或者需要接入 OpenAI SDK、 createOpenAICompatible() 这类外部生态时,再创建 provider。provider 仍然请求 Federation,而不是直接请求真实上游模型:

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

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

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

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

请求会进入 Federation 的 AIService:

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

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

调用路径选择

场景推荐方式
产品代码直接请求 Fed 文本或流式生成client.ai.text() / client.ai.stream()
Downcity Agent 使用 Fed 模型直接传 CityModel
外部 AI SDK 生态需要 LanguageModelcreateOpenAICompatible({ baseURL: client.ai.base_url })
OpenAI SDK、curl 或兼容客户端调试/v1/ai/chat/completions
图片、TTS、ASR、videoclient.ai.image_create() / client.ai.tts() / client.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: client.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 || "");
}