@entro314labs/ai-changelog-generator 3.1.1 → 3.2.1
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.
- package/CHANGELOG.md +412 -875
- package/README.md +8 -3
- package/ai-changelog-mcp.sh +0 -0
- package/ai-changelog.sh +0 -0
- package/bin/ai-changelog-dxt.js +9 -9
- package/bin/ai-changelog-mcp.js +19 -17
- package/bin/ai-changelog.js +6 -6
- package/package.json +80 -48
- package/src/ai-changelog-generator.js +91 -81
- package/src/application/orchestrators/changelog.orchestrator.js +791 -516
- package/src/application/services/application.service.js +137 -128
- package/src/cli.js +76 -57
- package/src/domains/ai/ai-analysis.service.js +289 -209
- package/src/domains/analysis/analysis.engine.js +328 -192
- package/src/domains/changelog/changelog.service.js +1174 -783
- package/src/domains/changelog/workspace-changelog.service.js +487 -249
- package/src/domains/git/git-repository.analyzer.js +348 -258
- package/src/domains/git/git.service.js +132 -112
- package/src/infrastructure/cli/cli.controller.js +390 -274
- package/src/infrastructure/config/configuration.manager.js +220 -190
- package/src/infrastructure/interactive/interactive-staging.service.js +154 -135
- package/src/infrastructure/interactive/interactive-workflow.service.js +200 -159
- package/src/infrastructure/mcp/mcp-server.service.js +208 -207
- package/src/infrastructure/metrics/metrics.collector.js +140 -123
- package/src/infrastructure/providers/core/base-provider.js +87 -40
- package/src/infrastructure/providers/implementations/anthropic.js +101 -99
- package/src/infrastructure/providers/implementations/azure.js +124 -101
- package/src/infrastructure/providers/implementations/bedrock.js +136 -126
- package/src/infrastructure/providers/implementations/dummy.js +23 -23
- package/src/infrastructure/providers/implementations/google.js +123 -114
- package/src/infrastructure/providers/implementations/huggingface.js +94 -87
- package/src/infrastructure/providers/implementations/lmstudio.js +75 -60
- package/src/infrastructure/providers/implementations/mock.js +69 -73
- package/src/infrastructure/providers/implementations/ollama.js +89 -66
- package/src/infrastructure/providers/implementations/openai.js +88 -89
- package/src/infrastructure/providers/implementations/vertex.js +227 -197
- package/src/infrastructure/providers/provider-management.service.js +245 -207
- package/src/infrastructure/providers/provider-manager.service.js +145 -125
- package/src/infrastructure/providers/utils/base-provider-helpers.js +308 -302
- package/src/infrastructure/providers/utils/model-config.js +220 -195
- package/src/infrastructure/providers/utils/provider-utils.js +105 -100
- package/src/infrastructure/validation/commit-message-validation.service.js +259 -161
- package/src/shared/constants/colors.js +453 -180
- package/src/shared/utils/cli-demo.js +285 -0
- package/src/shared/utils/cli-entry-utils.js +257 -249
- package/src/shared/utils/cli-ui.js +447 -0
- package/src/shared/utils/diff-processor.js +513 -0
- package/src/shared/utils/error-classes.js +125 -156
- package/src/shared/utils/json-utils.js +93 -89
- package/src/shared/utils/utils.js +1117 -945
- package/types/index.d.ts +353 -344
|
@@ -3,64 +3,65 @@
|
|
|
3
3
|
* Reduces code duplication by providing common implementations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
selectModelByComplexity,
|
|
8
|
-
standardConnectionTest,
|
|
9
|
-
createProviderErrorResponse,
|
|
10
|
-
createProviderSuccessResponse,
|
|
11
|
-
validateModelWithFallbacks,
|
|
12
|
-
extractProviderConfig,
|
|
13
|
-
buildClientOptions
|
|
14
|
-
} from './provider-utils.js';
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
MODEL_CONFIGS,
|
|
18
|
-
getProviderModelConfig,
|
|
19
|
-
getModelCapabilities,
|
|
20
|
-
getSuggestedModels,
|
|
21
|
-
normalizeModelName,
|
|
6
|
+
import {
|
|
22
7
|
analyzeCommitComplexity,
|
|
23
8
|
getBestModelForCapabilities,
|
|
24
|
-
|
|
25
|
-
|
|
9
|
+
getModelCapabilities,
|
|
10
|
+
getProviderModelConfig,
|
|
11
|
+
getSuggestedModels,
|
|
12
|
+
MODEL_CONFIGS,
|
|
13
|
+
normalizeModelName,
|
|
14
|
+
} from './model-config.js'
|
|
15
|
+
import {
|
|
16
|
+
buildClientOptions,
|
|
17
|
+
createProviderErrorResponse,
|
|
18
|
+
createProviderSuccessResponse,
|
|
19
|
+
extractProviderConfig,
|
|
20
|
+
selectModelByComplexity,
|
|
21
|
+
standardConnectionTest,
|
|
22
|
+
validateModelWithFallbacks,
|
|
23
|
+
} from './provider-utils.js'
|
|
26
24
|
|
|
27
25
|
// Provider utility functions
|
|
28
26
|
function isHubProvider(providerName) {
|
|
29
|
-
const config = MODEL_CONFIGS[providerName]
|
|
30
|
-
return config && config.isHub === true
|
|
27
|
+
const config = MODEL_CONFIGS[providerName]
|
|
28
|
+
return config && config.isHub === true
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
function selectHubModel(providerName, complexity = 'standard', availableModels = []) {
|
|
34
|
-
const modelConfig = getProviderModelConfig(providerName)
|
|
35
|
-
if (!modelConfig)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
const modelConfig = getProviderModelConfig(providerName)
|
|
33
|
+
if (!modelConfig) {
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const modelKey =
|
|
38
|
+
{
|
|
39
|
+
simple: 'smallModel',
|
|
40
|
+
standard: 'standardModel',
|
|
41
|
+
medium: 'mediumModel',
|
|
42
|
+
complex: 'complexModel',
|
|
43
|
+
}[complexity] || 'standardModel'
|
|
44
|
+
|
|
45
|
+
const preferredModel = modelConfig[modelKey]
|
|
46
|
+
|
|
46
47
|
// If we have available models list, check if preferred model exists
|
|
47
48
|
if (Array.isArray(availableModels) && availableModels.length > 0) {
|
|
48
49
|
if (availableModels.includes(preferredModel)) {
|
|
49
|
-
return preferredModel
|
|
50
|
+
return preferredModel
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
+
|
|
52
53
|
// Try fallbacks
|
|
53
54
|
for (const fallback of modelConfig.fallbacks || []) {
|
|
54
55
|
if (availableModels.includes(fallback)) {
|
|
55
|
-
return fallback
|
|
56
|
+
return fallback
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
|
-
|
|
59
|
+
|
|
59
60
|
// Return first available model if nothing else matches
|
|
60
|
-
return availableModels[0]
|
|
61
|
+
return availableModels[0]
|
|
61
62
|
}
|
|
62
|
-
|
|
63
|
-
return preferredModel
|
|
63
|
+
|
|
64
|
+
return preferredModel
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
/**
|
|
@@ -72,50 +73,59 @@ export function ModelRecommendationMixin(providerName) {
|
|
|
72
73
|
return {
|
|
73
74
|
getModelRecommendation(commitDetails) {
|
|
74
75
|
// Get available models for hub providers
|
|
75
|
-
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
76
|
-
const modelConfig = getProviderModelConfig(providerName, this.config, availableModels)
|
|
77
|
-
|
|
76
|
+
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
77
|
+
const modelConfig = getProviderModelConfig(providerName, this.config, availableModels)
|
|
78
|
+
|
|
78
79
|
// Use enhanced complexity analysis
|
|
79
|
-
const complexityAnalysis = analyzeCommitComplexity(commitDetails, providerName)
|
|
80
|
-
|
|
80
|
+
const complexityAnalysis = analyzeCommitComplexity(commitDetails, providerName)
|
|
81
|
+
|
|
81
82
|
// For hub providers, use specialized selection logic
|
|
82
83
|
if (isHubProvider(providerName)) {
|
|
83
|
-
const selectedModel = selectHubModel(
|
|
84
|
+
const selectedModel = selectHubModel(
|
|
85
|
+
providerName,
|
|
86
|
+
complexityAnalysis.complexity,
|
|
87
|
+
availableModels
|
|
88
|
+
)
|
|
84
89
|
return {
|
|
85
90
|
model: selectedModel,
|
|
86
91
|
complexity: complexityAnalysis.complexity,
|
|
87
92
|
reasoning: complexityAnalysis.reasoning,
|
|
88
93
|
isHubProvider: true,
|
|
89
|
-
availableModels: availableModels.length
|
|
90
|
-
}
|
|
94
|
+
availableModels: availableModels.length,
|
|
95
|
+
}
|
|
91
96
|
}
|
|
92
|
-
|
|
97
|
+
|
|
93
98
|
// Standard provider logic
|
|
94
|
-
return selectModelByComplexity(commitDetails, modelConfig)
|
|
99
|
+
return selectModelByComplexity(commitDetails, modelConfig)
|
|
95
100
|
},
|
|
96
|
-
|
|
101
|
+
|
|
97
102
|
async selectOptimalModel(commitDetails) {
|
|
98
103
|
// Enhanced async version with hub awareness
|
|
99
104
|
if (isHubProvider(providerName)) {
|
|
100
105
|
// Try to refresh available models for hub providers
|
|
101
106
|
if (this.refreshAvailableModels) {
|
|
102
107
|
try {
|
|
103
|
-
await this.refreshAvailableModels()
|
|
108
|
+
await this.refreshAvailableModels()
|
|
104
109
|
} catch (error) {
|
|
105
110
|
// Continue with cached models if refresh fails
|
|
106
|
-
console.warn(`Failed to refresh models for ${providerName}:`, error.message)
|
|
111
|
+
console.warn(`Failed to refresh models for ${providerName}:`, error.message)
|
|
107
112
|
}
|
|
108
113
|
}
|
|
109
114
|
}
|
|
110
|
-
|
|
111
|
-
return this.getModelRecommendation(commitDetails)
|
|
115
|
+
|
|
116
|
+
return this.getModelRecommendation(commitDetails)
|
|
112
117
|
},
|
|
113
|
-
|
|
118
|
+
|
|
114
119
|
async selectModelForCapabilities(requiredCapabilities = []) {
|
|
115
|
-
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
116
|
-
return getBestModelForCapabilities(
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
121
|
+
return getBestModelForCapabilities(
|
|
122
|
+
providerName,
|
|
123
|
+
requiredCapabilities,
|
|
124
|
+
this.config,
|
|
125
|
+
availableModels
|
|
126
|
+
)
|
|
127
|
+
},
|
|
128
|
+
}
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
/**
|
|
@@ -128,37 +138,33 @@ export function ConnectionTestMixin(providerName) {
|
|
|
128
138
|
async testConnection() {
|
|
129
139
|
if (!this.isAvailable()) {
|
|
130
140
|
return createProviderErrorResponse(
|
|
131
|
-
providerName,
|
|
132
|
-
'connection_test',
|
|
141
|
+
providerName,
|
|
142
|
+
'connection_test',
|
|
133
143
|
`${providerName} provider is not configured`,
|
|
134
144
|
[`Configure ${providerName.toUpperCase()}_API_KEY`]
|
|
135
|
-
)
|
|
145
|
+
)
|
|
136
146
|
}
|
|
137
|
-
|
|
138
|
-
const modelConfig = getProviderModelConfig(providerName, this.config)
|
|
139
|
-
const defaultModel = normalizeModelName(providerName, modelConfig.standardModel)
|
|
140
|
-
|
|
141
|
-
const result = await standardConnectionTest(
|
|
142
|
-
|
|
143
|
-
defaultModel
|
|
144
|
-
);
|
|
145
|
-
|
|
147
|
+
|
|
148
|
+
const modelConfig = getProviderModelConfig(providerName, this.config)
|
|
149
|
+
const defaultModel = normalizeModelName(providerName, modelConfig.standardModel)
|
|
150
|
+
|
|
151
|
+
const result = await standardConnectionTest(this.generateCompletion.bind(this), defaultModel)
|
|
152
|
+
|
|
146
153
|
if (result.success) {
|
|
147
154
|
return createProviderSuccessResponse(providerName, {
|
|
148
155
|
response: result.response,
|
|
149
156
|
model: result.model,
|
|
150
|
-
provider_info: this.getProviderInfo ? this.getProviderInfo() : {}
|
|
151
|
-
})
|
|
152
|
-
} else {
|
|
153
|
-
return createProviderErrorResponse(
|
|
154
|
-
providerName,
|
|
155
|
-
'connection_test',
|
|
156
|
-
result.error,
|
|
157
|
-
modelConfig.fallbacks
|
|
158
|
-
);
|
|
157
|
+
provider_info: this.getProviderInfo ? this.getProviderInfo() : {},
|
|
158
|
+
})
|
|
159
159
|
}
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
return createProviderErrorResponse(
|
|
161
|
+
providerName,
|
|
162
|
+
'connection_test',
|
|
163
|
+
result.error,
|
|
164
|
+
modelConfig.fallbacks
|
|
165
|
+
)
|
|
166
|
+
},
|
|
167
|
+
}
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
/**
|
|
@@ -175,94 +181,96 @@ export function ModelValidationMixin(providerName) {
|
|
|
175
181
|
'model_validation',
|
|
176
182
|
`${providerName} provider is not configured`,
|
|
177
183
|
[`Configure ${providerName.toUpperCase()}_API_KEY`]
|
|
178
|
-
)
|
|
184
|
+
)
|
|
179
185
|
}
|
|
180
|
-
|
|
181
|
-
const normalizedModel = normalizeModelName(providerName, modelName)
|
|
182
|
-
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
183
|
-
const fallbacks = getSuggestedModels(providerName, normalizedModel, availableModels)
|
|
184
|
-
|
|
186
|
+
|
|
187
|
+
const normalizedModel = normalizeModelName(providerName, modelName)
|
|
188
|
+
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
189
|
+
const fallbacks = getSuggestedModels(providerName, normalizedModel, availableModels)
|
|
190
|
+
|
|
185
191
|
// For hub providers, check if model is in available list first
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
if (
|
|
193
|
+
isHubProvider(providerName) &&
|
|
194
|
+
Array.isArray(availableModels) &&
|
|
195
|
+
availableModels.length > 0 &&
|
|
196
|
+
!availableModels.includes(normalizedModel)
|
|
197
|
+
) {
|
|
198
|
+
return createProviderErrorResponse(
|
|
199
|
+
providerName,
|
|
200
|
+
'model_validation',
|
|
201
|
+
`Model '${normalizedModel}' not found in available deployments`,
|
|
202
|
+
fallbacks
|
|
203
|
+
)
|
|
195
204
|
}
|
|
196
|
-
|
|
205
|
+
|
|
197
206
|
// Use provider-specific model testing if available
|
|
198
207
|
if (this.testModel) {
|
|
199
|
-
return validateModelWithFallbacks(
|
|
200
|
-
this.testModel.bind(this),
|
|
201
|
-
normalizedModel,
|
|
202
|
-
fallbacks
|
|
203
|
-
);
|
|
208
|
+
return validateModelWithFallbacks(this.testModel.bind(this), normalizedModel, fallbacks)
|
|
204
209
|
}
|
|
205
|
-
|
|
210
|
+
|
|
206
211
|
// Fallback to basic connection test with the model
|
|
207
212
|
try {
|
|
208
213
|
const result = await standardConnectionTest(
|
|
209
214
|
this.generateCompletion.bind(this),
|
|
210
215
|
normalizedModel
|
|
211
|
-
)
|
|
212
|
-
|
|
216
|
+
)
|
|
217
|
+
|
|
213
218
|
if (result.success) {
|
|
214
219
|
return createProviderSuccessResponse(providerName, {
|
|
215
220
|
model: normalizedModel,
|
|
216
221
|
capabilities: this.getCapabilities(normalizedModel),
|
|
217
222
|
isHubProvider: isHubProvider(providerName),
|
|
218
|
-
availableModels: availableModels.length
|
|
219
|
-
})
|
|
220
|
-
} else {
|
|
221
|
-
return createProviderErrorResponse(
|
|
222
|
-
providerName,
|
|
223
|
-
'model_validation',
|
|
224
|
-
result.error,
|
|
225
|
-
fallbacks
|
|
226
|
-
);
|
|
223
|
+
availableModels: availableModels.length,
|
|
224
|
+
})
|
|
227
225
|
}
|
|
226
|
+
return createProviderErrorResponse(
|
|
227
|
+
providerName,
|
|
228
|
+
'model_validation',
|
|
229
|
+
result.error,
|
|
230
|
+
fallbacks
|
|
231
|
+
)
|
|
228
232
|
} catch (error) {
|
|
229
233
|
return createProviderErrorResponse(
|
|
230
234
|
providerName,
|
|
231
235
|
'model_validation',
|
|
232
236
|
error.message,
|
|
233
237
|
fallbacks
|
|
234
|
-
)
|
|
238
|
+
)
|
|
235
239
|
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
240
|
+
},
|
|
241
|
+
}
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
/**
|
|
241
245
|
* Mixin that provides standard capabilities lookup
|
|
242
|
-
* @param {string} providerName - Name of the provider
|
|
246
|
+
* @param {string} providerName - Name of the provider
|
|
243
247
|
* @returns {Object} Mixin methods
|
|
244
248
|
*/
|
|
245
249
|
export function CapabilitiesMixin(providerName) {
|
|
246
250
|
return {
|
|
247
251
|
getCapabilities(modelName) {
|
|
248
|
-
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
249
|
-
const modelConfig = getProviderModelConfig(providerName, this.config, availableModels)
|
|
250
|
-
|
|
251
|
-
const model =
|
|
252
|
-
|
|
252
|
+
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
253
|
+
const modelConfig = getProviderModelConfig(providerName, this.config, availableModels)
|
|
254
|
+
|
|
255
|
+
const model =
|
|
256
|
+
modelName ||
|
|
257
|
+
this.config.AI_MODEL ||
|
|
253
258
|
this.config[`${providerName.toUpperCase()}_MODEL`] ||
|
|
254
|
-
modelConfig.standardModel
|
|
255
|
-
|
|
256
|
-
const normalizedModel = normalizeModelName(providerName, model)
|
|
257
|
-
const capabilities = getModelCapabilities(normalizedModel)
|
|
258
|
-
|
|
259
|
+
modelConfig.standardModel
|
|
260
|
+
|
|
261
|
+
const normalizedModel = normalizeModelName(providerName, model)
|
|
262
|
+
const capabilities = getModelCapabilities(normalizedModel)
|
|
263
|
+
|
|
259
264
|
// Add provider-specific metadata
|
|
260
265
|
if (isHubProvider(providerName)) {
|
|
261
|
-
capabilities.isHubProvider = true
|
|
262
|
-
capabilities.availableInHub =
|
|
266
|
+
capabilities.isHubProvider = true
|
|
267
|
+
capabilities.availableInHub =
|
|
268
|
+
!Array.isArray(availableModels) ||
|
|
269
|
+
availableModels.length === 0 ||
|
|
270
|
+
availableModels.includes(normalizedModel)
|
|
263
271
|
}
|
|
264
|
-
|
|
265
|
-
return capabilities
|
|
272
|
+
|
|
273
|
+
return capabilities
|
|
266
274
|
},
|
|
267
275
|
|
|
268
276
|
/**
|
|
@@ -278,67 +286,67 @@ export function CapabilitiesMixin(providerName) {
|
|
|
278
286
|
capabilities: {},
|
|
279
287
|
errors: [],
|
|
280
288
|
performance: {},
|
|
281
|
-
tested_at: new Date().toISOString()
|
|
282
|
-
}
|
|
289
|
+
tested_at: new Date().toISOString(),
|
|
290
|
+
}
|
|
283
291
|
|
|
284
292
|
try {
|
|
285
293
|
// Test 1: Basic availability
|
|
286
|
-
results.available = this.isAvailable()
|
|
294
|
+
results.available = this.isAvailable()
|
|
287
295
|
if (!results.available) {
|
|
288
|
-
results.errors.push('Provider not available - check configuration')
|
|
289
|
-
return results
|
|
296
|
+
results.errors.push('Provider not available - check configuration')
|
|
297
|
+
return results
|
|
290
298
|
}
|
|
291
299
|
|
|
292
300
|
// Test 2: Connection test
|
|
293
301
|
if (this.testConnection) {
|
|
294
|
-
const startTime = Date.now()
|
|
302
|
+
const startTime = Date.now()
|
|
295
303
|
try {
|
|
296
|
-
await this.testConnection()
|
|
297
|
-
results.connection = true
|
|
298
|
-
results.performance.connectionTime = Date.now() - startTime
|
|
304
|
+
await this.testConnection()
|
|
305
|
+
results.connection = true
|
|
306
|
+
results.performance.connectionTime = Date.now() - startTime
|
|
299
307
|
} catch (error) {
|
|
300
|
-
results.errors.push(`Connection test failed: ${error.message}`)
|
|
308
|
+
results.errors.push(`Connection test failed: ${error.message}`)
|
|
301
309
|
}
|
|
302
310
|
}
|
|
303
311
|
|
|
304
312
|
// Test 3: Model access test
|
|
305
313
|
if (options.testModel !== false) {
|
|
306
314
|
try {
|
|
307
|
-
const testStartTime = Date.now()
|
|
315
|
+
const testStartTime = Date.now()
|
|
308
316
|
const testResponse = await ProviderResponseHandler.executeWithErrorHandling(
|
|
309
317
|
this,
|
|
310
318
|
'test_model_access',
|
|
311
319
|
async () => {
|
|
312
|
-
return await this.generateCompletion(
|
|
313
|
-
{ role: 'user', content: 'Test message - respond with "OK"' }
|
|
314
|
-
|
|
320
|
+
return await this.generateCompletion(
|
|
321
|
+
[{ role: 'user', content: 'Test message - respond with "OK"' }],
|
|
322
|
+
{ max_tokens: 10 }
|
|
323
|
+
)
|
|
315
324
|
}
|
|
316
|
-
)
|
|
325
|
+
)
|
|
317
326
|
|
|
318
327
|
if (testResponse && !testResponse.error) {
|
|
319
|
-
results.modelAccess = true
|
|
320
|
-
results.performance.modelResponseTime = Date.now() - testStartTime
|
|
321
|
-
results.performance.tokensGenerated = testResponse.tokens || 0
|
|
328
|
+
results.modelAccess = true
|
|
329
|
+
results.performance.modelResponseTime = Date.now() - testStartTime
|
|
330
|
+
results.performance.tokensGenerated = testResponse.tokens || 0
|
|
322
331
|
} else {
|
|
323
|
-
results.errors.push(`Model test failed: ${testResponse.error || 'Unknown error'}`)
|
|
332
|
+
results.errors.push(`Model test failed: ${testResponse.error || 'Unknown error'}`)
|
|
324
333
|
}
|
|
325
334
|
} catch (error) {
|
|
326
|
-
results.errors.push(`Model access test failed: ${error.message}`)
|
|
335
|
+
results.errors.push(`Model access test failed: ${error.message}`)
|
|
327
336
|
}
|
|
328
337
|
}
|
|
329
338
|
|
|
330
339
|
// Test 4: Get detailed capabilities
|
|
331
340
|
try {
|
|
332
|
-
results.capabilities = this.getCapabilities(options.model)
|
|
341
|
+
results.capabilities = this.getCapabilities(options.model)
|
|
333
342
|
} catch (error) {
|
|
334
|
-
results.errors.push(`Capabilities detection failed: ${error.message}`)
|
|
343
|
+
results.errors.push(`Capabilities detection failed: ${error.message}`)
|
|
335
344
|
}
|
|
336
|
-
|
|
337
345
|
} catch (error) {
|
|
338
|
-
results.errors.push(`Capability testing failed: ${error.message}`)
|
|
346
|
+
results.errors.push(`Capability testing failed: ${error.message}`)
|
|
339
347
|
}
|
|
340
348
|
|
|
341
|
-
return results
|
|
349
|
+
return results
|
|
342
350
|
},
|
|
343
351
|
|
|
344
352
|
/**
|
|
@@ -350,61 +358,70 @@ export function CapabilitiesMixin(providerName) {
|
|
|
350
358
|
status: 'unknown',
|
|
351
359
|
available: false,
|
|
352
360
|
configured: false,
|
|
353
|
-
timestamp: new Date().toISOString()
|
|
354
|
-
}
|
|
361
|
+
timestamp: new Date().toISOString(),
|
|
362
|
+
}
|
|
355
363
|
|
|
356
364
|
try {
|
|
357
|
-
health.available = this.isAvailable()
|
|
358
|
-
health.configured = this.getName && this.config
|
|
359
|
-
|
|
365
|
+
health.available = this.isAvailable()
|
|
366
|
+
health.configured = this.getName && this.config
|
|
367
|
+
|
|
360
368
|
if (health.available && health.configured) {
|
|
361
|
-
health.status = 'healthy'
|
|
369
|
+
health.status = 'healthy'
|
|
362
370
|
} else if (health.configured) {
|
|
363
|
-
health.status = 'configured_but_unavailable'
|
|
371
|
+
health.status = 'configured_but_unavailable'
|
|
364
372
|
} else {
|
|
365
|
-
health.status = 'not_configured'
|
|
373
|
+
health.status = 'not_configured'
|
|
366
374
|
}
|
|
367
375
|
} catch (error) {
|
|
368
|
-
health.status = 'error'
|
|
369
|
-
health.error = error.message
|
|
376
|
+
health.status = 'error'
|
|
377
|
+
health.error = error.message
|
|
370
378
|
}
|
|
371
379
|
|
|
372
|
-
return health
|
|
380
|
+
return health
|
|
373
381
|
},
|
|
374
|
-
|
|
382
|
+
|
|
375
383
|
getSimilarModels(modelName, providedAvailableModels = []) {
|
|
376
384
|
// Use provider's available models if not provided
|
|
377
|
-
const availableModels =
|
|
378
|
-
providedAvailableModels
|
|
379
|
-
|
|
380
|
-
|
|
385
|
+
const availableModels =
|
|
386
|
+
providedAvailableModels.length > 0
|
|
387
|
+
? providedAvailableModels
|
|
388
|
+
: this.getAvailableModels
|
|
389
|
+
? this.getAvailableModels()
|
|
390
|
+
: []
|
|
391
|
+
|
|
381
392
|
// Enhanced similarity matching for hub providers
|
|
382
|
-
if (
|
|
383
|
-
|
|
393
|
+
if (
|
|
394
|
+
isHubProvider(providerName) &&
|
|
395
|
+
Array.isArray(availableModels) &&
|
|
396
|
+
availableModels.length > 0
|
|
397
|
+
) {
|
|
398
|
+
const modelFamily = modelName.split('-')[0] || modelName.split('.')[0] // e.g., 'gpt', 'claude', 'gemini', 'anthropic'
|
|
384
399
|
const familyModels = availableModels
|
|
385
|
-
.filter(m => m.includes(modelFamily) && m !== modelName)
|
|
386
|
-
.slice(0, 3)
|
|
387
|
-
|
|
400
|
+
.filter((m) => m.includes(modelFamily) && m !== modelName)
|
|
401
|
+
.slice(0, 3)
|
|
402
|
+
|
|
388
403
|
// If no family matches, get models from the same capability tier
|
|
389
404
|
if (familyModels.length === 0) {
|
|
390
|
-
const modelCapabilities = getModelCapabilities(modelName)
|
|
405
|
+
const modelCapabilities = getModelCapabilities(modelName)
|
|
391
406
|
const similarCapabilityModels = availableModels
|
|
392
|
-
.filter(m => {
|
|
393
|
-
const caps = getModelCapabilities(m)
|
|
394
|
-
return
|
|
395
|
-
|
|
407
|
+
.filter((m) => {
|
|
408
|
+
const caps = getModelCapabilities(m)
|
|
409
|
+
return (
|
|
410
|
+
caps.reasoning === modelCapabilities.reasoning &&
|
|
411
|
+
caps.large_context === modelCapabilities.large_context
|
|
412
|
+
)
|
|
396
413
|
})
|
|
397
|
-
.slice(0, 3)
|
|
398
|
-
return similarCapabilityModels
|
|
414
|
+
.slice(0, 3)
|
|
415
|
+
return similarCapabilityModels
|
|
399
416
|
}
|
|
400
|
-
|
|
401
|
-
return familyModels
|
|
417
|
+
|
|
418
|
+
return familyModels
|
|
402
419
|
}
|
|
403
|
-
|
|
420
|
+
|
|
404
421
|
// Standard provider logic with enhanced suggestions
|
|
405
|
-
return getSuggestedModels(providerName, modelName, availableModels)
|
|
406
|
-
}
|
|
407
|
-
}
|
|
422
|
+
return getSuggestedModels(providerName, modelName, availableModels)
|
|
423
|
+
},
|
|
424
|
+
}
|
|
408
425
|
}
|
|
409
426
|
|
|
410
427
|
/**
|
|
@@ -416,53 +433,49 @@ export function CapabilitiesMixin(providerName) {
|
|
|
416
433
|
export function ConfigurationMixin(providerName, defaults = {}) {
|
|
417
434
|
return {
|
|
418
435
|
getProviderConfig() {
|
|
419
|
-
return extractProviderConfig(
|
|
420
|
-
this.config,
|
|
421
|
-
providerName.toUpperCase(),
|
|
422
|
-
defaults
|
|
423
|
-
);
|
|
436
|
+
return extractProviderConfig(this.config, providerName.toUpperCase(), defaults)
|
|
424
437
|
},
|
|
425
|
-
|
|
438
|
+
|
|
426
439
|
getProviderModelConfig() {
|
|
427
|
-
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
428
|
-
return getProviderModelConfig(providerName, this.config, availableModels)
|
|
440
|
+
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
441
|
+
return getProviderModelConfig(providerName, this.config, availableModels)
|
|
429
442
|
},
|
|
430
|
-
|
|
443
|
+
|
|
431
444
|
buildClientOptions(extraDefaults = {}) {
|
|
432
|
-
const providerConfig = this.getProviderConfig()
|
|
433
|
-
return buildClientOptions(providerConfig, { ...defaults, ...extraDefaults })
|
|
445
|
+
const providerConfig = this.getProviderConfig()
|
|
446
|
+
return buildClientOptions(providerConfig, { ...defaults, ...extraDefaults })
|
|
434
447
|
},
|
|
435
|
-
|
|
448
|
+
|
|
436
449
|
getProviderInfo() {
|
|
437
|
-
const providerConfig = this.getProviderConfig()
|
|
438
|
-
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
439
|
-
const modelConfig = getProviderModelConfig(providerName, this.config, availableModels)
|
|
440
|
-
|
|
450
|
+
const providerConfig = this.getProviderConfig()
|
|
451
|
+
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
452
|
+
const modelConfig = getProviderModelConfig(providerName, this.config, availableModels)
|
|
453
|
+
|
|
441
454
|
const info = {
|
|
442
455
|
name: providerName,
|
|
443
456
|
configured: this.isAvailable(),
|
|
444
|
-
config_keys: Object.keys(providerConfig).filter(k => providerConfig[k]),
|
|
457
|
+
config_keys: Object.keys(providerConfig).filter((k) => providerConfig[k]),
|
|
445
458
|
default_model: modelConfig.standardModel || 'unknown',
|
|
446
|
-
isHub: isHubProvider(providerName)
|
|
447
|
-
}
|
|
448
|
-
|
|
459
|
+
isHub: isHubProvider(providerName),
|
|
460
|
+
}
|
|
461
|
+
|
|
449
462
|
// Add hub-specific information
|
|
450
463
|
if (isHubProvider(providerName)) {
|
|
451
464
|
info.hubInfo = {
|
|
452
465
|
availableModels: availableModels.length,
|
|
453
466
|
supportedProviders: modelConfig.hubInfo?.supportedProviders || [],
|
|
454
467
|
defaultProvider: modelConfig.hubInfo?.defaultProvider,
|
|
455
|
-
canDetectDeployments: !!(this.getAvailableModels || this.refreshAvailableModels)
|
|
456
|
-
}
|
|
457
|
-
|
|
468
|
+
canDetectDeployments: !!(this.getAvailableModels || this.refreshAvailableModels),
|
|
469
|
+
}
|
|
470
|
+
|
|
458
471
|
if (availableModels.length > 0) {
|
|
459
|
-
info.hubInfo.sampleModels = availableModels.slice(0, 3)
|
|
472
|
+
info.hubInfo.sampleModels = availableModels.slice(0, 3)
|
|
460
473
|
}
|
|
461
474
|
}
|
|
462
|
-
|
|
463
|
-
return info
|
|
464
|
-
}
|
|
465
|
-
}
|
|
475
|
+
|
|
476
|
+
return info
|
|
477
|
+
},
|
|
478
|
+
}
|
|
466
479
|
}
|
|
467
480
|
|
|
468
481
|
/**
|
|
@@ -481,13 +494,13 @@ export class ProviderResponseHandler {
|
|
|
481
494
|
static async executeWithErrorHandling(provider, operation, operationFn, context = {}) {
|
|
482
495
|
// Check availability first
|
|
483
496
|
if (!provider.isAvailable()) {
|
|
484
|
-
return
|
|
497
|
+
return ProviderResponseHandler.createUnavailableResponse(provider.getName(), operation)
|
|
485
498
|
}
|
|
486
499
|
|
|
487
500
|
try {
|
|
488
|
-
return await operationFn()
|
|
501
|
+
return await operationFn()
|
|
489
502
|
} catch (error) {
|
|
490
|
-
return provider.handleProviderError(error, operation, context)
|
|
503
|
+
return provider.handleProviderError(error, operation, context)
|
|
491
504
|
}
|
|
492
505
|
}
|
|
493
506
|
|
|
@@ -503,7 +516,7 @@ export class ProviderResponseHandler {
|
|
|
503
516
|
operation,
|
|
504
517
|
`${providerName} provider is not configured`,
|
|
505
518
|
[`Configure ${providerName.toUpperCase()}_API_KEY and other required settings`]
|
|
506
|
-
)
|
|
519
|
+
)
|
|
507
520
|
}
|
|
508
521
|
|
|
509
522
|
/**
|
|
@@ -513,16 +526,21 @@ export class ProviderResponseHandler {
|
|
|
513
526
|
* @returns {Promise<Array>} Array of results
|
|
514
527
|
*/
|
|
515
528
|
static async executeMultiple(provider, operations) {
|
|
516
|
-
const results = []
|
|
529
|
+
const results = []
|
|
517
530
|
for (const op of operations) {
|
|
518
|
-
const result = await
|
|
519
|
-
|
|
531
|
+
const result = await ProviderResponseHandler.executeWithErrorHandling(
|
|
532
|
+
provider,
|
|
533
|
+
op.name,
|
|
534
|
+
op.fn,
|
|
535
|
+
op.context
|
|
536
|
+
)
|
|
537
|
+
results.push(result)
|
|
520
538
|
// Stop on first error if any operation fails
|
|
521
539
|
if (result.error) {
|
|
522
|
-
break
|
|
540
|
+
break
|
|
523
541
|
}
|
|
524
542
|
}
|
|
525
|
-
return results
|
|
543
|
+
return results
|
|
526
544
|
}
|
|
527
545
|
}
|
|
528
546
|
|
|
@@ -536,59 +554,49 @@ export function ErrorHandlingMixin(providerName) {
|
|
|
536
554
|
handleProviderError(error, operation, context = {}) {
|
|
537
555
|
// Common error patterns and their standardized responses
|
|
538
556
|
if (error.message.includes('API key') || error.message.includes('401')) {
|
|
539
|
-
return createProviderErrorResponse(
|
|
540
|
-
providerName
|
|
541
|
-
|
|
542
|
-
'Invalid or missing API key',
|
|
543
|
-
[`Check ${providerName.toUpperCase()}_API_KEY configuration`]
|
|
544
|
-
);
|
|
557
|
+
return createProviderErrorResponse(providerName, operation, 'Invalid or missing API key', [
|
|
558
|
+
`Check ${providerName.toUpperCase()}_API_KEY configuration`,
|
|
559
|
+
])
|
|
545
560
|
}
|
|
546
|
-
|
|
561
|
+
|
|
547
562
|
if (error.message.includes('model') && error.message.includes('not found')) {
|
|
548
|
-
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
549
|
-
const
|
|
550
|
-
const fallbacks = getSuggestedModels(providerName, context.model, availableModels)
|
|
551
|
-
|
|
552
|
-
let errorMessage = `Model not available: ${context.model}
|
|
553
|
-
if (
|
|
554
|
-
|
|
563
|
+
const availableModels = this.getAvailableModels ? this.getAvailableModels() : []
|
|
564
|
+
const _modelConfig = getProviderModelConfig(providerName, this.config, availableModels)
|
|
565
|
+
const fallbacks = getSuggestedModels(providerName, context.model, availableModels)
|
|
566
|
+
|
|
567
|
+
let errorMessage = `Model not available: ${context.model}`
|
|
568
|
+
if (
|
|
569
|
+
isHubProvider(providerName) &&
|
|
570
|
+
Array.isArray(availableModels) &&
|
|
571
|
+
availableModels.length > 0
|
|
572
|
+
) {
|
|
573
|
+
errorMessage += ` (Available models: ${availableModels.length})`
|
|
555
574
|
}
|
|
556
|
-
|
|
557
|
-
return createProviderErrorResponse(
|
|
558
|
-
providerName,
|
|
559
|
-
operation,
|
|
560
|
-
errorMessage,
|
|
561
|
-
fallbacks
|
|
562
|
-
);
|
|
575
|
+
|
|
576
|
+
return createProviderErrorResponse(providerName, operation, errorMessage, fallbacks)
|
|
563
577
|
}
|
|
564
|
-
|
|
578
|
+
|
|
565
579
|
if (error.message.includes('rate limit') || error.message.includes('429')) {
|
|
566
|
-
return createProviderErrorResponse(
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
['Wait before retrying', 'Consider upgrading API plan']
|
|
571
|
-
);
|
|
580
|
+
return createProviderErrorResponse(providerName, operation, 'Rate limit exceeded', [
|
|
581
|
+
'Wait before retrying',
|
|
582
|
+
'Consider upgrading API plan',
|
|
583
|
+
])
|
|
572
584
|
}
|
|
573
|
-
|
|
585
|
+
|
|
574
586
|
if (error.message.includes('timeout')) {
|
|
575
|
-
return createProviderErrorResponse(
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
['Increase timeout setting', 'Try again later']
|
|
580
|
-
);
|
|
587
|
+
return createProviderErrorResponse(providerName, operation, 'Request timeout', [
|
|
588
|
+
'Increase timeout setting',
|
|
589
|
+
'Try again later',
|
|
590
|
+
])
|
|
581
591
|
}
|
|
582
|
-
|
|
592
|
+
|
|
583
593
|
// Generic error response
|
|
584
|
-
return createProviderErrorResponse(
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
591
|
-
};
|
|
594
|
+
return createProviderErrorResponse(providerName, operation, error.message, [
|
|
595
|
+
'Check provider configuration',
|
|
596
|
+
'Verify network connectivity',
|
|
597
|
+
])
|
|
598
|
+
},
|
|
599
|
+
}
|
|
592
600
|
}
|
|
593
601
|
|
|
594
602
|
/**
|
|
@@ -605,18 +613,18 @@ export function applyMixins(ProviderClass, providerName, mixins = []) {
|
|
|
605
613
|
ConnectionTestMixin,
|
|
606
614
|
ModelValidationMixin,
|
|
607
615
|
CapabilitiesMixin,
|
|
608
|
-
ErrorHandlingMixin
|
|
609
|
-
]
|
|
610
|
-
|
|
611
|
-
const allMixins = [...defaultMixins, ...mixins]
|
|
612
|
-
|
|
616
|
+
ErrorHandlingMixin,
|
|
617
|
+
]
|
|
618
|
+
|
|
619
|
+
const allMixins = [...defaultMixins, ...mixins]
|
|
620
|
+
|
|
613
621
|
// Apply each mixin to the prototype
|
|
614
|
-
allMixins.forEach(mixinFn => {
|
|
615
|
-
const methods = mixinFn(providerName)
|
|
616
|
-
Object.assign(ProviderClass.prototype, methods)
|
|
617
|
-
})
|
|
618
|
-
|
|
619
|
-
return ProviderClass
|
|
622
|
+
allMixins.forEach((mixinFn) => {
|
|
623
|
+
const methods = mixinFn(providerName)
|
|
624
|
+
Object.assign(ProviderClass.prototype, methods)
|
|
625
|
+
})
|
|
626
|
+
|
|
627
|
+
return ProviderClass
|
|
620
628
|
}
|
|
621
629
|
|
|
622
630
|
/**
|
|
@@ -626,35 +634,33 @@ export function applyMixins(ProviderClass, providerName, mixins = []) {
|
|
|
626
634
|
* @returns {Function} Base provider class with mixins applied
|
|
627
635
|
*/
|
|
628
636
|
export function createEnhancedProvider(providerName, options = {}) {
|
|
629
|
-
const { mixins = [] } = options
|
|
630
|
-
|
|
637
|
+
const { mixins = [] } = options
|
|
638
|
+
|
|
631
639
|
class EnhancedProvider {
|
|
632
640
|
constructor(config) {
|
|
633
|
-
this.config = config
|
|
634
|
-
this.providerName = providerName
|
|
635
|
-
|
|
641
|
+
this.config = config
|
|
642
|
+
this.providerName = providerName
|
|
643
|
+
|
|
636
644
|
// Initialize provider-specific client if available method exists
|
|
637
|
-
if (this.initializeClient) {
|
|
638
|
-
|
|
639
|
-
this.initializeClient();
|
|
640
|
-
}
|
|
645
|
+
if (this.initializeClient && this.isAvailable()) {
|
|
646
|
+
this.initializeClient()
|
|
641
647
|
}
|
|
642
648
|
}
|
|
643
|
-
|
|
649
|
+
|
|
644
650
|
getName() {
|
|
645
|
-
return providerName
|
|
651
|
+
return providerName
|
|
646
652
|
}
|
|
647
|
-
|
|
653
|
+
|
|
648
654
|
// These methods should be implemented by the specific provider
|
|
649
655
|
isAvailable() {
|
|
650
|
-
throw new Error('isAvailable() must be implemented by the provider')
|
|
656
|
+
throw new Error('isAvailable() must be implemented by the provider')
|
|
651
657
|
}
|
|
652
|
-
|
|
658
|
+
|
|
653
659
|
async generateCompletion() {
|
|
654
|
-
throw new Error('generateCompletion() must be implemented by the provider')
|
|
660
|
+
throw new Error('generateCompletion() must be implemented by the provider')
|
|
655
661
|
}
|
|
656
662
|
}
|
|
657
|
-
|
|
663
|
+
|
|
658
664
|
// Apply all mixins
|
|
659
|
-
return applyMixins(EnhancedProvider, providerName, mixins)
|
|
660
|
-
}
|
|
665
|
+
return applyMixins(EnhancedProvider, providerName, mixins)
|
|
666
|
+
}
|