@entro314labs/ai-changelog-generator 3.1.1 → 3.2.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/CHANGELOG.md +383 -877
- 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 +83 -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 +253 -193
- package/src/domains/changelog/changelog.service.js +1062 -784
- package/src/domains/changelog/workspace-changelog.service.js +420 -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
|
@@ -4,21 +4,21 @@
|
|
|
4
4
|
* Supports Gemini 2.5, 2.0 and 1.5 models
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { GoogleGenAI } from '@google/genai'
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
import { applyMixins } from '../utils/base-provider-helpers.js'
|
|
11
|
-
import { buildClientOptions } from '../utils/provider-utils.js'
|
|
7
|
+
import { GoogleGenAI } from '@google/genai'
|
|
8
|
+
|
|
9
|
+
import { BaseProvider } from '../core/base-provider.js'
|
|
10
|
+
import { applyMixins } from '../utils/base-provider-helpers.js'
|
|
11
|
+
import { buildClientOptions } from '../utils/provider-utils.js'
|
|
12
12
|
|
|
13
13
|
class GoogleProvider extends BaseProvider {
|
|
14
14
|
constructor(config) {
|
|
15
|
-
super(config)
|
|
16
|
-
this.genAI = null
|
|
17
|
-
this.modelCache = new Map()
|
|
18
|
-
this.maxCacheSize = 50
|
|
15
|
+
super(config)
|
|
16
|
+
this.genAI = null
|
|
17
|
+
this.modelCache = new Map()
|
|
18
|
+
this.maxCacheSize = 50 // Limit cache size to prevent memory leaks
|
|
19
19
|
|
|
20
20
|
if (this.isAvailable()) {
|
|
21
|
-
this.initializeClient()
|
|
21
|
+
this.initializeClient()
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -26,29 +26,31 @@ class GoogleProvider extends BaseProvider {
|
|
|
26
26
|
const clientOptions = buildClientOptions(this.getProviderConfig(), {
|
|
27
27
|
apiVersion: 'v1',
|
|
28
28
|
timeout: 60000,
|
|
29
|
-
maxRetries: 2
|
|
30
|
-
})
|
|
29
|
+
maxRetries: 2,
|
|
30
|
+
})
|
|
31
31
|
|
|
32
32
|
this.genAI = new GoogleGenAI({
|
|
33
33
|
apiKey: clientOptions.apiKey,
|
|
34
34
|
apiVersion: clientOptions.apiVersion,
|
|
35
35
|
apiEndpoint: clientOptions.apiEndpoint,
|
|
36
36
|
timeout: clientOptions.timeout,
|
|
37
|
-
retry: clientOptions.maxRetries
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
retry: clientOptions.maxRetries
|
|
38
|
+
? {
|
|
39
|
+
retries: clientOptions.maxRetries,
|
|
40
|
+
factor: 2,
|
|
41
|
+
minTimeout: 1000,
|
|
42
|
+
maxTimeout: 60000,
|
|
43
|
+
}
|
|
44
|
+
: undefined,
|
|
45
|
+
})
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
getName() {
|
|
47
|
-
return 'google'
|
|
49
|
+
return 'google'
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
isAvailable() {
|
|
51
|
-
return !!this.config.GOOGLE_API_KEY
|
|
53
|
+
return !!this.config.GOOGLE_API_KEY
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
async generateCompletion(messages, options = {}) {
|
|
@@ -56,43 +58,44 @@ class GoogleProvider extends BaseProvider {
|
|
|
56
58
|
return this.handleProviderError(
|
|
57
59
|
new Error('Google provider is not configured'),
|
|
58
60
|
'generate_completion'
|
|
59
|
-
)
|
|
61
|
+
)
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
try {
|
|
63
|
-
const modelConfig = this.getProviderModelConfig()
|
|
64
|
-
const modelName = options.model || modelConfig.standardModel
|
|
65
|
+
const modelConfig = this.getProviderModelConfig()
|
|
66
|
+
const modelName = options.model || modelConfig.standardModel
|
|
65
67
|
|
|
66
|
-
const systemInstruction = messages.find(m => m.role === 'system')?.content
|
|
68
|
+
const systemInstruction = messages.find((m) => m.role === 'system')?.content
|
|
67
69
|
|
|
68
70
|
// Separate system instruction from chat history
|
|
69
71
|
const history = messages
|
|
70
|
-
.filter(m => m.role !== 'system')
|
|
71
|
-
.map(m => {
|
|
72
|
-
const role = m.role === 'assistant' ? 'model' : 'user'
|
|
72
|
+
.filter((m) => m.role !== 'system')
|
|
73
|
+
.map((m) => {
|
|
74
|
+
const role = m.role === 'assistant' ? 'model' : 'user'
|
|
73
75
|
if (typeof m.content === 'string') {
|
|
74
|
-
return { role, parts: [{ text: m.content }] }
|
|
76
|
+
return { role, parts: [{ text: m.content }] }
|
|
75
77
|
}
|
|
76
78
|
if (Array.isArray(m.content)) {
|
|
77
|
-
const parts = m.content.map(part => {
|
|
79
|
+
const parts = m.content.map((part) => {
|
|
78
80
|
if (typeof part === 'string') {
|
|
79
|
-
return { text: part }
|
|
80
|
-
}
|
|
81
|
+
return { text: part }
|
|
82
|
+
}
|
|
83
|
+
if (part.type === 'image_url') {
|
|
81
84
|
return {
|
|
82
85
|
inlineData: {
|
|
83
86
|
mimeType: 'image/jpeg',
|
|
84
87
|
data: part.image_url.url.startsWith('data:image/')
|
|
85
88
|
? part.image_url.url.split(',')[1]
|
|
86
|
-
: Buffer.from(part.image_url.url).toString('base64')
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
+
: Buffer.from(part.image_url.url).toString('base64'),
|
|
90
|
+
},
|
|
91
|
+
}
|
|
89
92
|
}
|
|
90
|
-
return { text: JSON.stringify(part) }
|
|
91
|
-
})
|
|
92
|
-
return { role, parts }
|
|
93
|
+
return { text: JSON.stringify(part) }
|
|
94
|
+
})
|
|
95
|
+
return { role, parts }
|
|
93
96
|
}
|
|
94
|
-
return { role, parts: [{ text: JSON.stringify(m.content) }] }
|
|
95
|
-
})
|
|
97
|
+
return { role, parts: [{ text: JSON.stringify(m.content) }] }
|
|
98
|
+
})
|
|
96
99
|
|
|
97
100
|
const generationConfig = {
|
|
98
101
|
temperature: options.temperature || 0.4,
|
|
@@ -101,8 +104,9 @@ class GoogleProvider extends BaseProvider {
|
|
|
101
104
|
topK: options.top_k || 64,
|
|
102
105
|
candidateCount: options.n || 1,
|
|
103
106
|
stopSequences: options.stop || [],
|
|
104
|
-
responseMimeType:
|
|
105
|
-
|
|
107
|
+
responseMimeType:
|
|
108
|
+
options.response_format?.type === 'json_object' ? 'application/json' : undefined,
|
|
109
|
+
}
|
|
106
110
|
|
|
107
111
|
const safetySettings = [
|
|
108
112
|
{
|
|
@@ -120,59 +124,64 @@ class GoogleProvider extends BaseProvider {
|
|
|
120
124
|
{
|
|
121
125
|
category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
|
122
126
|
threshold: 'BLOCK_MEDIUM_AND_ABOVE',
|
|
123
|
-
}
|
|
124
|
-
]
|
|
127
|
+
},
|
|
128
|
+
]
|
|
125
129
|
|
|
126
130
|
const modelOptions = {
|
|
127
131
|
model: modelName,
|
|
128
132
|
generationConfig,
|
|
129
|
-
safetySettings
|
|
130
|
-
}
|
|
133
|
+
safetySettings,
|
|
134
|
+
}
|
|
131
135
|
|
|
132
136
|
if (systemInstruction) {
|
|
133
|
-
modelOptions.systemInstruction = systemInstruction
|
|
137
|
+
modelOptions.systemInstruction = systemInstruction
|
|
134
138
|
}
|
|
135
139
|
|
|
136
140
|
if (options.tools && options.tools.length > 0) {
|
|
137
141
|
try {
|
|
138
|
-
const tools = options.tools.map(tool => ({
|
|
139
|
-
functionDeclarations: [
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
const tools = options.tools.map((tool) => ({
|
|
143
|
+
functionDeclarations: [
|
|
144
|
+
{
|
|
145
|
+
name: tool.function.name,
|
|
146
|
+
description: tool.function.description,
|
|
147
|
+
parameters: tool.function.parameters,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
}))
|
|
151
|
+
|
|
152
|
+
const model = this.genAI.models.getModel(modelName)
|
|
147
153
|
const chat = model.startChat({
|
|
148
154
|
tools,
|
|
149
155
|
history: history.slice(0, -1),
|
|
150
|
-
systemInstruction: systemInstruction ? { text: systemInstruction } : undefined
|
|
151
|
-
})
|
|
156
|
+
systemInstruction: systemInstruction ? { text: systemInstruction } : undefined,
|
|
157
|
+
})
|
|
152
158
|
|
|
153
|
-
const lastMessage = history
|
|
154
|
-
const result = await chat.sendMessage(lastMessage.parts)
|
|
155
|
-
const response = result.response
|
|
159
|
+
const lastMessage = history.at(-1)
|
|
160
|
+
const result = await chat.sendMessage(lastMessage.parts)
|
|
161
|
+
const response = result.response
|
|
156
162
|
|
|
157
|
-
const functionCalls = response.functionCalls()
|
|
163
|
+
const functionCalls = response.functionCalls()
|
|
158
164
|
|
|
159
165
|
return {
|
|
160
166
|
content: response.text(),
|
|
161
167
|
model: modelName,
|
|
162
|
-
tool_calls:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
tool_calls:
|
|
169
|
+
functionCalls.length > 0
|
|
170
|
+
? functionCalls.map((call) => ({
|
|
171
|
+
id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
172
|
+
type: 'function',
|
|
173
|
+
function: {
|
|
174
|
+
name: call.name,
|
|
175
|
+
arguments: call.args ? JSON.stringify(call.args) : '{}',
|
|
176
|
+
},
|
|
177
|
+
}))
|
|
178
|
+
: undefined,
|
|
170
179
|
tokens: response.usageMetadata?.totalTokenCount || 0,
|
|
171
|
-
finish_reason: functionCalls.length > 0 ? 'tool_calls' : 'stop'
|
|
172
|
-
}
|
|
180
|
+
finish_reason: functionCalls.length > 0 ? 'tool_calls' : 'stop',
|
|
181
|
+
}
|
|
173
182
|
} catch (error) {
|
|
174
|
-
console.error('Error in tool calling:', error)
|
|
175
|
-
return this.handleProviderError(error, 'generate_completion', { model: options.model })
|
|
183
|
+
console.error('Error in tool calling:', error)
|
|
184
|
+
return this.handleProviderError(error, 'generate_completion', { model: options.model })
|
|
176
185
|
}
|
|
177
186
|
}
|
|
178
187
|
|
|
@@ -181,40 +190,40 @@ class GoogleProvider extends BaseProvider {
|
|
|
181
190
|
model: modelName,
|
|
182
191
|
generationConfig,
|
|
183
192
|
safetySettings,
|
|
184
|
-
systemInstruction: systemInstruction ? { text: systemInstruction } : undefined
|
|
185
|
-
})
|
|
193
|
+
systemInstruction: systemInstruction ? { text: systemInstruction } : undefined,
|
|
194
|
+
})
|
|
186
195
|
|
|
187
196
|
const result = await model.generateContent({
|
|
188
|
-
contents: history
|
|
189
|
-
})
|
|
197
|
+
contents: history,
|
|
198
|
+
})
|
|
190
199
|
|
|
191
|
-
const response = await result.response
|
|
200
|
+
const response = await result.response
|
|
192
201
|
|
|
193
202
|
return {
|
|
194
203
|
content: response.text(),
|
|
195
204
|
model: modelName,
|
|
196
205
|
tokens: response.usageMetadata?.totalTokenCount || 0,
|
|
197
|
-
finish_reason: response.candidates?.[0]?.finishReason || 'stop'
|
|
198
|
-
}
|
|
206
|
+
finish_reason: response.candidates?.[0]?.finishReason || 'stop',
|
|
207
|
+
}
|
|
199
208
|
} catch (error) {
|
|
200
209
|
// Handle rate limits and retries
|
|
201
210
|
if (error.message.includes('rate limit') || error.message.includes('quota')) {
|
|
202
|
-
const retryDelay = this.getRetryDelay()
|
|
203
|
-
console.warn(`Rate limit hit, retrying in ${retryDelay}ms...`)
|
|
204
|
-
await this.sleep(retryDelay)
|
|
205
|
-
return this.generateCompletion(messages, options)
|
|
211
|
+
const retryDelay = this.getRetryDelay()
|
|
212
|
+
console.warn(`Rate limit hit, retrying in ${retryDelay}ms...`)
|
|
213
|
+
await this.sleep(retryDelay)
|
|
214
|
+
return this.generateCompletion(messages, options)
|
|
206
215
|
}
|
|
207
|
-
return this.handleProviderError(error, 'generate_completion', { model: options.model })
|
|
216
|
+
return this.handleProviderError(error, 'generate_completion', { model: options.model })
|
|
208
217
|
}
|
|
209
218
|
}
|
|
210
219
|
|
|
211
220
|
// Google-specific helper methods
|
|
212
221
|
getRetryDelay() {
|
|
213
|
-
return Math.random() * 5000 + 1000
|
|
222
|
+
return Math.random() * 5000 + 1000 // Random delay between 1-6 seconds
|
|
214
223
|
}
|
|
215
224
|
|
|
216
225
|
sleep(ms) {
|
|
217
|
-
return new Promise(resolve => setTimeout(resolve, ms))
|
|
226
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
218
227
|
}
|
|
219
228
|
|
|
220
229
|
getAvailableModels() {
|
|
@@ -224,50 +233,50 @@ class GoogleProvider extends BaseProvider {
|
|
|
224
233
|
name: 'Gemini 2.5 Flash',
|
|
225
234
|
contextWindow: 1048576, // 1M tokens
|
|
226
235
|
maxOutput: 8192,
|
|
227
|
-
inputCost: 0.00000075,
|
|
228
|
-
outputCost: 0.
|
|
236
|
+
inputCost: 0.00000075, // $0.75 per 1M tokens
|
|
237
|
+
outputCost: 0.000003, // $3.00 per 1M tokens
|
|
229
238
|
features: ['text', 'vision', 'tools', 'multimodal', 'speed'],
|
|
230
|
-
description: 'Fastest Gemini 2.5 model for high-throughput tasks'
|
|
239
|
+
description: 'Fastest Gemini 2.5 model for high-throughput tasks',
|
|
231
240
|
},
|
|
232
241
|
{
|
|
233
242
|
id: 'gemini-2.5-pro',
|
|
234
243
|
name: 'Gemini 2.5 Pro',
|
|
235
244
|
contextWindow: 2097152, // 2M tokens
|
|
236
245
|
maxOutput: 8192,
|
|
237
|
-
inputCost: 0.00000125,
|
|
238
|
-
outputCost: 0.
|
|
246
|
+
inputCost: 0.00000125, // $1.25 per 1M tokens
|
|
247
|
+
outputCost: 0.000005, // $5.00 per 1M tokens
|
|
239
248
|
features: ['text', 'vision', 'tools', 'reasoning', 'thinking'],
|
|
240
|
-
description: 'Most capable Gemini model with thinking mode support'
|
|
249
|
+
description: 'Most capable Gemini model with thinking mode support',
|
|
241
250
|
},
|
|
242
251
|
{
|
|
243
252
|
id: 'gemini-2.0-flash-001',
|
|
244
253
|
name: 'Gemini 2.0 Flash',
|
|
245
254
|
contextWindow: 1048576, // 1M tokens
|
|
246
255
|
maxOutput: 8192,
|
|
247
|
-
inputCost: 0.00000075,
|
|
248
|
-
outputCost: 0.
|
|
256
|
+
inputCost: 0.00000075, // $0.75 per 1M tokens
|
|
257
|
+
outputCost: 0.000003, // $3.00 per 1M tokens
|
|
249
258
|
features: ['text', 'vision', 'tools', 'multimodal', 'functions'],
|
|
250
|
-
description: 'Multimodal capabilities with function calling'
|
|
259
|
+
description: 'Multimodal capabilities with function calling',
|
|
251
260
|
},
|
|
252
261
|
{
|
|
253
262
|
id: 'gemini-1.5-pro',
|
|
254
263
|
name: 'Gemini 1.5 Pro',
|
|
255
264
|
contextWindow: 2097152, // 2M tokens
|
|
256
265
|
maxOutput: 8192,
|
|
257
|
-
inputCost: 0.00000125,
|
|
258
|
-
outputCost: 0.
|
|
266
|
+
inputCost: 0.00000125, // $1.25 per 1M tokens
|
|
267
|
+
outputCost: 0.000005, // $5.00 per 1M tokens
|
|
259
268
|
features: ['text', 'vision', 'tools'],
|
|
260
|
-
description: 'High-intelligence model for complex reasoning'
|
|
269
|
+
description: 'High-intelligence model for complex reasoning',
|
|
261
270
|
},
|
|
262
271
|
{
|
|
263
272
|
id: 'gemini-1.5-flash',
|
|
264
273
|
name: 'Gemini 1.5 Flash',
|
|
265
274
|
contextWindow: 1048576, // 1M tokens
|
|
266
275
|
maxOutput: 8192,
|
|
267
|
-
inputCost: 0.00000075,
|
|
268
|
-
outputCost: 0.
|
|
276
|
+
inputCost: 0.00000075, // $0.75 per 1M tokens
|
|
277
|
+
outputCost: 0.000003, // $3.00 per 1M tokens
|
|
269
278
|
features: ['text', 'vision', 'tools'],
|
|
270
|
-
description: 'Fast and versatile performance across diverse tasks'
|
|
279
|
+
description: 'Fast and versatile performance across diverse tasks',
|
|
271
280
|
},
|
|
272
281
|
{
|
|
273
282
|
id: 'gemini-1.5-flash-8b',
|
|
@@ -275,19 +284,19 @@ class GoogleProvider extends BaseProvider {
|
|
|
275
284
|
contextWindow: 1048576, // 1M tokens
|
|
276
285
|
maxOutput: 8192,
|
|
277
286
|
inputCost: 0.000000375, // $0.375 per 1M tokens
|
|
278
|
-
outputCost: 0.
|
|
287
|
+
outputCost: 0.00000015, // $1.50 per 1M tokens
|
|
279
288
|
features: ['text', 'vision'],
|
|
280
|
-
description: 'High volume and lower intelligence tasks'
|
|
281
|
-
}
|
|
282
|
-
]
|
|
289
|
+
description: 'High volume and lower intelligence tasks',
|
|
290
|
+
},
|
|
291
|
+
]
|
|
283
292
|
}
|
|
284
293
|
|
|
285
294
|
getDefaultModel() {
|
|
286
|
-
return 'gemini-2.5-flash'
|
|
295
|
+
return 'gemini-2.5-flash'
|
|
287
296
|
}
|
|
288
297
|
|
|
289
298
|
getRequiredEnvVars() {
|
|
290
|
-
return ['GOOGLE_API_KEY']
|
|
299
|
+
return ['GOOGLE_API_KEY']
|
|
291
300
|
}
|
|
292
301
|
|
|
293
302
|
getProviderModelConfig() {
|
|
@@ -299,8 +308,8 @@ class GoogleProvider extends BaseProvider {
|
|
|
299
308
|
reasoningModel: 'gemini-2.5-pro',
|
|
300
309
|
default: 'gemini-2.5-flash',
|
|
301
310
|
temperature: 0.4,
|
|
302
|
-
maxTokens: 8192
|
|
303
|
-
}
|
|
311
|
+
maxTokens: 8192,
|
|
312
|
+
}
|
|
304
313
|
}
|
|
305
314
|
|
|
306
315
|
getModelCapabilities(modelName) {
|
|
@@ -311,10 +320,10 @@ class GoogleProvider extends BaseProvider {
|
|
|
311
320
|
multimodal: true,
|
|
312
321
|
largeContext: true,
|
|
313
322
|
thinking: modelName.includes('2.5-pro'),
|
|
314
|
-
speed: modelName.includes('flash')
|
|
315
|
-
}
|
|
323
|
+
speed: modelName.includes('flash'),
|
|
324
|
+
}
|
|
316
325
|
}
|
|
317
326
|
}
|
|
318
327
|
|
|
319
328
|
// Apply mixins to add standard provider functionality
|
|
320
|
-
export default applyMixins(GoogleProvider, 'google')
|
|
329
|
+
export default applyMixins(GoogleProvider, 'google')
|