-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
114 lines (95 loc) · 2.83 KB
/
Copy pathindex.ts
File metadata and controls
114 lines (95 loc) · 2.83 KB
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import { config } from "./lib/config.js";
import { createLogger } from "./lib/logger.js";
import { ChromaStore } from "./store/chroma.js";
import { MemoryStore } from "./store/memory.js";
import type { StoreBackend } from "./store/base.js";
import { startServer } from "./api/server.js";
const log = createLogger("Engram");
async function main() {
log.info("Engram starting", {
backend: config.STORE_BACKEND,
port: config.API_PORT,
collection: config.COLLECTION_NAME,
});
const store: StoreBackend =
config.STORE_BACKEND === "chroma" ? new ChromaStore() : new MemoryStore();
await store.initialize();
if (config.PRUNE_ON_STARTUP) {
const pruned = await store.pruneExpired();
if (pruned > 0) {
log.info("Pruned on startup", { count: pruned });
}
}
const server = startServer(store);
const stopPruneLoop = startPruneLoop(store);
const cleanupSignalHandlers = registerShutdownHandlers(() => {
log.warn("Shutdown requested. Stopping API server and prune loop.");
stopPruneLoop();
server.stop();
});
log.info(
`Ready - POST http://localhost:${config.API_PORT}/memories to store, POST /search to retrieve`
);
return () => {
cleanupSignalHandlers();
stopPruneLoop();
server.stop();
};
}
function startPruneLoop(store: StoreBackend): () => void {
const intervalMs = config.PRUNE_INTERVAL_HOURS * 60 * 60 * 1000;
let stopped = false;
let timer: ReturnType<typeof setTimeout> | null = null;
let pruneInFlight = false;
const scheduleNext = () => {
if (stopped) {
return;
}
timer = setTimeout(async () => {
if (pruneInFlight) {
log.warn("Skipping scheduled prune because a previous prune is still running");
scheduleNext();
return;
}
pruneInFlight = true;
const startedAt = Date.now();
try {
const count = await store.pruneExpired();
if (count > 0) {
log.info("Scheduled prune", { count, durationMs: Date.now() - startedAt });
}
} catch (err) {
log.error("Scheduled prune failed", {
error: err instanceof Error ? err.message : String(err),
});
} finally {
pruneInFlight = false;
scheduleNext();
}
}, intervalMs);
};
scheduleNext();
return () => {
stopped = true;
if (timer) {
clearTimeout(timer);
timer = null;
}
};
}
function registerShutdownHandlers(onShutdown: () => void): () => void {
const handleSignal = (signal: NodeJS.Signals) => {
log.warn("Shutdown signal received", { signal });
onShutdown();
};
process.once("SIGINT", handleSignal);
process.once("SIGTERM", handleSignal);
return () => {
process.off("SIGINT", handleSignal);
process.off("SIGTERM", handleSignal);
};
}
main().catch((err) => {
console.error("Fatal:", err);
process.exit(1);
});