@entro314labs/ai-changelog-generator 3.2.1 → 3.3.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.
- package/ai-changelog-mcp.sh +0 -0
- package/ai-changelog.sh +0 -0
- package/bin/ai-changelog-dxt.js +0 -0
- package/package.json +72 -80
- package/src/ai-changelog-generator.js +5 -4
- package/src/application/orchestrators/changelog.orchestrator.js +12 -202
- package/src/cli.js +4 -5
- package/src/domains/ai/ai-analysis.service.js +2 -0
- package/src/domains/analysis/analysis.engine.js +714 -37
- package/src/domains/changelog/changelog.service.js +615 -30
- package/src/domains/changelog/workspace-changelog.service.js +418 -627
- package/src/domains/git/commit-tagger.js +552 -0
- package/src/domains/git/git-manager.js +357 -0
- package/src/domains/git/git.service.js +865 -16
- package/src/infrastructure/cli/cli.controller.js +14 -9
- package/src/infrastructure/config/configuration.manager.js +24 -2
- package/src/infrastructure/interactive/interactive-workflow.service.js +8 -1
- package/src/infrastructure/mcp/mcp-server.service.js +35 -11
- package/src/infrastructure/providers/core/base-provider.js +1 -1
- package/src/infrastructure/providers/implementations/anthropic.js +16 -173
- package/src/infrastructure/providers/implementations/azure.js +16 -63
- package/src/infrastructure/providers/implementations/dummy.js +13 -16
- package/src/infrastructure/providers/implementations/mock.js +13 -26
- package/src/infrastructure/providers/implementations/ollama.js +12 -4
- package/src/infrastructure/providers/implementations/openai.js +13 -165
- package/src/infrastructure/providers/provider-management.service.js +126 -412
- package/src/infrastructure/providers/utils/base-provider-helpers.js +11 -0
- package/src/shared/utils/cli-ui.js +1 -1
- package/src/shared/utils/diff-processor.js +21 -19
- package/src/shared/utils/error-classes.js +33 -0
- package/src/shared/utils/utils.js +65 -60
- package/src/domains/git/git-repository.analyzer.js +0 -678
|
@@ -83,6 +83,11 @@ export class CLIController {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
async ensureConfig() {
|
|
86
|
+
// Skip config setup in test environments to prevent hanging
|
|
87
|
+
if (process.env.NODE_ENV === 'test' || process.env.CI) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
86
91
|
const configPath = path.join(process.cwd(), '.env.local')
|
|
87
92
|
try {
|
|
88
93
|
await access(configPath)
|
|
@@ -345,17 +350,17 @@ class ValidateCommand extends BaseCommand {
|
|
|
345
350
|
const validation = await appService.validateConfiguration()
|
|
346
351
|
|
|
347
352
|
if (validation.valid) {
|
|
348
|
-
|
|
353
|
+
EnhancedConsole.success('✅ Configuration is valid')
|
|
349
354
|
} else {
|
|
350
|
-
|
|
355
|
+
EnhancedConsole.error('❌ Configuration has issues:')
|
|
351
356
|
validation.issues.forEach((issue) => {
|
|
352
|
-
|
|
357
|
+
EnhancedConsole.log(` - ${issue}`)
|
|
353
358
|
})
|
|
354
359
|
|
|
355
360
|
if (validation.recommendations.length > 0) {
|
|
356
|
-
|
|
361
|
+
EnhancedConsole.info('\n💡 Recommendations:')
|
|
357
362
|
validation.recommendations.forEach((rec) => {
|
|
358
|
-
|
|
363
|
+
EnhancedConsole.log(` - ${rec}`)
|
|
359
364
|
})
|
|
360
365
|
}
|
|
361
366
|
}
|
|
@@ -554,7 +559,7 @@ class ProvidersCommand extends BaseCommand {
|
|
|
554
559
|
colors.dim('\nUse "ai-changelog providers configure <provider>" to set up a provider')
|
|
555
560
|
)
|
|
556
561
|
} catch (error) {
|
|
557
|
-
|
|
562
|
+
EnhancedConsole.error(`Error listing providers: ${error.message}`)
|
|
558
563
|
}
|
|
559
564
|
}
|
|
560
565
|
|
|
@@ -577,7 +582,7 @@ class ProvidersCommand extends BaseCommand {
|
|
|
577
582
|
)
|
|
578
583
|
}
|
|
579
584
|
} catch (error) {
|
|
580
|
-
|
|
585
|
+
EnhancedConsole.error(`Error switching provider: ${error.message}`)
|
|
581
586
|
}
|
|
582
587
|
}
|
|
583
588
|
|
|
@@ -637,7 +642,7 @@ class ProvidersCommand extends BaseCommand {
|
|
|
637
642
|
console.log(colors.infoMessage('\nAfter adding the configuration, run:'))
|
|
638
643
|
console.log(colors.highlight(`ai-changelog providers validate ${providerName}`))
|
|
639
644
|
} catch (error) {
|
|
640
|
-
|
|
645
|
+
EnhancedConsole.error(`Error configuring provider: ${error.message}`)
|
|
641
646
|
}
|
|
642
647
|
}
|
|
643
648
|
|
|
@@ -676,7 +681,7 @@ class ProvidersCommand extends BaseCommand {
|
|
|
676
681
|
}
|
|
677
682
|
}
|
|
678
683
|
} catch (error) {
|
|
679
|
-
|
|
684
|
+
EnhancedConsole.error(`Error validating provider: ${error.message}`)
|
|
680
685
|
}
|
|
681
686
|
}
|
|
682
687
|
}
|
|
@@ -151,7 +151,7 @@ export class ConfigurationManager {
|
|
|
151
151
|
releaseTagGlobPattern: 'v[0-9]*.[0-9]*.[0-9]*',
|
|
152
152
|
},
|
|
153
153
|
changelog: {
|
|
154
|
-
commitTypes: ['feat', 'fix', 'perf', 'refactor', 'docs'],
|
|
154
|
+
commitTypes: ['feat', 'fix', 'perf', 'refactor', 'docs', 'merge'],
|
|
155
155
|
includeInvalidCommits: true,
|
|
156
156
|
commitIgnoreRegexPattern: '^WIP ',
|
|
157
157
|
headlines: {
|
|
@@ -529,7 +529,21 @@ export class ConfigurationManager {
|
|
|
529
529
|
}
|
|
530
530
|
|
|
531
531
|
getCommitTypes() {
|
|
532
|
-
return
|
|
532
|
+
return (
|
|
533
|
+
this.changelogConfig?.convention?.commitTypes || [
|
|
534
|
+
'feat',
|
|
535
|
+
'fix',
|
|
536
|
+
'docs',
|
|
537
|
+
'style',
|
|
538
|
+
'refactor',
|
|
539
|
+
'perf',
|
|
540
|
+
'test',
|
|
541
|
+
'build',
|
|
542
|
+
'ci',
|
|
543
|
+
'chore',
|
|
544
|
+
'revert',
|
|
545
|
+
]
|
|
546
|
+
)
|
|
533
547
|
}
|
|
534
548
|
|
|
535
549
|
getChangelogCommitTypes() {
|
|
@@ -565,4 +579,12 @@ export class ConfigurationManager {
|
|
|
565
579
|
const pattern = this.changelogConfig.changelog.commitIgnoreRegexPattern
|
|
566
580
|
return pattern ? new RegExp(pattern) : /^WIP /
|
|
567
581
|
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Get the complete configuration object
|
|
585
|
+
* @returns {Object} Configuration object
|
|
586
|
+
*/
|
|
587
|
+
getConfig() {
|
|
588
|
+
return this.config || {}
|
|
589
|
+
}
|
|
568
590
|
}
|
|
@@ -198,6 +198,10 @@ Provide 3 suggestions.`
|
|
|
198
198
|
return summary
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
async generateChangelogForCommitHashes(commitHashes) {
|
|
202
|
+
return await this.generateChangelogForCommits(commitHashes)
|
|
203
|
+
}
|
|
204
|
+
|
|
201
205
|
async selectSpecificCommits() {
|
|
202
206
|
return await selectSpecificCommits()
|
|
203
207
|
}
|
|
@@ -207,7 +211,10 @@ Provide 3 suggestions.`
|
|
|
207
211
|
const spinner = new SimpleSpinner(`Generating changelog for recent ${count} commits...`)
|
|
208
212
|
spinner.start()
|
|
209
213
|
|
|
210
|
-
const commits =
|
|
214
|
+
const commits =
|
|
215
|
+
(await this.gitService.getCommitsSince?.(null)) ||
|
|
216
|
+
(await this.gitService.getCommitAnalysis?.()) ||
|
|
217
|
+
[]
|
|
211
218
|
const recentCommits = commits.slice(0, count)
|
|
212
219
|
|
|
213
220
|
if (recentCommits.length === 0) {
|
|
@@ -18,9 +18,9 @@ import { ChangelogOrchestrator } from '../../application/orchestrators/changelog
|
|
|
18
18
|
// Import application services
|
|
19
19
|
import { ApplicationService } from '../../application/services/application.service.js'
|
|
20
20
|
import { AnalysisEngine } from '../../domains/analysis/analysis.engine.js'
|
|
21
|
-
import {
|
|
21
|
+
import { GitService } from '../../domains/git/git.service.js'
|
|
22
22
|
import { ConfigurationManager } from '../config/configuration.manager.js'
|
|
23
|
-
import {
|
|
23
|
+
import { ProviderManagerService } from '../providers/provider-manager.service.js'
|
|
24
24
|
|
|
25
25
|
const __filename = fileURLToPath(import.meta.url)
|
|
26
26
|
const __dirname = dirname(__filename)
|
|
@@ -64,9 +64,9 @@ class AIChangelogMCPServer {
|
|
|
64
64
|
this.configManager = new ConfigurationManager()
|
|
65
65
|
this.applicationService = new ApplicationService()
|
|
66
66
|
this.changelogOrchestrator = new ChangelogOrchestrator(this.configManager)
|
|
67
|
-
this.gitAnalyzer = new
|
|
67
|
+
this.gitAnalyzer = new GitService()
|
|
68
68
|
this.analysisEngine = new AnalysisEngine()
|
|
69
|
-
this.providerService = new
|
|
69
|
+
this.providerService = new ProviderManagerService(this.configManager.getConfig())
|
|
70
70
|
|
|
71
71
|
// Log available configuration
|
|
72
72
|
const hasProvider = process.env.AI_PROVIDER
|
|
@@ -419,15 +419,39 @@ class AIChangelogMCPServer {
|
|
|
419
419
|
result += `\n${testResult}`
|
|
420
420
|
}
|
|
421
421
|
break
|
|
422
|
-
case 'test':
|
|
423
|
-
|
|
422
|
+
case 'test': {
|
|
423
|
+
const activeProvider = this.providerService.getActiveProvider()
|
|
424
|
+
if (!activeProvider) {
|
|
425
|
+
throw new Error('No active provider found')
|
|
426
|
+
}
|
|
427
|
+
result = await this.providerService.testProvider(activeProvider.getName())
|
|
424
428
|
break
|
|
425
|
-
|
|
426
|
-
|
|
429
|
+
}
|
|
430
|
+
case 'configure': {
|
|
431
|
+
if (!provider) {
|
|
432
|
+
throw new Error('Provider required for configure action')
|
|
433
|
+
}
|
|
434
|
+
const providerData = this.providerService.findProviderByName(provider)
|
|
435
|
+
if (!providerData) {
|
|
436
|
+
throw new Error(`Provider '${provider}' not found`)
|
|
437
|
+
}
|
|
438
|
+
result = {
|
|
439
|
+
name: provider,
|
|
440
|
+
available: providerData.available,
|
|
441
|
+
configuration: providerData.instance.getConfiguration
|
|
442
|
+
? providerData.instance.getConfiguration()
|
|
443
|
+
: {},
|
|
444
|
+
requiredVars: providerData.instance.getRequiredEnvVars
|
|
445
|
+
? providerData.instance.getRequiredEnvVars()
|
|
446
|
+
: [],
|
|
447
|
+
}
|
|
427
448
|
break
|
|
428
|
-
|
|
429
|
-
|
|
449
|
+
}
|
|
450
|
+
case 'validate': {
|
|
451
|
+
const validationResults = await this.providerService.validateAll()
|
|
452
|
+
result = validationResults
|
|
430
453
|
break
|
|
454
|
+
}
|
|
431
455
|
default:
|
|
432
456
|
throw new Error(`Unknown action: ${action}`)
|
|
433
457
|
}
|
|
@@ -436,7 +460,7 @@ class AIChangelogMCPServer {
|
|
|
436
460
|
content: [
|
|
437
461
|
{
|
|
438
462
|
type: 'text',
|
|
439
|
-
text: result,
|
|
463
|
+
text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
|
|
440
464
|
},
|
|
441
465
|
],
|
|
442
466
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Abstract Base Provider for AI models.
|
|
3
3
|
* Defines the interface that all provider plugins must implement.
|
|
4
4
|
*/
|
|
5
|
-
import { AbstractMethodError } from '../../../shared/utils/
|
|
5
|
+
import { AbstractMethodError } from '../../../shared/utils/error-classes.js'
|
|
6
6
|
|
|
7
7
|
export class BaseProvider {
|
|
8
8
|
constructor(config) {
|
|
@@ -2,7 +2,6 @@ import Anthropic from '@anthropic-ai/sdk'
|
|
|
2
2
|
|
|
3
3
|
import { BaseProvider } from '../core/base-provider.js'
|
|
4
4
|
import { applyMixins, ProviderResponseHandler } from '../utils/base-provider-helpers.js'
|
|
5
|
-
import { buildClientOptions } from '../utils/provider-utils.js'
|
|
6
5
|
|
|
7
6
|
export class AnthropicProvider extends BaseProvider {
|
|
8
7
|
constructor(config) {
|
|
@@ -14,7 +13,7 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
initializeClient() {
|
|
17
|
-
const clientOptions =
|
|
16
|
+
const clientOptions = this.buildClientOptions({
|
|
18
17
|
timeout: 60000,
|
|
19
18
|
maxRetries: 2,
|
|
20
19
|
defaultHeaders: {
|
|
@@ -24,7 +23,7 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
24
23
|
})
|
|
25
24
|
|
|
26
25
|
this.anthropic = new Anthropic({
|
|
27
|
-
apiKey: clientOptions.
|
|
26
|
+
apiKey: clientOptions.ANTHROPIC_API_KEY,
|
|
28
27
|
baseURL: clientOptions.baseURL,
|
|
29
28
|
timeout: clientOptions.timeout,
|
|
30
29
|
maxRetries: clientOptions.maxRetries,
|
|
@@ -45,7 +44,8 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
getDefaultModel() {
|
|
48
|
-
|
|
47
|
+
const modelConfig = this.getProviderModelConfig()
|
|
48
|
+
return modelConfig.standardModel
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
async generateCompletion(messages, options = {}) {
|
|
@@ -115,127 +115,32 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
115
115
|
|
|
116
116
|
async getAvailableModels() {
|
|
117
117
|
// Anthropic doesn't provide a models endpoint, return known models
|
|
118
|
+
// Use direct model names to avoid circular dependency with mixins
|
|
118
119
|
return [
|
|
119
|
-
{
|
|
120
|
-
name: 'claude-sonnet-4-20250514',
|
|
121
|
-
id: 'claude-sonnet-4-20250514',
|
|
122
|
-
description: 'Claude Sonnet 4 - Latest balanced model (2025)',
|
|
123
|
-
contextWindow: 200000,
|
|
124
|
-
capabilities: {
|
|
125
|
-
reasoning: true,
|
|
126
|
-
function_calling: true,
|
|
127
|
-
json_mode: true,
|
|
128
|
-
multimodal: true,
|
|
129
|
-
largeContext: true,
|
|
130
|
-
toolUse: true,
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
120
|
{
|
|
134
121
|
name: 'claude-opus-4-20250514',
|
|
135
122
|
id: 'claude-opus-4-20250514',
|
|
136
123
|
description: 'Claude Opus 4 - Most capable model for complex tasks (2025)',
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
multimodal: true,
|
|
143
|
-
largeContext: true,
|
|
144
|
-
toolUse: true,
|
|
145
|
-
advancedReasoning: true,
|
|
146
|
-
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'claude-sonnet-4-20250514',
|
|
127
|
+
id: 'claude-sonnet-4-20250514',
|
|
128
|
+
description: 'Claude Sonnet 4 - Latest balanced model (2025)',
|
|
147
129
|
},
|
|
148
130
|
{
|
|
149
131
|
name: 'claude-3.7-sonnet-20250219',
|
|
150
132
|
id: 'claude-3.7-sonnet-20250219',
|
|
151
133
|
description: 'Claude 3.7 Sonnet - Enhanced reasoning capabilities',
|
|
152
|
-
contextWindow: 200000,
|
|
153
|
-
capabilities: {
|
|
154
|
-
reasoning: true,
|
|
155
|
-
function_calling: true,
|
|
156
|
-
json_mode: true,
|
|
157
|
-
multimodal: true,
|
|
158
|
-
largeContext: true,
|
|
159
|
-
toolUse: true,
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
name: 'claude-3-5-sonnet-20241022',
|
|
164
|
-
id: 'claude-3-5-sonnet-20241022',
|
|
165
|
-
description: 'Claude 3.5 Sonnet - Previous generation',
|
|
166
|
-
contextWindow: 200000,
|
|
167
|
-
capabilities: {
|
|
168
|
-
reasoning: true,
|
|
169
|
-
function_calling: true,
|
|
170
|
-
json_mode: true,
|
|
171
|
-
multimodal: true,
|
|
172
|
-
},
|
|
173
134
|
},
|
|
174
135
|
{
|
|
175
136
|
name: 'claude-3-5-haiku-20241022',
|
|
176
137
|
id: 'claude-3-5-haiku-20241022',
|
|
177
138
|
description: 'Claude 3.5 Haiku - Fast and efficient',
|
|
178
|
-
contextWindow: 200000,
|
|
179
|
-
capabilities: {
|
|
180
|
-
reasoning: true,
|
|
181
|
-
function_calling: true,
|
|
182
|
-
json_mode: true,
|
|
183
|
-
multimodal: true,
|
|
184
|
-
},
|
|
185
139
|
},
|
|
186
140
|
]
|
|
187
141
|
}
|
|
188
142
|
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
const models = await this.getAvailableModels()
|
|
192
|
-
const model = models.find((m) => m.name === modelName)
|
|
193
|
-
|
|
194
|
-
if (model) {
|
|
195
|
-
return {
|
|
196
|
-
available: true,
|
|
197
|
-
model: modelName,
|
|
198
|
-
capabilities: model.capabilities,
|
|
199
|
-
contextWindow: model.contextWindow,
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
const availableModels = models.map((m) => m.name)
|
|
203
|
-
return {
|
|
204
|
-
available: false,
|
|
205
|
-
error: `Model '${modelName}' not available`,
|
|
206
|
-
alternatives: availableModels.slice(0, 5),
|
|
207
|
-
}
|
|
208
|
-
} catch (error) {
|
|
209
|
-
return {
|
|
210
|
-
available: false,
|
|
211
|
-
error: error.message,
|
|
212
|
-
alternatives: [
|
|
213
|
-
'claude-sonnet-4-20250514',
|
|
214
|
-
'claude-opus-4-20250514',
|
|
215
|
-
'claude-3.7-sonnet-20250219',
|
|
216
|
-
],
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
async testConnection() {
|
|
222
|
-
try {
|
|
223
|
-
const response = await this.generateCompletion([{ role: 'user', content: 'Hello' }], {
|
|
224
|
-
max_tokens: 5,
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
success: true,
|
|
229
|
-
model: response.model,
|
|
230
|
-
message: 'Connection successful',
|
|
231
|
-
}
|
|
232
|
-
} catch (error) {
|
|
233
|
-
return {
|
|
234
|
-
success: false,
|
|
235
|
-
error: error.message,
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
143
|
+
// testConnection() and validateModelAvailability() now provided by mixins
|
|
239
144
|
|
|
240
145
|
async testModel(modelName) {
|
|
241
146
|
try {
|
|
@@ -258,73 +163,11 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
258
163
|
}
|
|
259
164
|
}
|
|
260
165
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
json_mode: true,
|
|
267
|
-
reasoning: true,
|
|
268
|
-
multimodal: true,
|
|
269
|
-
large_context: true,
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
getModelRecommendation(commitDetails) {
|
|
274
|
-
const { files = 0, lines = 0, breaking = false, complex = false } = commitDetails
|
|
275
|
-
|
|
276
|
-
// Use the most capable model for complex or breaking changes
|
|
277
|
-
if (breaking || complex || files > 20 || lines > 1000) {
|
|
278
|
-
return {
|
|
279
|
-
model: 'claude-3-5-sonnet-20241022',
|
|
280
|
-
reason: 'Complex or breaking change requiring advanced reasoning',
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Use standard model for medium changes
|
|
285
|
-
if (files > 5 || lines > 200) {
|
|
286
|
-
return {
|
|
287
|
-
model: 'claude-3-sonnet-20240229',
|
|
288
|
-
reason: 'Medium-sized change requiring good analysis',
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Use efficient model for small changes
|
|
293
|
-
return {
|
|
294
|
-
model: 'claude-3-haiku-20240307',
|
|
295
|
-
reason: 'Small change, optimized for efficiency',
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
async selectOptimalModel(commitInfo) {
|
|
300
|
-
const recommendation = this.getModelRecommendation(commitInfo)
|
|
301
|
-
const validation = await this.validateModelAvailability(recommendation.model)
|
|
302
|
-
|
|
303
|
-
if (validation.available) {
|
|
304
|
-
return {
|
|
305
|
-
model: recommendation.model,
|
|
306
|
-
reason: recommendation.reason,
|
|
307
|
-
capabilities: validation.capabilities,
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return {
|
|
311
|
-
model: this.getDefaultModel(),
|
|
312
|
-
reason: 'Fallback to default model',
|
|
313
|
-
capabilities: this.getCapabilities(this.getDefaultModel()),
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
getProviderModelConfig() {
|
|
318
|
-
return {
|
|
319
|
-
smallModel: 'claude-3-5-haiku-20241022',
|
|
320
|
-
mediumModel: 'claude-3.7-sonnet-20250219',
|
|
321
|
-
standardModel: 'claude-sonnet-4-20250514',
|
|
322
|
-
complexModel: 'claude-opus-4-20250514',
|
|
323
|
-
default: 'claude-sonnet-4-20250514',
|
|
324
|
-
temperature: 0.3,
|
|
325
|
-
maxTokens: 4096,
|
|
326
|
-
}
|
|
327
|
-
}
|
|
166
|
+
// All common methods now provided by mixins:
|
|
167
|
+
// - getCapabilities() from CapabilitiesMixin
|
|
168
|
+
// - getModelRecommendation() from ModelRecommendationMixin
|
|
169
|
+
// - selectOptimalModel() from ModelRecommendationMixin
|
|
170
|
+
// - getProviderModelConfig() from ConfigurationMixin
|
|
328
171
|
}
|
|
329
172
|
|
|
330
173
|
// Apply mixins to add standard provider functionality
|
|
@@ -3,6 +3,7 @@ import { AzureOpenAI } from 'openai'
|
|
|
3
3
|
|
|
4
4
|
import { BaseProvider } from '../core/base-provider.js'
|
|
5
5
|
import { applyMixins, ProviderResponseHandler } from '../utils/base-provider-helpers.js'
|
|
6
|
+
import { getProviderModelConfig } from '../utils/model-config.js'
|
|
6
7
|
|
|
7
8
|
class AzureOpenAIProvider extends BaseProvider {
|
|
8
9
|
constructor(config) {
|
|
@@ -168,59 +169,11 @@ class AzureOpenAIProvider extends BaseProvider {
|
|
|
168
169
|
|
|
169
170
|
// Azure-specific helper methods
|
|
170
171
|
getDeploymentName() {
|
|
171
|
-
|
|
172
|
+
const modelConfig = this.getProviderModelConfig()
|
|
173
|
+
return this.config.AZURE_OPENAI_DEPLOYMENT_NAME || modelConfig.standardModel
|
|
172
174
|
}
|
|
173
175
|
|
|
174
|
-
|
|
175
|
-
const contextWindows = {
|
|
176
|
-
// Latest 2025 models (Azure exclusive)
|
|
177
|
-
o4: 500000,
|
|
178
|
-
'o4-mini': 200000,
|
|
179
|
-
o3: 300000,
|
|
180
|
-
'o3-mini': 150000,
|
|
181
|
-
// Standard 2025 models
|
|
182
|
-
'gpt-4o': 128000,
|
|
183
|
-
'gpt-4o-mini': 128000,
|
|
184
|
-
o1: 200000,
|
|
185
|
-
'o1-mini': 128000,
|
|
186
|
-
'gpt-4.1': 200000,
|
|
187
|
-
'gpt-4.1-mini': 200000,
|
|
188
|
-
'gpt-4.1-nano': 200000,
|
|
189
|
-
// Legacy models
|
|
190
|
-
'gpt-4': 8192,
|
|
191
|
-
'gpt-4-32k': 32768,
|
|
192
|
-
'gpt-4-turbo': 128000,
|
|
193
|
-
'gpt-35-turbo': 4096,
|
|
194
|
-
'gpt-35-turbo-16k': 16384,
|
|
195
|
-
}
|
|
196
|
-
return contextWindows[modelName] || 128000
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
getModelCapabilities(modelName) {
|
|
200
|
-
return {
|
|
201
|
-
reasoning:
|
|
202
|
-
modelName.includes('o1') ||
|
|
203
|
-
modelName.includes('o3') ||
|
|
204
|
-
modelName.includes('o4') ||
|
|
205
|
-
modelName.includes('gpt-4'),
|
|
206
|
-
function_calling: !(
|
|
207
|
-
modelName.includes('o1') ||
|
|
208
|
-
modelName.includes('o3') ||
|
|
209
|
-
modelName.includes('o4')
|
|
210
|
-
), // o-series models don't support function calling
|
|
211
|
-
json_mode: true,
|
|
212
|
-
multimodal: modelName.includes('gpt-4o') || modelName.includes('gpt-4.1'),
|
|
213
|
-
largeContext:
|
|
214
|
-
modelName.includes('4.1') ||
|
|
215
|
-
modelName.includes('o1') ||
|
|
216
|
-
modelName.includes('o3') ||
|
|
217
|
-
modelName.includes('o4') ||
|
|
218
|
-
modelName.includes('4o'),
|
|
219
|
-
promptCaching: modelName.includes('4.1'),
|
|
220
|
-
advancedReasoning: modelName.includes('o3') || modelName.includes('o4'),
|
|
221
|
-
azureExclusive: modelName.includes('o3') || modelName.includes('o4'),
|
|
222
|
-
}
|
|
223
|
-
}
|
|
176
|
+
// Model capabilities now handled by centralized CapabilitiesMixin
|
|
224
177
|
|
|
225
178
|
// Azure-specific method for testing deployment availability
|
|
226
179
|
async testDeployment(deploymentName) {
|
|
@@ -257,19 +210,19 @@ class AzureOpenAIProvider extends BaseProvider {
|
|
|
257
210
|
return this._cachedDeployments
|
|
258
211
|
}
|
|
259
212
|
|
|
260
|
-
// Get
|
|
261
|
-
const
|
|
262
|
-
commonDeployments: ['o4', 'o3', 'gpt-4.1', 'gpt-4o', 'gpt-35-turbo', 'o1'],
|
|
263
|
-
fallbacks: ['gpt-4.1', 'gpt-4o', 'o1', 'gpt-35-turbo'],
|
|
264
|
-
}
|
|
213
|
+
// Get model config from centralized system (avoiding circular dependency)
|
|
214
|
+
const modelConfig = getProviderModelConfig('azure', this.config)
|
|
265
215
|
|
|
266
216
|
const potentialDeployments = [
|
|
267
217
|
// User configured deployment
|
|
268
218
|
this.config.AZURE_OPENAI_DEPLOYMENT_NAME,
|
|
269
|
-
//
|
|
270
|
-
|
|
219
|
+
// Models from config
|
|
220
|
+
modelConfig.complexModel,
|
|
221
|
+
modelConfig.standardModel,
|
|
222
|
+
modelConfig.mediumModel,
|
|
223
|
+
modelConfig.smallModel,
|
|
271
224
|
// Fallback models
|
|
272
|
-
...
|
|
225
|
+
...modelConfig.fallbacks,
|
|
273
226
|
]
|
|
274
227
|
.filter(Boolean)
|
|
275
228
|
.filter((v, i, a) => a.indexOf(v) === i) // Remove duplicates
|
|
@@ -294,16 +247,16 @@ class AzureOpenAIProvider extends BaseProvider {
|
|
|
294
247
|
|
|
295
248
|
if (availableDeployments.length === 0) {
|
|
296
249
|
console.warn('⚠️ No Azure deployments found. Using configured deployment name as fallback.')
|
|
297
|
-
// Fallback to configured deployment even if untested
|
|
298
|
-
const fallback = this.config.AZURE_OPENAI_DEPLOYMENT_NAME ||
|
|
250
|
+
// Fallback to configured deployment even if untested
|
|
251
|
+
const fallback = this.config.AZURE_OPENAI_DEPLOYMENT_NAME || modelConfig.standardModel
|
|
299
252
|
return [fallback]
|
|
300
253
|
}
|
|
301
254
|
|
|
302
255
|
return availableDeployments
|
|
303
256
|
} catch (error) {
|
|
304
257
|
console.warn('⚠️ Failed to detect Azure deployments:', error.message)
|
|
305
|
-
// Return
|
|
306
|
-
return
|
|
258
|
+
// Return fallback models from config
|
|
259
|
+
return modelConfig.fallbacks
|
|
307
260
|
}
|
|
308
261
|
}
|
|
309
262
|
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* Fallback provider when no other providers are available
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ProviderError } from '../../../shared/utils/
|
|
6
|
+
import { ProviderError } from '../../../shared/utils/error-classes.js'
|
|
7
7
|
import { BaseProvider } from '../core/base-provider.js'
|
|
8
|
+
import { applyMixins } from '../utils/base-provider-helpers.js'
|
|
8
9
|
|
|
9
10
|
class DummyProvider extends BaseProvider {
|
|
10
11
|
constructor(config = {}) {
|
|
@@ -12,22 +13,22 @@ class DummyProvider extends BaseProvider {
|
|
|
12
13
|
this.name = 'dummy'
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
/**
|
|
16
|
-
* Get provider name
|
|
17
|
-
* @returns {string} Provider name
|
|
18
|
-
*/
|
|
19
16
|
getName() {
|
|
20
17
|
return this.name
|
|
21
18
|
}
|
|
22
19
|
|
|
23
|
-
/**
|
|
24
|
-
* Check if provider is available
|
|
25
|
-
* @returns {boolean} Always true for dummy provider
|
|
26
|
-
*/
|
|
27
20
|
isAvailable() {
|
|
28
21
|
return true
|
|
29
22
|
}
|
|
30
23
|
|
|
24
|
+
getRequiredEnvVars() {
|
|
25
|
+
return []
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getDefaultModel() {
|
|
29
|
+
return 'rule-based'
|
|
30
|
+
}
|
|
31
|
+
|
|
31
32
|
/**
|
|
32
33
|
* Generate completion (always fails with informative error)
|
|
33
34
|
* @param {Array} messages - Messages for completion
|
|
@@ -93,20 +94,16 @@ class DummyProvider extends BaseProvider {
|
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
getAvailableModels() {
|
|
97
|
+
async getAvailableModels() {
|
|
97
98
|
return [
|
|
98
99
|
{
|
|
99
100
|
id: 'rule-based',
|
|
100
101
|
name: 'Rule-based Fallback',
|
|
101
|
-
contextWindow: 0,
|
|
102
|
-
maxOutput: 0,
|
|
103
|
-
inputCost: 0,
|
|
104
|
-
outputCost: 0,
|
|
105
|
-
features: [],
|
|
106
102
|
description: 'No AI model available - configure a provider',
|
|
107
103
|
},
|
|
108
104
|
]
|
|
109
105
|
}
|
|
110
106
|
}
|
|
111
107
|
|
|
112
|
-
|
|
108
|
+
// Apply minimal mixins (error handling only - dummy provider doesn't need full functionality)
|
|
109
|
+
export default applyMixins ? applyMixins(DummyProvider, 'dummy', []) : DummyProvider
|