快速开始

现在怎么用

用默认数据库、Admin City 和 User City 跑通一条完整调用链。

如果你想先按仓库里的现成产品体验,入口是:

  • cities/node
  • cli/city

跑通后,你会得到这个调用链:

Town Client
    ↓ Admin City 创建 town / 签发 user_token
Downcity City
    ↓ User City 携带 user_token + town_id 调用 service
Provider → AIService → handler

1. 启动 City

import { CityBase, AIService, Provider } from "@downcity/city";
import Database from "better-sqlite3";
import { drizzle } from "drizzle-orm/better-sqlite3";

const sqlite = new Database("./data.sqlite");
sqlite.pragma("journal_mode = WAL");

const db = Object.assign(drizzle(sqlite), {
  $client: { exec: (sql: string) => sqlite.exec(sql) },
});

const base = new CityBase({ db, dialect: "sqlite", raw: sqlite });

// 注册一个 echo 模型
const echo = new Provider("echo", {
  text: async (ctx) => ({
    id: crypto.randomUUID(),
    role: "assistant",
    parts: [{ type: "text", text: `Echo: ${ctx.input.prompt}` }],
  }),
  stream: async (ctx) => {
    const { createUIMessageStreamResponse } = await import("ai");
    return createUIMessageStreamResponse({
      execute: async ({ writer }) => {
        writer.write({ type: "text-delta", textDelta: `Echo: ${ctx.input.prompt}` });
      },
    });
  },
});

const ai = new AIService();
ai.use(echo.model({ id: "local-echo", name: "Local Echo", default: true }));
base.use(ai);

import { serve } from "@hono/node-server";

await base.health();
serve({ fetch: base.router().fetch, port: 43127, hostname: "127.0.0.1" });

2. 创建 town 并签发 token

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

const admin = new City({
  role: "admin",
  city_url: "http://127.0.0.1:43127",
  admin_secret_key: process.env.DOWNCITY_CITY_ADMIN_SECRET_KEY,
});

const town = await admin.towns.create({ name: "Demo Town" });
const token = await admin.towns.tokens.apply({
  town_id: town.town_id,
  user_id: "user_123",
  metadata: { plan: "pro" },
  ttl: "7d",
});

3. 产品侧调用

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

const client = new City({
  role: "user",
  city_url: "http://127.0.0.1:43127",
  town_id: town.town_id,
  user_token: token.user_token,
});

const catalog = await client.ai.listModels();

// SDK 通路
const result = await client.ai.text({
  model: catalog.default(),
  prompt: "你好 Downcity",
});

// OpenAI 兼容通路(curl)
// POST /v1/ai/chat/completions
// { model: "local-echo", messages: [{ role: "user", content: "hello" }] }

4. 你真正复用的是什么

多个产品接入后,复用的是这些东西:

  • .env 中的 provider key
  • user_token 校验
  • AIService 的模型路由
  • hook 里的套餐、限额、日志和扣费

下一步: