49 lines
1.6 KiB
TypeScript
49 lines
1.6 KiB
TypeScript
import type { OllamaEnsureResult, ThtRunner } from "../tht/tht-runner.js";
|
|
import type { PrincipalContext } from "../auth/principal.js";
|
|
|
|
interface ReadyEntry {
|
|
expiresAt: number;
|
|
result: OllamaEnsureResult;
|
|
}
|
|
|
|
/**
|
|
* 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<string, Promise<OllamaEnsureResult>>();
|
|
private ready = new Map<string, ReadyEntry>();
|
|
|
|
constructor(
|
|
private tht: ThtRunner,
|
|
private timeoutSec: number,
|
|
private ttlMs = 60_000,
|
|
private now: () => number = Date.now,
|
|
) {}
|
|
|
|
ensure(workspace = "", principal?: PrincipalContext): Promise<OllamaEnsureResult> {
|
|
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 = runner.ollamaEnsure(workspace, this.timeoutSec)
|
|
.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;
|
|
}
|
|
}
|