import type { OllamaEnsureResult, SemanticReadinessCode, ThtRunner, } from "../tht/tht-runner.js"; import type { PrincipalContext } from "../auth/principal.js"; import type { WorkspaceDescriptor } from "../workspaces/schema.js"; export type ReadinessResult = OllamaEnsureResult & { code?: SemanticReadinessCode }; interface ReadyEntry { expiresAt: number; result: ReadinessResult; } /** * Deduplicates embedding readiness checks and keeps only short-lived successes. * Failures are deliberately not cached so a submit can retry after a transient outage. */ export class ReadinessManager { private inFlight = new Map>(); private ready = new Map(); constructor( private tht: ThtRunner, private timeoutSec: number, private ttlMs = 60_000, private now: () => number = Date.now, ) {} ensure( workspace = "", principal?: PrincipalContext, descriptor?: WorkspaceDescriptor, ): Promise { const key = `${principal?.issuer ?? ""}\0${principal?.subject ?? ""}\0${workspace}`; const cached = this.ready.get(key); if (cached && cached.expiresAt > this.now()) return Promise.resolve(cached.result); if (cached) this.ready.delete(key); const current = this.inFlight.get(key); if (current) return current; const runner = principal && typeof (this.tht as any).withPrincipal === "function" ? this.tht.withPrincipal(principal) : this.tht; const pending = (async (): Promise => { try { if (descriptor) { const qdrant = await runner.qdrantEnsure(descriptor, this.timeoutSec, "self_heal"); if (!qdrant.ok) return qdrant; } const ollama = await runner.ollamaEnsure(workspace, this.timeoutSec); return ollama.ok ? ollama : { ...ollama, code: "workspace_not_activatable" }; } catch { return { ok: false, code: "workspace_not_activatable" }; } })() .then((result) => { if (result.ok) { this.ready.set(key, { result, expiresAt: this.now() + this.ttlMs }); } return result; }) .finally(() => { if (this.inFlight.get(key) === pending) this.inFlight.delete(key); }); this.inFlight.set(key, pending); return pending; } }