quill/oracle Public Public: anyone can read this; nobody can change it.

workbenchthis machine
Files 03f1664f22cdaf6fba424941edb6890e6b00b5a4
112 lines · 3.8 KB · ~1.1k tok Raw History
1
/**
2
 * The ledger. One row per question the oracle has ever been asked — asking
3
 * again returns the same row, because the verdict cannot change — plus the
4
 * outcome a person recorded afterwards.
5
 */
6
7
import { Database } from "bun:sqlite";
8
import { mkdirSync } from "node:fs";
9
import { join } from "node:path";
10
import { normalize, verdictFor, type Outcome } from "./oracle.ts";
11
12
export interface Row {
13
  id: number;
14
  question: string;
15
  normalized: string;
16
  stance: string;
17
  confidence: number;
18
  auspice: string;
19
  asked_count: number;
20
  asked_at: string;
21
  outcome: Outcome;
22
  judged_at: string | null;
23
}
24
25
export function open(dataDir = process.env.FORGE_APP_DATA ?? "./data"): Database {
26
  mkdirSync(dataDir, { recursive: true });
27
  const db = new Database(join(dataDir, "oracle.db"));
28
  db.exec("PRAGMA journal_mode = WAL");
29
  db.exec(`CREATE TABLE IF NOT EXISTS verdict (
30
    id INTEGER PRIMARY KEY,
31
    question TEXT NOT NULL,
32
    normalized TEXT NOT NULL UNIQUE,
33
    stance TEXT NOT NULL,
34
    confidence INTEGER NOT NULL,
35
    auspice TEXT NOT NULL,
36
    asked_count INTEGER NOT NULL DEFAULT 1,
37
    asked_at TEXT NOT NULL DEFAULT (datetime('now')),
38
    outcome TEXT NOT NULL DEFAULT 'unjudged' CHECK (outcome IN ('unjudged','right','wrong')),
39
    judged_at TEXT
40
  )`);
41
  return db;
42
}
43
44
/**
45
 * Ask. A question the ledger already holds is not answered again: its row
46
 * comes back with one more ask against it, which is the whole conceit —
47
 * the oracle does not get a second opinion either.
48
 */
49
export function ask(db: Database, question: string): Row {
50
  const trimmed = question.trim().slice(0, 280);
51
  const key = normalize(trimmed);
52
  const existing = db
53
    .query<Row, [string]>("SELECT * FROM verdict WHERE normalized = ?1")
54
    .get(key);
55
  if (existing) {
56
    db.query("UPDATE verdict SET asked_count = asked_count + 1 WHERE id = ?1").run(
57
      existing.id,
58
    );
59
    return { ...existing, asked_count: existing.asked_count + 1 };
60
  }
61
  const verdict = verdictFor(trimmed);
62
  const inserted = db
63
    .query<Row, [string, string, string, number, string]>(
64
      `INSERT INTO verdict (question, normalized, stance, confidence, auspice)
65
       VALUES (?1, ?2, ?3, ?4, ?5) RETURNING *`,
66
    )
67
    .get(trimmed, key, verdict.stance, verdict.confidence, verdict.auspice);
68
  if (!inserted) throw new Error("oracle: the ledger refused a verdict");
69
  return inserted;
70
}
71
72
export function get(db: Database, id: number): Row | null {
73
  return db.query<Row, [number]>("SELECT * FROM verdict WHERE id = ?1").get(id);
74
}
75
76
export function recent(db: Database, limit = 8): Row[] {
77
  return db
78
    .query<Row, [number]>("SELECT * FROM verdict ORDER BY id DESC LIMIT ?1")
79
    .all(limit);
80
}
81
82
export type Filter = "all" | "unjudged" | "right" | "wrong";
83
84
export function isFilter(value: string): value is Filter {
85
  return value === "all" || value === "unjudged" || value === "right" || value === "wrong";
86
}
87
88
export function ledger(db: Database, filter: Filter = "all", limit = 200): Row[] {
89
  if (filter === "all")
90
    return db.query<Row, [number]>("SELECT * FROM verdict ORDER BY id DESC LIMIT ?1").all(limit);
91
  return db
92
    .query<Row, [string, number]>(
93
      "SELECT * FROM verdict WHERE outcome = ?1 ORDER BY id DESC LIMIT ?2",
94
    )
95
    .all(filter, limit);
96
}
97
98
/** Record how it turned out. `unjudged` takes a score back off the board. */
99
export function judge(db: Database, id: number, outcome: Outcome): Row | null {
100
  db.query<Row, [string, string | null, number]>(
101
    "UPDATE verdict SET outcome = ?1, judged_at = ?2 WHERE id = ?3",
102
  ).run(outcome, outcome === "unjudged" ? null : new Date().toISOString(), id);
103
  return get(db, id);
104
}
105
106
export function scored(db: Database): { confidence: number; outcome: string }[] {
107
  return db
108
    .query<{ confidence: number; outcome: string }, []>(
109
      "SELECT confidence, outcome FROM verdict",
110
    )
111
    .all();
112
}
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts