summaryrefslogtreecommitdiffhomepage
path: root/packages/core/src/models/resolver.ts
blob: 3ada71345e788bc57cef98a992b63bc43aebd81c (plain)
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
import type { ResolvedModel } from "../types/index.js";
import type { ModelRegistry } from "./registry.js";

export class ModelResolver {
	private registry: ModelRegistry;

	constructor(registry: ModelRegistry) {
		this.registry = registry;
	}

	resolve(tag: string): ResolvedModel | null {
		const models = this.registry.getModelsByTag(tag);
		const keys = this.registry.getKeys();

		for (const keyState of keys) {
			if (keyState.status !== "active") continue;
			const model = models.find((m) => m.provider === keyState.definition.provider);
			if (model) {
				return { model, key: keyState.definition };
			}
		}

		return null;
	}

	async waitForKey(
		tag: string,
		options?: {
			pollIntervalMs?: number;
			signal?: AbortSignal;
			onWaiting?: () => void;
			onResume?: () => void;
		},
	): Promise<ResolvedModel | null> {
		const pollIntervalMs = options?.pollIntervalMs ?? 60000;
		const signal = options?.signal;

		// Try immediately first
		const immediate = this.resolve(tag);
		if (immediate) return immediate;

		// Check if aborted before entering wait state
		if (signal?.aborted) return null;

		options?.onWaiting?.();

		return new Promise<ResolvedModel | null>((resolve) => {
			let timer: ReturnType<typeof setTimeout> | null = null;

			const cleanup = () => {
				if (timer !== null) {
					clearTimeout(timer);
					timer = null;
				}
			};

			const onAbort = () => {
				cleanup();
				resolve(null);
			};

			if (signal) {
				signal.addEventListener("abort", onAbort, { once: true });
			}

			const poll = () => {
				if (signal?.aborted) {
					resolve(null);
					return;
				}

				const result = this.resolve(tag);
				if (result) {
					if (signal) {
						signal.removeEventListener("abort", onAbort);
					}
					options?.onResume?.();
					resolve(result);
					return;
				}

				timer = setTimeout(poll, pollIntervalMs);
			};

			timer = setTimeout(poll, pollIntervalMs);
		});
	}
}