-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathAIProviderRegistry.ts
More file actions
116 lines (91 loc) · 2.72 KB
/
AIProviderRegistry.ts
File metadata and controls
116 lines (91 loc) · 2.72 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
115
116
// src/api/ai/core/AIProviderRegistry.ts
import { AIProvider } from './AIProvider'
import { AICapability } from './types'
type ProviderHealth = {
failures: number
lastFailureAt?: number
}
const MAX_FAILURES = 3
const COOLDOWN_PERIOD_MS = 300000 // 5 minutes
class AIProviderRegistry {
private providers = new Map<string, AIProvider>()
private health = new Map<string, ProviderHealth>()
// --------------------
// Provider management
// --------------------
register(provider: AIProvider) {
if (this.providers.has(provider.name)) {
throw new Error(
`A provider with the name "${provider.name}" is already registered.`
)
}
this.providers.set(provider.name, provider)
this.health.set(provider.name, { failures: 0 })
}
get(name: string): AIProvider | undefined {
return this.providers.get(name)
}
list(): AIProvider[] {
return Array.from(this.providers.values())
}
findByCapabilities(capabilities: AICapability[]): AIProvider[] {
return Array.from(this.providers.values()).filter(provider =>
capabilities.every(cap => provider.supports(cap))
)
}
// --------------------
// Health tracking with cooldown recovery
// --------------------
private isHealthy(providerName: string): boolean {
const info = this.health.get(providerName)
if (!info) return true
if (info.failures < MAX_FAILURES) {
return true
}
// Allow retry after cooldown period
if (info.lastFailureAt && Date.now() - info.lastFailureAt > COOLDOWN_PERIOD_MS) {
this.markSuccess(providerName) // Reset health to allow retry
return true
}
return false
}
private markFailure(providerName: string) {
const info = this.health.get(providerName)
if (!info) return
info.failures += 1
info.lastFailureAt = Date.now()
}
private markSuccess(providerName: string) {
const info = this.health.get(providerName)
if (!info) return
info.failures = 0
}
// --------------------
// Provider + Model routing
// --------------------
async generateTextWithModel(
providerName: string,
modelId: string,
input: { prompt: string }
): Promise<string> {
const provider = this.get(providerName)
if (!provider) {
throw new Error(`Provider "${providerName}" not found`)
}
if (!this.isHealthy(provider.name)) {
throw new Error(`Provider "${providerName}" is currently degraded`)
}
try {
const result = await provider.generateText({
...input,
context: modelId,
})
this.markSuccess(provider.name)
return result
} catch (err) {
this.markFailure(provider.name)
throw err
}
}
}
export const aiProviderRegistry = new AIProviderRegistry()