Downcity
Downcity City DocsQuickstart

Try It Now

Run a complete call chain with the default database, AdminClient, and UserClient.

1. Start City

import { City, 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 City({ db, dialect: "sqlite", raw: sqlite });

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 ai = new AIService();
ai.use(echo.model({ id: "local-echo", name: "Local Echo", default: true }));
base.use(ai);

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

2. Create Studio & Token

const admin = new AdminClient({
  base_url: "http://127.0.0.1:43127",
  admin_secret_key: process.env.DOWNCITY_CITY_ADMIN_SECRET_KEY,
});
const studio = await admin.studios.create({ name: "Demo Studio" });
const token = await admin.studios.tokens.apply({
  studio_id: studio.studio_id,
  user_id: "user_123",
  ttl: "7d",
});

3. Client Call

const client = new UserClient({
  base_url: "http://127.0.0.1:43127",
  studio_id: studio.studio_id,
  user_token: token.user_token,
});

// SDK pathway
const result = await client.ai.text({ prompt: "Hello" });

// OpenAI-compatible pathway
// POST /v1/ai/chat/completions

Next Steps