@sriinnu/kosha-discovery 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +286 -0
  3. package/dist/aliases.d.ts +51 -0
  4. package/dist/aliases.d.ts.map +1 -0
  5. package/dist/aliases.js +115 -0
  6. package/dist/aliases.js.map +1 -0
  7. package/dist/cache.d.ts +57 -0
  8. package/dist/cache.d.ts.map +1 -0
  9. package/dist/cache.js +123 -0
  10. package/dist/cache.js.map +1 -0
  11. package/dist/cli-commands.d.ts +142 -0
  12. package/dist/cli-commands.d.ts.map +1 -0
  13. package/dist/cli-commands.js +746 -0
  14. package/dist/cli-commands.js.map +1 -0
  15. package/dist/cli-format.d.ts +119 -0
  16. package/dist/cli-format.d.ts.map +1 -0
  17. package/dist/cli-format.js +171 -0
  18. package/dist/cli-format.js.map +1 -0
  19. package/dist/cli.d.ts +15 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +143 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/credentials/index.d.ts +2 -0
  24. package/dist/credentials/index.d.ts.map +1 -0
  25. package/dist/credentials/index.js +2 -0
  26. package/dist/credentials/index.js.map +1 -0
  27. package/dist/credentials/resolver.d.ts +170 -0
  28. package/dist/credentials/resolver.d.ts.map +1 -0
  29. package/dist/credentials/resolver.js +597 -0
  30. package/dist/credentials/resolver.js.map +1 -0
  31. package/dist/discovery/anthropic.d.ts +49 -0
  32. package/dist/discovery/anthropic.d.ts.map +1 -0
  33. package/dist/discovery/anthropic.js +101 -0
  34. package/dist/discovery/anthropic.js.map +1 -0
  35. package/dist/discovery/base.d.ts +59 -0
  36. package/dist/discovery/base.d.ts.map +1 -0
  37. package/dist/discovery/base.js +123 -0
  38. package/dist/discovery/base.js.map +1 -0
  39. package/dist/discovery/bedrock.d.ts +122 -0
  40. package/dist/discovery/bedrock.d.ts.map +1 -0
  41. package/dist/discovery/bedrock.js +356 -0
  42. package/dist/discovery/bedrock.js.map +1 -0
  43. package/dist/discovery/cerebras.d.ts +43 -0
  44. package/dist/discovery/cerebras.d.ts.map +1 -0
  45. package/dist/discovery/cerebras.js +65 -0
  46. package/dist/discovery/cerebras.js.map +1 -0
  47. package/dist/discovery/cohere.d.ts +57 -0
  48. package/dist/discovery/cohere.d.ts.map +1 -0
  49. package/dist/discovery/cohere.js +82 -0
  50. package/dist/discovery/cohere.js.map +1 -0
  51. package/dist/discovery/deepinfra.d.ts +41 -0
  52. package/dist/discovery/deepinfra.d.ts.map +1 -0
  53. package/dist/discovery/deepinfra.js +86 -0
  54. package/dist/discovery/deepinfra.js.map +1 -0
  55. package/dist/discovery/deepseek.d.ts +17 -0
  56. package/dist/discovery/deepseek.d.ts.map +1 -0
  57. package/dist/discovery/deepseek.js +45 -0
  58. package/dist/discovery/deepseek.js.map +1 -0
  59. package/dist/discovery/fireworks.d.ts +48 -0
  60. package/dist/discovery/fireworks.d.ts.map +1 -0
  61. package/dist/discovery/fireworks.js +101 -0
  62. package/dist/discovery/fireworks.js.map +1 -0
  63. package/dist/discovery/glm.d.ts +17 -0
  64. package/dist/discovery/glm.d.ts.map +1 -0
  65. package/dist/discovery/glm.js +52 -0
  66. package/dist/discovery/glm.js.map +1 -0
  67. package/dist/discovery/google.d.ts +53 -0
  68. package/dist/discovery/google.d.ts.map +1 -0
  69. package/dist/discovery/google.js +122 -0
  70. package/dist/discovery/google.js.map +1 -0
  71. package/dist/discovery/groq.d.ts +44 -0
  72. package/dist/discovery/groq.d.ts.map +1 -0
  73. package/dist/discovery/groq.js +89 -0
  74. package/dist/discovery/groq.js.map +1 -0
  75. package/dist/discovery/index.d.ts +48 -0
  76. package/dist/discovery/index.d.ts.map +1 -0
  77. package/dist/discovery/index.js +116 -0
  78. package/dist/discovery/index.js.map +1 -0
  79. package/dist/discovery/llama-cpp.d.ts +45 -0
  80. package/dist/discovery/llama-cpp.d.ts.map +1 -0
  81. package/dist/discovery/llama-cpp.js +137 -0
  82. package/dist/discovery/llama-cpp.js.map +1 -0
  83. package/dist/discovery/minimax.d.ts +16 -0
  84. package/dist/discovery/minimax.d.ts.map +1 -0
  85. package/dist/discovery/minimax.js +56 -0
  86. package/dist/discovery/minimax.js.map +1 -0
  87. package/dist/discovery/mistral.d.ts +45 -0
  88. package/dist/discovery/mistral.d.ts.map +1 -0
  89. package/dist/discovery/mistral.js +82 -0
  90. package/dist/discovery/mistral.js.map +1 -0
  91. package/dist/discovery/moonshot.d.ts +16 -0
  92. package/dist/discovery/moonshot.d.ts.map +1 -0
  93. package/dist/discovery/moonshot.js +46 -0
  94. package/dist/discovery/moonshot.js.map +1 -0
  95. package/dist/discovery/nvidia.d.ts +60 -0
  96. package/dist/discovery/nvidia.d.ts.map +1 -0
  97. package/dist/discovery/nvidia.js +145 -0
  98. package/dist/discovery/nvidia.js.map +1 -0
  99. package/dist/discovery/ollama.d.ts +57 -0
  100. package/dist/discovery/ollama.d.ts.map +1 -0
  101. package/dist/discovery/ollama.js +151 -0
  102. package/dist/discovery/ollama.js.map +1 -0
  103. package/dist/discovery/openai-compatible.d.ts +126 -0
  104. package/dist/discovery/openai-compatible.d.ts.map +1 -0
  105. package/dist/discovery/openai-compatible.js +161 -0
  106. package/dist/discovery/openai-compatible.js.map +1 -0
  107. package/dist/discovery/openai.d.ts +62 -0
  108. package/dist/discovery/openai.d.ts.map +1 -0
  109. package/dist/discovery/openai.js +157 -0
  110. package/dist/discovery/openai.js.map +1 -0
  111. package/dist/discovery/openrouter.d.ts +78 -0
  112. package/dist/discovery/openrouter.d.ts.map +1 -0
  113. package/dist/discovery/openrouter.js +184 -0
  114. package/dist/discovery/openrouter.js.map +1 -0
  115. package/dist/discovery/perplexity.d.ts +66 -0
  116. package/dist/discovery/perplexity.d.ts.map +1 -0
  117. package/dist/discovery/perplexity.js +86 -0
  118. package/dist/discovery/perplexity.js.map +1 -0
  119. package/dist/discovery/together.d.ts +41 -0
  120. package/dist/discovery/together.d.ts.map +1 -0
  121. package/dist/discovery/together.js +88 -0
  122. package/dist/discovery/together.js.map +1 -0
  123. package/dist/discovery/vertex.d.ts +146 -0
  124. package/dist/discovery/vertex.d.ts.map +1 -0
  125. package/dist/discovery/vertex.js +371 -0
  126. package/dist/discovery/vertex.js.map +1 -0
  127. package/dist/discovery/zai.d.ts +17 -0
  128. package/dist/discovery/zai.d.ts.map +1 -0
  129. package/dist/discovery/zai.js +51 -0
  130. package/dist/discovery/zai.js.map +1 -0
  131. package/dist/discovery-contract.d.ts +186 -0
  132. package/dist/discovery-contract.d.ts.map +1 -0
  133. package/dist/discovery-contract.js +115 -0
  134. package/dist/discovery-contract.js.map +1 -0
  135. package/dist/discovery-routes.d.ts +14 -0
  136. package/dist/discovery-routes.d.ts.map +1 -0
  137. package/dist/discovery-routes.js +98 -0
  138. package/dist/discovery-routes.js.map +1 -0
  139. package/dist/enrichment/index.d.ts +2 -0
  140. package/dist/enrichment/index.d.ts.map +1 -0
  141. package/dist/enrichment/index.js +2 -0
  142. package/dist/enrichment/index.js.map +1 -0
  143. package/dist/enrichment/litellm.d.ts +52 -0
  144. package/dist/enrichment/litellm.d.ts.map +1 -0
  145. package/dist/enrichment/litellm.js +205 -0
  146. package/dist/enrichment/litellm.js.map +1 -0
  147. package/dist/index.d.ts +30 -0
  148. package/dist/index.d.ts.map +1 -0
  149. package/dist/index.js +33 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/normalize.d.ts +64 -0
  152. package/dist/normalize.d.ts.map +1 -0
  153. package/dist/normalize.js +166 -0
  154. package/dist/normalize.js.map +1 -0
  155. package/dist/provider-catalog.d.ts +62 -0
  156. package/dist/provider-catalog.d.ts.map +1 -0
  157. package/dist/provider-catalog.js +331 -0
  158. package/dist/provider-catalog.js.map +1 -0
  159. package/dist/registry-delta.d.ts +17 -0
  160. package/dist/registry-delta.d.ts.map +1 -0
  161. package/dist/registry-delta.js +74 -0
  162. package/dist/registry-delta.js.map +1 -0
  163. package/dist/registry-discovery.d.ts +66 -0
  164. package/dist/registry-discovery.d.ts.map +1 -0
  165. package/dist/registry-discovery.js +339 -0
  166. package/dist/registry-discovery.js.map +1 -0
  167. package/dist/registry-query.d.ts +74 -0
  168. package/dist/registry-query.d.ts.map +1 -0
  169. package/dist/registry-query.js +310 -0
  170. package/dist/registry-query.js.map +1 -0
  171. package/dist/registry-runtime.d.ts +48 -0
  172. package/dist/registry-runtime.d.ts.map +1 -0
  173. package/dist/registry-runtime.js +255 -0
  174. package/dist/registry-runtime.js.map +1 -0
  175. package/dist/registry-selection.d.ts +31 -0
  176. package/dist/registry-selection.d.ts.map +1 -0
  177. package/dist/registry-selection.js +167 -0
  178. package/dist/registry-selection.js.map +1 -0
  179. package/dist/registry-state.d.ts +92 -0
  180. package/dist/registry-state.d.ts.map +1 -0
  181. package/dist/registry-state.js +35 -0
  182. package/dist/registry-state.js.map +1 -0
  183. package/dist/registry.d.ts +127 -0
  184. package/dist/registry.d.ts.map +1 -0
  185. package/dist/registry.js +248 -0
  186. package/dist/registry.js.map +1 -0
  187. package/dist/resilience.d.ts +212 -0
  188. package/dist/resilience.d.ts.map +1 -0
  189. package/dist/resilience.js +292 -0
  190. package/dist/resilience.js.map +1 -0
  191. package/dist/security.d.ts +44 -0
  192. package/dist/security.d.ts.map +1 -0
  193. package/dist/security.js +184 -0
  194. package/dist/security.js.map +1 -0
  195. package/dist/server.d.ts +41 -0
  196. package/dist/server.d.ts.map +1 -0
  197. package/dist/server.js +320 -0
  198. package/dist/server.js.map +1 -0
  199. package/dist/shell-safe.d.ts +15 -0
  200. package/dist/shell-safe.d.ts.map +1 -0
  201. package/dist/shell-safe.js +20 -0
  202. package/dist/shell-safe.js.map +1 -0
  203. package/dist/types.d.ts +365 -0
  204. package/dist/types.d.ts.map +1 -0
  205. package/dist/types.js +10 -0
  206. package/dist/types.js.map +1 -0
  207. package/logo.svg +81 -0
  208. package/package.json +85 -0
