File size: 2,160 Bytes
ab4f92b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Browser UI for trying prompts (zero dependencies).
//
//   bun ui.ts          then open http://localhost:3000
//   PORT=8080 node ui.ts
//
// Serves ui.html and a JSON endpoint backed by the plain-TypeScript model in model.ts.

import { readFileSync } from "node:fs";
import { createServer } from "node:http";
import { join } from "node:path";
import { Autocompleter } from "./model.ts";

const DIR = import.meta.dirname ?? new URL(".", import.meta.url).pathname;
const PORT = Number(process.env.PORT ?? 3000);

console.log("Loading model...");
const ac = new Autocompleter(DIR);
const html = readFileSync(join(DIR, "ui.html"));
const cfg = ac.model.cfg;
const info = {
  params: ac.model.numParams,
  vocab: cfg.vocab_size,
  layers: cfg.num_hidden_layers,
  hidden: cfg.hidden_size,
  neurons: cfg.intermediate_size,
};

function json(res: import("node:http").ServerResponse, status: number, body: unknown): void {
  res.writeHead(status, { "content-type": "application/json" });
  res.end(JSON.stringify(body));
}

createServer((req, res) => {
  if (req.method === "GET" && (req.url === "/" || req.url === "/index.html")) {
    res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
    res.end(html);
  } else if (req.method === "GET" && req.url === "/api/info") {
    json(res, 200, info);
  } else if (req.method === "POST" && req.url === "/api/complete") {
    let body = "";
    req.on("data", (chunk) => (body += chunk));
    req.on("end", () => {
      try {
        const { prompt, topK } = JSON.parse(body || "{}");
        if (typeof prompt !== "string" || prompt.length > 1000) return json(res, 400, { error: "prompt must be a string under 1000 chars" });
        const t0 = performance.now();
        const result = prompt.trim() ? ac.complete(prompt, Math.min(Number(topK) || 5, 20)) : { prompt, completion: "", top: [] };
        json(res, 200, { ...result, ms: Math.round(performance.now() - t0) });
      } catch (e) {
        json(res, 400, { error: String(e) });
      }
    });
  } else {
    res.writeHead(404).end();
  }
}).listen(PORT, "127.0.0.1", () => console.log(`tiny-autocomplete UI: http://localhost:${PORT}`));