@@ -0,0 +1,248 @@
1
+ /**
2
+ * kosha-discovery — Thin registry façade.
3
+ *
4
+ * I keep `ModelRegistry` as the stable public API while the heavy lifting
5
+ * lives in focused helper modules that stay under the file-size policy.
6
+ * @module
7
+ */
8
+ import { readFile } from "fs/promises";
9
+ import { homedir } from "os";
10
+ import { join } from "path";
11
+ import { normalizeProviderId } from "./provider-catalog.js";
12
+ import { registryBuildSnapshot, registryClassifyError, registryDiscoveryDelta, registryDiscoverySnapshot, registryMakeCursor, registryRecordDiscoveryMutation, registryRecordObservation, registrySnapshotForDelta, registryWatchDiscovery, } from "./registry-discovery.js";
13
+ import { modelRoles, modelSupportsRole, normalizeRoleToken, registryCapabilities, registryCheapestModels, registryMissingCredentialPrompts, registryModelRouteInfo, registryModelRoutes, registryModels, registryProviderRoles, } from "./registry-query.js";
14
+ import { registryCheapestCandidates, registryExecutionBindingHints, } from "./registry-selection.js";
15
+ import { createRegistryState } from "./registry-state.js";
16
+ import { enrichRegistryModels, fallbackRegistryCredential, getRegistryCredentialResolver, loadRegistryDiscoverers, loadRegistryFromCache, populateRegistryModelAliases, registryDiscover, registryRefresh, saveRegistryToCache, } from "./registry-runtime.js";
17
+ /**
18
+ * Public registry API for provider discovery and routing-oriented queries.
19
+ */
20
+ export class ModelRegistry {
21
+ state;
22
+ constructor(config) {
23
+ this.state = createRegistryState(config);
24
+ this.currentCursor = registryMakeCursor(this.state);
25
+ }
26
+ /** Compatibility accessor retained for existing tests and debug hooks. */
27
+ get providerMap() { return this.state.providerMap; }
28
+ /** Compatibility accessor retained for existing tests and debug hooks. */
29
+ get aliasResolver() { return this.state.aliasResolver; }
30
+ /** Compatibility accessor retained for existing tests and debug hooks. */
31
+ get discoveredAt() { return this.state.discoveredAt; }
32
+ set discoveredAt(value) { this.state.discoveredAt = value; }
33
+ /** Compatibility accessor retained for existing tests and debug hooks. */
34
+ get healthTracker() { return this.state.healthTracker; }
35
+ get currentCursor() { return this.state.currentCursor; }
36
+ set currentCursor(value) { this.state.currentCursor = value; }
37
+ get lastSnapshotCache() { return this.state.lastSnapshotCache; }
38
+ set lastSnapshotCache(value) { this.state.lastSnapshotCache = value; }
39
+ /**
40
+ * Run discovery across all or selected providers.
41
+ */
42
+ async discover(options) {
43
+ const credentialResolver = await getRegistryCredentialResolver();
44
+ return registryDiscover(this.state, this.dependencies(credentialResolver), options);
45
+ }
46
+ /**
47
+ * Force a fresh discovery pass, bypassing cache for the targeted scope.
48
+ */
49
+ async refresh(providerId) {
50
+ await registryRefresh(this.state, (options) => this.discover(options), providerId);
51
+ }
52
+ /**
53
+ * Force a live discovery fetch and return a summary payload.
54
+ *
55
+ * This always bypasses cache, so callers can ask for "latest now"
56
+ * without relying on TTL expiry.
57
+ */
58
+ async fetchLatestDetails(options) {
59
+ const providers = await this.discover({ ...options, force: true });
60
+ return {
61
+ providers,
62
+ modelCount: providers.reduce((sum, provider) => sum + provider.models.length, 0),
63
+ discoveredAt: this.discoveredAt,
64
+ };
65
+ }
66
+ /** Return all known models with optional provider/origin/mode filters. */
67
+ models(filter) {
68
+ return registryModels(this.state, filter);
69
+ }
70
+ /** Return the provider -> model -> roles matrix used by routing clients. */
71
+ providerRoles(filter) {
72
+ return registryProviderRoles(this.state, filter);
73
+ }
74
+ /** Return prompts for discovered providers missing required credentials. */
75
+ missingCredentialPrompts(providerIds) {
76
+ return registryMissingCredentialPrompts(this.state, providerIds);
77
+ }
78
+ /** Return the cheapest ranked legacy candidates for the requested query. */
79
+ cheapestModels(options) {
80
+ return registryCheapestModels(this.state, options);
81
+ }
82
+ /** Return every provider route for a normalized model identifier. */
83
+ modelRoutes(modelId) {
84
+ return registryModelRoutes(this.state, modelId);
85
+ }
86
+ /** Return enriched route metadata for a normalized model identifier. */
87
+ modelRouteInfo(modelId) {
88
+ return registryModelRouteInfo(this.state, modelId);
89
+ }
90
+ /** Resolve a model by canonical ID or configured alias. */
91
+ model(idOrAlias) {
92
+ const resolvedId = this.aliasResolver.resolve(idOrAlias);
93
+ return registryModels(this.state).find((model) => model.id === resolvedId);
94
+ }
95
+ /** Return a single provider by canonical or alias provider ID. */
96
+ provider(id) {
97
+ const normalizedProviderId = normalizeProviderId(id) ?? id;
98
+ return this.providerMap.get(normalizedProviderId);
99
+ }
100
+ /** Return all currently known providers. */
101
+ providers_list() {
102
+ return Array.from(this.providerMap.values());
103
+ }
104
+ /** Return errors captured during the most recent discovery pass. */
105
+ discoveryErrors() {
106
+ return [...this.state.lastDiscoveryErrors];
107
+ }
108
+ /** Return raw circuit-breaker health details for monitoring/debugging. */
109
+ providerHealth() {
110
+ return this.healthTracker.healthReport();
111
+ }
112
+ /** Build the stable v1 discovery snapshot for daemon consumers. */
113
+ discoverySnapshot() {
114
+ return registryDiscoverySnapshot(this.state);
115
+ }
116
+ /** Return delta batches since the provided cursor. */
117
+ discoveryDelta(options) {
118
+ return registryDiscoveryDelta(this.state, options);
119
+ }
120
+ /** Stream live discovery deltas through an async iterator. */
121
+ watchDiscovery(options) {
122
+ return registryWatchDiscovery(this.state, options);
123
+ }
124
+ /** Return cheapest candidates using the trusted v1 capability taxonomy. */
125
+ cheapestCandidates(query = {}) {
126
+ return registryCheapestCandidates(this.state, query);
127
+ }
128
+ /** Return query-scoped binding hints without taking routing authority. */
129
+ executionBindingHints(query = {}) {
130
+ return registryExecutionBindingHints(this.state, query);
131
+ }
132
+ /** Reset one provider breaker or the full health tracker. */
133
+ resetHealth(providerId) {
134
+ if (providerId) {
135
+ this.healthTracker.breaker(normalizeProviderId(providerId) ?? providerId).reset();
136
+ return;
137
+ }
138
+ this.healthTracker.resetAll();
139
+ }
140
+ /** Aggregate capability statistics across the current model set. */
141
+ capabilities(filter) {
142
+ return registryCapabilities(this.state, filter);
143
+ }
144
+ /** Normalize a role or capability token used by legacy queries. */
145
+ normalizeRoleToken(value) {
146
+ return normalizeRoleToken(value);
147
+ }
148
+ /** Return the deduplicated role list for a model. */
149
+ modelRoles(model) {
150
+ return modelRoles(model);
151
+ }
152
+ /** Check whether a model satisfies a role or capability query. */
153
+ modelSupportsRole(model, roleOrCapability) {
154
+ return modelSupportsRole(model, roleOrCapability);
155
+ }
156
+ /** Resolve a configured alias to its canonical model ID. */
157
+ resolve(alias) {
158
+ return this.aliasResolver.resolve(alias);
159
+ }
160
+ /** Add a custom alias mapping. */
161
+ alias(short, modelId) {
162
+ this.aliasResolver.addAlias(short, modelId);
163
+ }
164
+ /** Serialize the registry into a plain JSON-compatible snapshot. */
165
+ toJSON() {
166
+ return {
167
+ providers: this.providers_list(),
168
+ aliases: this.aliasResolver.all(),
169
+ discoveredAt: this.discoveredAt,
170
+ };
171
+ }
172
+ /** Restore a registry instance from a serialized JSON payload. */
173
+ static fromJSON(data) {
174
+ const registry = new ModelRegistry({ aliases: data.aliases });
175
+ for (const provider of data.providers) {
176
+ registry.providerMap.set(provider.id, provider);
177
+ }
178
+ registry.discoveredAt = data.discoveredAt;
179
+ registry.currentCursor = registryMakeCursor(registry.state);
180
+ registry.lastSnapshotCache = registryBuildSnapshot(registry.state, registry.currentCursor);
181
+ return registry;
182
+ }
183
+ /** Load config from global/project files and merge explicit overrides last. */
184
+ static async loadConfigFile(overrides) {
185
+ const layers = [];
186
+ const globalConfig = await ModelRegistry.readJsonFile(join(homedir(), ".kosharc.json"));
187
+ const projectConfig = await ModelRegistry.readJsonFile(join(process.cwd(), "kosha.config.json"));
188
+ if (globalConfig)
189
+ layers.push(globalConfig);
190
+ if (projectConfig)
191
+ layers.push(projectConfig);
192
+ if (overrides)
193
+ layers.push(overrides);
194
+ if (layers.length === 0)
195
+ return {};
196
+ // I only deep-merge the provider and alias maps; the rest stays shallow.
197
+ return layers.reduce((merged, layer) => ({
198
+ ...merged,
199
+ ...layer,
200
+ providers: { ...merged.providers, ...layer.providers },
201
+ aliases: { ...merged.aliases, ...layer.aliases },
202
+ }), {});
203
+ }
204
+ dependencies(credentialResolver) {
205
+ return {
206
+ // I bind the loaded resolver once per discovery pass to avoid repeated dynamic imports.
207
+ resolveCredential: credentialResolver ? (providerId, explicitKey) => credentialResolver.resolve(providerId, explicitKey) : null,
208
+ loadDiscoverers: (providerIds, includeLocal) => loadRegistryDiscoverers(this.state, providerIds, includeLocal),
209
+ enrichModels: () => enrichRegistryModels(this.state),
210
+ populateModelAliases: () => populateRegistryModelAliases(this.state),
211
+ loadFromCache: (providerIds) => loadRegistryFromCache(this.state, providerIds),
212
+ saveToCache: () => saveRegistryToCache(this.state),
213
+ fallbackCredential: fallbackRegistryCredential,
214
+ snapshotForDelta: () => registrySnapshotForDelta(this.state),
215
+ recordDiscoveryMutation: (previousSnapshot) => registryRecordDiscoveryMutation(this.state, previousSnapshot),
216
+ recordObservation: (providerId, entry) => registryRecordObservation(this.state, providerId, entry),
217
+ classifyError: (errorMessage) => registryClassifyError(errorMessage),
218
+ };
219
+ }
220
+ /** Compatibility wrapper retained for tests that probe internal mutation APIs. */
221
+ snapshotForDelta() {
222
+ return registrySnapshotForDelta(this.state);
223
+ }
224
+ /** Compatibility wrapper retained for tests that probe internal mutation APIs. */
225
+ recordDiscoveryMutation(previousSnapshot) {
226
+ return registryRecordDiscoveryMutation(this.state, previousSnapshot);
227
+ }
228
+ /** Compatibility wrapper retained for tests that probe internal mutation APIs. */
229
+ recordObservation(providerId, entry) {
230
+ return registryRecordObservation(this.state, providerId, entry);
231
+ }
232
+ /** Compatibility wrapper retained for internal error classification hooks. */
233
+ classifyError(errorMessage) {
234
+ return registryClassifyError(errorMessage);
235
+ }
236
+ static async readJsonFile(filePath) {
237
+ try {
238
+ return JSON.parse(await readFile(filePath, "utf-8"));
239
+ }
240
+ catch (error) {
241
+ if (error instanceof SyntaxError) {
242
+ console.warn(`kosha: config file has invalid JSON: ${filePath}`);
243
+ }
244
+ return null;
245
+ }
246
+ }
247
+ }
248
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * kosha-discovery — Resilience primitives.
3
+ *
4
+ * Provides a {@link CircuitBreaker} for per-provider fault isolation,
5
+ * a {@link HealthTracker} that manages breakers for all known providers,
6
+ * and a {@link StaleCachePolicy} that implements stale-while-revalidate
7
+ * semantics on top of {@link KoshaCache}.
8
+ *
9
+ * The module is self-contained with no side-effects on import.
10
+ * @module
11
+ */
12
+ import type { KoshaCache } from "./cache.js";
13
+ /** Possible states of a {@link CircuitBreaker}. */
14
+ export type CircuitState = "closed" | "open" | "half-open";
15
+ /** Configuration options for a {@link CircuitBreaker} instance. */
16
+ export interface CircuitBreakerOptions {
17
+ /**
18
+ * Number of consecutive failures before the circuit opens.
19
+ * @default 3
20
+ */
21
+ failureThreshold?: number;
22
+ /**
23
+ * Time (in milliseconds) the circuit stays open before transitioning
24
+ * to `half-open` to allow a probe request.
25
+ * @default 60_000
26
+ */
27
+ resetTimeoutMs?: number;
28
+ /**
29
+ * Number of consecutive successes in `half-open` state required to
30
+ * close the circuit again.
31
+ * @default 1
32
+ */
33
+ halfOpenSuccessThreshold?: number;
34
+ }
35
+ /**
36
+ * A point-in-time health snapshot for a single provider circuit.
37
+ */
38
+ export interface ProviderHealth {
39
+ /** Provider slug this health record belongs to. */
40
+ providerId: string;
41
+ /** Current circuit state. */
42
+ state: CircuitState;
43
+ /** Number of consecutive failures recorded since last reset. */
44
+ failureCount: number;
45
+ /** Unix timestamp (ms) of the most recent recorded failure, or 0. */
46
+ lastFailureTime: number;
47
+ /** Error message from the most recent failure, if available. */
48
+ lastError?: string;
49
+ /** Unix timestamp (ms) of the most recent recorded success, or 0. */
50
+ lastSuccessTime: number;
51
+ }
52
+ /**
53
+ * Wrapper returned by {@link StaleCachePolicy.getWithStale}.
54
+ */
55
+ export interface StaleResult<T> {
56
+ /** The cached payload. */
57
+ data: T;
58
+ /** True when the cache entry has exceeded its TTL. */
59
+ stale: boolean;
60
+ /** Milliseconds elapsed since the entry was written. */
61
+ age: number;
62
+ /** Unix timestamp (ms) when the entry was originally cached. */
63
+ cachedAt: number;
64
+ }
65
+ /**
66
+ * Per-provider circuit breaker with three states.
67
+ *
68
+ * - **closed** — Normal operation. All requests are allowed. Consecutive
69
+ * failures are counted; once the {@link CircuitBreakerOptions.failureThreshold}
70
+ * is reached the circuit transitions to `open`.
71
+ * - **open** — Requests are rejected immediately (canExecute returns false).
72
+ * After {@link CircuitBreakerOptions.resetTimeoutMs} has elapsed the circuit
73
+ * transitions to `half-open` so a single probe request can be attempted.
74
+ * - **half-open** — Exactly one request is let through. A success closes the
75
+ * circuit; a failure re-opens it and resets the timeout.
76
+ *
77
+ * @example
78
+ * const cb = new CircuitBreaker("anthropic", { failureThreshold: 5 });
79
+ * if (cb.canExecute()) {
80
+ * try {
81
+ * const result = await callApi();
82
+ * cb.onSuccess();
83
+ * } catch (err) {
84
+ * cb.onFailure(err.message);
85
+ * }
86
+ * }
87
+ */
88
+ export declare class CircuitBreaker {
89
+ readonly providerId: string;
90
+ private options;
91
+ private state;
92
+ private failureCount;
93
+ private successCount;
94
+ private lastFailureTime;
95
+ private lastSuccessTime;
96
+ private lastError?;
97
+ /** Resolved threshold: consecutive failures before opening. */
98
+ private readonly failureThreshold;
99
+ /** Resolved open-state duration before allowing a probe. */
100
+ private readonly resetTimeoutMs;
101
+ /** Resolved success count in half-open needed to close. */
102
+ private readonly halfOpenSuccessThreshold;
103
+ constructor(providerId: string, options?: CircuitBreakerOptions);
104
+ /**
105
+ * Check whether a request should be allowed through.
106
+ *
107
+ * - `closed` → always true.
108
+ * - `open` → false, unless the reset timeout has elapsed, in which case
109
+ * the circuit transitions to `half-open` and returns true for the probe.
110
+ * - `half-open` → true (the probe request is already in flight).
111
+ */
112
+ canExecute(): boolean;
113
+ /**
114
+ * Record a successful API call.
115
+ *
116
+ * In `half-open` state, once enough successes accumulate (per
117
+ * {@link CircuitBreakerOptions.halfOpenSuccessThreshold}) the circuit closes.
118
+ * In `closed` state the failure counter is reset.
119
+ */
120
+ onSuccess(): void;
121
+ /**
122
+ * Record a failed API call.
123
+ *
124
+ * In `closed` state, increments the failure counter and opens the circuit
125
+ * when the threshold is reached. In `half-open` state, immediately
126
+ * re-opens the circuit.
127
+ *
128
+ * @param error - Optional error message to store for diagnostics.
129
+ */
130
+ onFailure(error?: string): void;
131
+ /**
132
+ * Return a point-in-time health snapshot for this provider's circuit.
133
+ */
134
+ health(): ProviderHealth;
135
+ /**
136
+ * Force the circuit back to `closed` state, resetting all counters.
137
+ * Useful for manual recovery or test teardown.
138
+ */
139
+ reset(): void;
140
+ private transitionToOpen;
141
+ private transitionToClosed;
142
+ }
143
+ /**
144
+ * Manages {@link CircuitBreaker} instances for all tracked providers.
145
+ *
146
+ * Breakers are created lazily on first access via {@link breaker}.
147
+ *
148
+ * @example
149
+ * const tracker = new HealthTracker();
150
+ * const cb = tracker.breaker("anthropic");
151
+ * if (cb.canExecute()) { ... }
152
+ */
153
+ export declare class HealthTracker {
154
+ private breakers;
155
+ /**
156
+ * Retrieve the {@link CircuitBreaker} for the given provider, creating
157
+ * one with default options if it does not yet exist.
158
+ *
159
+ * @param providerId - Provider slug (e.g. `"anthropic"`).
160
+ * @param options - Options forwarded to a newly created breaker only.
161
+ */
162
+ breaker(providerId: string, options?: CircuitBreakerOptions): CircuitBreaker;
163
+ /**
164
+ * Return health snapshots for every tracked provider, sorted by provider ID.
165
+ */
166
+ healthReport(): ProviderHealth[];
167
+ /**
168
+ * Return provider IDs whose circuit is `closed` or `half-open`
169
+ * (i.e. requests are currently being allowed through).
170
+ */
171
+ availableProviders(): string[];
172
+ /**
173
+ * Return provider IDs whose circuit is `open`
174
+ * (i.e. requests are currently being rejected).
175
+ */
176
+ downProviders(): string[];
177
+ /**
178
+ * Reset all tracked circuit breakers to `closed` state.
179
+ */
180
+ resetAll(): void;
181
+ }
182
+ /**
183
+ * Utility that wraps {@link KoshaCache} reads to implement
184
+ * stale-while-revalidate semantics.
185
+ *
186
+ * Unlike the registry's normal cache path (which returns `null` for expired
187
+ * entries), this policy always returns whatever was cached along with a
188
+ * `stale` flag. This lets callers serve the old data immediately while
189
+ * triggering a background refresh.
190
+ *
191
+ * Returns `null` only when the cache has never held a value for the key.
192
+ *
193
+ * @example
194
+ * const result = await StaleCachePolicy.getWithStale<ModelCard[]>(cache, "provider_anthropic");
195
+ * if (result) {
196
+ * serveToClient(result.data); // always fast
197
+ * if (result.stale) triggerBackgroundRefresh();
198
+ * }
199
+ */
200
+ export declare class StaleCachePolicy {
201
+ /**
202
+ * Fetch a cached value regardless of its expiry, annotating the result
203
+ * with staleness metadata.
204
+ *
205
+ * @param cache - The {@link KoshaCache} instance to read from.
206
+ * @param key - Cache key to look up.
207
+ * @returns A {@link StaleResult} when any cached value exists, or `null`
208
+ * when the key has never been written.
209
+ */
210
+ static getWithStale<T>(cache: KoshaCache, key: string): Promise<StaleResult<T> | null>;
211
+ }
212
+ //# sourceMappingURL=resilience.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resilience.d.ts","sourceRoot":"","sources":["../src/resilience.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,mDAAmD;AACnD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,mEAAmE;AACnE,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,KAAK,EAAE,YAAY,CAAC;IACpB,gEAAgE;IAChE,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,eAAe,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,eAAe,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC7B,0BAA0B;IAC1B,IAAI,EAAE,CAAC,CAAC;IACR,sDAAsD;IACtD,KAAK,EAAE,OAAO,CAAC;IACf,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,cAAc;IAed,QAAQ,CAAC,UAAU,EAAE,MAAM;IAAE,OAAO,CAAC,OAAO;IAdxD,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,SAAS,CAAC,CAAS;IAE3B,+DAA+D;IAC/D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,2DAA2D;IAC3D,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAS;gBAE7B,UAAU,EAAE,MAAM,EAAU,OAAO,GAAE,qBAA0B;IAMpF;;;;;;;OAOG;IACH,UAAU,IAAI,OAAO;IAoBrB;;;;;;OAMG;IACH,SAAS,IAAI,IAAI;IAiBjB;;;;;;;;OAQG;IACH,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAkB/B;;OAEG;IACH,MAAM,IAAI,cAAc;IAWxB;;;OAGG;IACH,KAAK,IAAI,IAAI;IAWb,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,kBAAkB;CAK1B;AAMD;;;;;;;;;GASG;AACH,qBAAa,aAAa;IACzB,OAAO,CAAC,QAAQ,CAAqC;IAErD;;;;;;OAMG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,cAAc;IAS5E;;OAEG;IACH,YAAY,IAAI,cAAc,EAAE;IAMhC;;;OAGG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAO9B;;;OAGG;IACH,aAAa,IAAI,MAAM,EAAE;IAOzB;;OAEG;IACH,QAAQ,IAAI,IAAI;CAKhB;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,gBAAgB;IAC5B;;;;;;;;OAQG;WACU,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CA8B5F"}