@theia/ai-google 1.76.0-next.28 → 1.76.0-next.34
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/lib/browser/google-frontend-application-contribution.d.ts +13 -10
- package/lib/browser/google-frontend-application-contribution.d.ts.map +1 -1
- package/lib/browser/google-frontend-application-contribution.js +36 -50
- package/lib/browser/google-frontend-application-contribution.js.map +1 -1
- package/lib/common/google-language-models-manager.d.ts +15 -1
- package/lib/common/google-language-models-manager.d.ts.map +1 -1
- package/lib/common/google-preferences.d.ts +2 -1
- package/lib/common/google-preferences.d.ts.map +1 -1
- package/lib/common/google-preferences.js +16 -7
- package/lib/common/google-preferences.js.map +1 -1
- package/lib/node/google-language-model.d.ts +9 -0
- package/lib/node/google-language-model.d.ts.map +1 -1
- package/lib/node/google-language-model.js +8 -4
- package/lib/node/google-language-model.js.map +1 -1
- package/lib/node/google-language-models-manager-impl.d.ts +43 -1
- package/lib/node/google-language-models-manager-impl.d.ts.map +1 -1
- package/lib/node/google-language-models-manager-impl.js +97 -3
- package/lib/node/google-language-models-manager-impl.js.map +1 -1
- package/lib/node/google-language-models-manager-impl.spec.js +162 -0
- package/lib/node/google-language-models-manager-impl.spec.js.map +1 -1
- package/package.json +4 -4
- package/src/browser/google-frontend-application-contribution.ts +40 -55
- package/src/common/google-language-models-manager.ts +16 -2
- package/src/common/google-preferences.ts +17 -6
- package/src/node/google-language-model.ts +15 -2
- package/src/node/google-language-models-manager-impl.spec.ts +164 -1
- package/src/node/google-language-models-manager-impl.ts +109 -5
|
@@ -156,6 +156,20 @@ function toGoogleRole(message: LanguageModelMessage): 'user' | 'model' {
|
|
|
156
156
|
* Implements the Gemini language model integration for Theia. Reasoning-level
|
|
157
157
|
* translation lives in {@link googleReasoningFor}.
|
|
158
158
|
*/
|
|
159
|
+
/** Options for {@link createGoogleClient}. */
|
|
160
|
+
export interface GoogleClientOptions {
|
|
161
|
+
readonly apiKey: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The single place a Gemini SDK client is built, so that a chat request, a model lookup and the model
|
|
166
|
+
* discovery all reach the provider the same way.
|
|
167
|
+
*/
|
|
168
|
+
export function createGoogleClient(options: GoogleClientOptions): GoogleGenAI {
|
|
169
|
+
// TODO test vertexai
|
|
170
|
+
return new GoogleGenAI({ apiKey: options.apiKey, vertexai: false });
|
|
171
|
+
}
|
|
172
|
+
|
|
159
173
|
export class GoogleModel implements LanguageModel {
|
|
160
174
|
|
|
161
175
|
/** Provider identifier, used to key per-provider settings (e.g. server tool selections) and the capabilities UI. */
|
|
@@ -580,8 +594,7 @@ export class GoogleModel implements LanguageModel {
|
|
|
580
594
|
throw new Error('Please provide GOOGLE_API_KEY in preferences or via environment variable');
|
|
581
595
|
}
|
|
582
596
|
|
|
583
|
-
|
|
584
|
-
return new GoogleGenAI({ apiKey, vertexai: false });
|
|
597
|
+
return createGoogleClient({ apiKey });
|
|
585
598
|
}
|
|
586
599
|
|
|
587
600
|
/**
|
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
|
|
17
17
|
import { expect } from 'chai';
|
|
18
18
|
import type { Model } from '@google/genai';
|
|
19
|
-
import { ReasoningApi } from '@theia/ai-core';
|
|
19
|
+
import { DiscoveredModel, ReasoningApi } from '@theia/ai-core';
|
|
20
20
|
import { GoogleLanguageModelsManagerImpl, reasoningApiFromModelId } from './google-language-models-manager-impl';
|
|
21
21
|
import { GoogleModelDescription } from '../common';
|
|
22
22
|
import { MockLogger } from '@theia/core/lib/common/test/mock-logger';
|
|
23
|
+
import { TestModelDiscoveryFetcher } from '@theia/ai-core/lib/node/test/test-model-discovery-fetcher';
|
|
23
24
|
|
|
24
25
|
class TestableGoogleManager extends GoogleLanguageModelsManagerImpl {
|
|
25
26
|
public retrieveCalls: string[] = [];
|
|
@@ -46,6 +47,36 @@ class TestableGoogleManager extends GoogleLanguageModelsManagerImpl {
|
|
|
46
47
|
}
|
|
47
48
|
return this.stubbedInfo;
|
|
48
49
|
}
|
|
50
|
+
|
|
51
|
+
public stubbedModels: Model[] = [];
|
|
52
|
+
public listCalls = 0;
|
|
53
|
+
/** Throw {@link failWith} for the first `failTimes` list calls, then return {@link stubbedModels}. */
|
|
54
|
+
public failTimes = 0;
|
|
55
|
+
public failWith: Error = new Error('boom');
|
|
56
|
+
/** The real discovery fetcher, snapshotting in memory and retrying without waiting. */
|
|
57
|
+
public readonly testFetcher = new TestModelDiscoveryFetcher();
|
|
58
|
+
protected override readonly discoveryFetcher = this.testFetcher;
|
|
59
|
+
|
|
60
|
+
/** In-memory stand-in for the on-disk snapshot. */
|
|
61
|
+
public get snapshot(): DiscoveredModel[] | undefined {
|
|
62
|
+
return this.testFetcher.snapshot;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public set snapshot(models: DiscoveredModel[] | undefined) {
|
|
66
|
+
this.testFetcher.snapshot = models;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected override async listModels(_apiKey: string): Promise<Model[]> {
|
|
70
|
+
this.listCalls++;
|
|
71
|
+
if (this.listCalls <= this.failTimes) {
|
|
72
|
+
throw this.failWith;
|
|
73
|
+
}
|
|
74
|
+
return this.stubbedModels;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function listedModel(name: string, supportedActions?: string[], displayName?: string, modelDescription?: string): Model {
|
|
79
|
+
return ({ name, supportedActions, displayName, description: modelDescription }) as unknown as Model;
|
|
49
80
|
}
|
|
50
81
|
|
|
51
82
|
function description(model: string): GoogleModelDescription {
|
|
@@ -184,3 +215,135 @@ describe('GoogleLanguageModelsManagerImpl - fetchModelInfo cache', () => {
|
|
|
184
215
|
expect(manager.retrieveCalls).to.deep.equal(['gemini-3-pro']);
|
|
185
216
|
});
|
|
186
217
|
});
|
|
218
|
+
|
|
219
|
+
describe('GoogleLanguageModelsManagerImpl - fetchAvailableModels', () => {
|
|
220
|
+
let manager: TestableGoogleManager;
|
|
221
|
+
|
|
222
|
+
beforeEach(() => {
|
|
223
|
+
manager = new TestableGoogleManager();
|
|
224
|
+
(manager as unknown as { logger: MockLogger }).logger = new MockLogger();
|
|
225
|
+
manager.setApiKey('key');
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('returns an empty result and skips the network when no API key is set', async () => {
|
|
229
|
+
const previous = { google: process.env.GOOGLE_API_KEY, gemini: process.env.GEMINI_API_KEY };
|
|
230
|
+
delete process.env.GOOGLE_API_KEY;
|
|
231
|
+
delete process.env.GEMINI_API_KEY;
|
|
232
|
+
try {
|
|
233
|
+
manager.setApiKey(undefined);
|
|
234
|
+
expect(await manager.fetchAvailableModels()).to.deep.equal({ models: [], fromCache: false });
|
|
235
|
+
expect(manager.listCalls).to.equal(0);
|
|
236
|
+
} finally {
|
|
237
|
+
if (previous.google !== undefined) { process.env.GOOGLE_API_KEY = previous.google; }
|
|
238
|
+
if (previous.gemini !== undefined) { process.env.GEMINI_API_KEY = previous.gemini; }
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('strips the "models/" prefix, drops models that cannot generate content, and caches the result', async () => {
|
|
243
|
+
manager.stubbedModels = [
|
|
244
|
+
listedModel('models/gemini-3-pro', ['generateContent', 'countTokens'], 'Gemini 3 Pro', 'The best one.'),
|
|
245
|
+
listedModel('models/text-embedding-004', ['embedContent']),
|
|
246
|
+
// Older SDKs omit the field; such models are kept rather than silently dropped.
|
|
247
|
+
listedModel('models/gemini-3-flash'),
|
|
248
|
+
listedModel('models/gemini-3-pro', ['generateContent'])
|
|
249
|
+
];
|
|
250
|
+
const result = await manager.fetchAvailableModels();
|
|
251
|
+
expect(result.fromCache).to.equal(false);
|
|
252
|
+
expect(result.models.map(model => model.id)).to.deep.equal(['gemini-3-pro', 'gemini-3-flash']);
|
|
253
|
+
expect(result.models[0].label).to.equal('Gemini 3 Pro');
|
|
254
|
+
expect(result.models[0].description).to.equal('The best one.');
|
|
255
|
+
expect(manager.snapshot).to.deep.equal(result.models);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('keeps the Gemini chat models and drops what the endpoint otherwise carries', async () => {
|
|
259
|
+
manager.stubbedModels = [
|
|
260
|
+
listedModel('models/gemini-flash-latest'),
|
|
261
|
+
listedModel('models/gemini-3.8-flash'),
|
|
262
|
+
listedModel('models/gemini-3.1-pro-preview'),
|
|
263
|
+
// Chat-capable, but not a model of this family: its parameter count would read as a version.
|
|
264
|
+
listedModel('models/gemma-3-27b-it'),
|
|
265
|
+
listedModel('models/learnlm-2.0-experimental'),
|
|
266
|
+
// Gemini, and reported as generating content, but not chat: they answer through an API of
|
|
267
|
+
// their own, or they generate something other than text.
|
|
268
|
+
listedModel('models/gemini-3.5-pro-deep-research'),
|
|
269
|
+
listedModel('models/gemini-2.5-flash-preview-tts'),
|
|
270
|
+
listedModel('models/gemini-2.5-flash-image'),
|
|
271
|
+
listedModel('models/gemini-2.5-computer-use-preview'),
|
|
272
|
+
// The Live API and embedding models need no id term: they report what they can do.
|
|
273
|
+
listedModel('models/gemini-2.5-flash-live', ['bidiGenerateContent']),
|
|
274
|
+
listedModel('models/gemini-embedding-001', ['embedContent']),
|
|
275
|
+
listedModel('models/imagen-4.0-generate-001')
|
|
276
|
+
];
|
|
277
|
+
const result = await manager.fetchAvailableModels();
|
|
278
|
+
expect(result.models.map(model => model.id)).to.deep.equal([
|
|
279
|
+
'gemini-flash-latest',
|
|
280
|
+
'gemini-3.8-flash',
|
|
281
|
+
'gemini-3.1-pro-preview'
|
|
282
|
+
]);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('retries transient network errors and then succeeds', async () => {
|
|
286
|
+
manager.stubbedModels = [listedModel('models/gemini-3-pro')];
|
|
287
|
+
manager.failTimes = 2;
|
|
288
|
+
manager.failWith = new Error('fetch failed');
|
|
289
|
+
const result = await manager.fetchAvailableModels();
|
|
290
|
+
expect(result.models.map(model => model.id)).to.deep.equal(['gemini-3-pro']);
|
|
291
|
+
expect(manager.listCalls).to.equal(3);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('falls back to the cached snapshot when the fetch keeps failing', async () => {
|
|
295
|
+
manager.snapshot = [{ id: 'gemini-3-pro' }];
|
|
296
|
+
manager.failTimes = 99;
|
|
297
|
+
manager.failWith = new Error('ETIMEDOUT');
|
|
298
|
+
const result = await manager.fetchAvailableModels();
|
|
299
|
+
expect(result).to.deep.equal({ models: [{ id: 'gemini-3-pro' }], fromCache: true, error: 'ETIMEDOUT' });
|
|
300
|
+
expect(manager.listCalls).to.equal(3);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('does not retry auth errors and throws without a snapshot', async () => {
|
|
304
|
+
manager.failTimes = 99;
|
|
305
|
+
manager.failWith = new Error('403 permission denied');
|
|
306
|
+
let threw = false;
|
|
307
|
+
try {
|
|
308
|
+
await manager.fetchAvailableModels();
|
|
309
|
+
} catch (error) {
|
|
310
|
+
threw = true;
|
|
311
|
+
expect((error as Error).message).to.equal('403 permission denied');
|
|
312
|
+
}
|
|
313
|
+
expect(threw).to.be.true;
|
|
314
|
+
expect(manager.listCalls).to.equal(1);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('GoogleLanguageModelsManagerImpl - environment API key consent', () => {
|
|
319
|
+
|
|
320
|
+
it('leaves an environment key unused until it is allowed, wherever a key would be read', async () => {
|
|
321
|
+
const previous = { GOOGLE_API_KEY: process.env.GOOGLE_API_KEY, GEMINI_API_KEY: process.env.GEMINI_API_KEY };
|
|
322
|
+
delete process.env.GOOGLE_API_KEY;
|
|
323
|
+
delete process.env.GEMINI_API_KEY;
|
|
324
|
+
process.env.GOOGLE_API_KEY = 'from-the-environment';
|
|
325
|
+
try {
|
|
326
|
+
const manager = new TestableGoogleManager();
|
|
327
|
+
// The gate sits on the key, so a custom endpoint or a configured model cannot reach past it either.
|
|
328
|
+
expect(manager.apiKey).to.equal(undefined);
|
|
329
|
+
// Discovery still needs to know the key is there, in order to ask for it.
|
|
330
|
+
expect(await manager.getApiKeySource()).to.equal('environment');
|
|
331
|
+
|
|
332
|
+
manager.setAllowEnvironmentApiKey(true);
|
|
333
|
+
expect(manager.apiKey).to.equal('from-the-environment');
|
|
334
|
+
|
|
335
|
+
// Withdrawing the consent stops it being used at once.
|
|
336
|
+
manager.setAllowEnvironmentApiKey(false);
|
|
337
|
+
expect(manager.apiKey).to.equal(undefined);
|
|
338
|
+
} finally {
|
|
339
|
+
if (previous.GOOGLE_API_KEY !== undefined) { process.env.GOOGLE_API_KEY = previous.GOOGLE_API_KEY; } else { delete process.env.GOOGLE_API_KEY; }
|
|
340
|
+
if (previous.GEMINI_API_KEY !== undefined) { process.env.GEMINI_API_KEY = previous.GEMINI_API_KEY; } else { delete process.env.GEMINI_API_KEY; }
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('uses a key set in the preferences whatever the environment says', () => {
|
|
345
|
+
const manager = new TestableGoogleManager();
|
|
346
|
+
manager.setApiKey('from-the-preference');
|
|
347
|
+
expect(manager.apiKey).to.equal('from-the-preference');
|
|
348
|
+
});
|
|
349
|
+
});
|
|
@@ -14,14 +14,19 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
ApiKeySource, DiscoveredModel, DiscoveredModels, LanguageModelRegistry, LanguageModelStatus, ModelDiscoveryResult, ReasoningApi, ReasoningSupport
|
|
19
|
+
} from '@theia/ai-core';
|
|
20
|
+
import { ModelDiscoveryFetcher } from '@theia/ai-core/lib/node';
|
|
18
21
|
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
|
19
|
-
import {
|
|
20
|
-
import { GoogleModel } from './google-language-model';
|
|
22
|
+
import { Model } from '@google/genai';
|
|
23
|
+
import { createGoogleClient, GoogleModel } from './google-language-model';
|
|
21
24
|
import { GOOGLE_SERVER_TOOLS } from './google-server-tools';
|
|
22
25
|
import { GoogleLanguageModelsManager, GoogleModelDescription } from '../common';
|
|
23
26
|
import { ILogger } from '@theia/core';
|
|
24
27
|
|
|
28
|
+
const GOOGLE_SNAPSHOT_FILE = 'google-models.json';
|
|
29
|
+
|
|
25
30
|
export interface GoogleLanguageModelRetrySettings {
|
|
26
31
|
maxRetriesOnErrors: number;
|
|
27
32
|
retryDelayOnRateLimitError: number;
|
|
@@ -56,6 +61,12 @@ interface ResolvedModelMetadata {
|
|
|
56
61
|
@injectable()
|
|
57
62
|
export class GoogleLanguageModelsManagerImpl implements GoogleLanguageModelsManager {
|
|
58
63
|
protected _apiKey: string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Whether a key found in the environment may be used. Withheld until the user confirms it, so the
|
|
66
|
+
* gate sits on the key itself: every path that reaches for one — discovery, a custom endpoint, a
|
|
67
|
+
* manually configured model — is covered, and revoking the consent takes effect at once.
|
|
68
|
+
*/
|
|
69
|
+
protected _allowEnvironmentApiKey = false;
|
|
59
70
|
protected retrySettings: GoogleLanguageModelRetrySettings = {
|
|
60
71
|
maxRetriesOnErrors: 3,
|
|
61
72
|
retryDelayOnRateLimitError: 60,
|
|
@@ -71,8 +82,97 @@ export class GoogleLanguageModelsManagerImpl implements GoogleLanguageModelsMana
|
|
|
71
82
|
@inject(ILogger) @named('ai-google:GoogleLanguageModelsManagerImpl')
|
|
72
83
|
protected readonly logger: ILogger;
|
|
73
84
|
|
|
85
|
+
@inject(ModelDiscoveryFetcher)
|
|
86
|
+
protected readonly discoveryFetcher: ModelDiscoveryFetcher;
|
|
87
|
+
|
|
74
88
|
get apiKey(): string | undefined {
|
|
75
|
-
return this._apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
|
|
89
|
+
return this._apiKey ?? (this._allowEnvironmentApiKey ? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY : undefined);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async getApiKeySource(): Promise<ApiKeySource> {
|
|
93
|
+
if (this._apiKey) {
|
|
94
|
+
return 'preference';
|
|
95
|
+
}
|
|
96
|
+
if (process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY) {
|
|
97
|
+
return 'environment';
|
|
98
|
+
}
|
|
99
|
+
return 'none';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async fetchAvailableModels(): Promise<ModelDiscoveryResult> {
|
|
103
|
+
const apiKey = this.apiKey;
|
|
104
|
+
if (!apiKey) {
|
|
105
|
+
return { models: [], fromCache: false };
|
|
106
|
+
}
|
|
107
|
+
return this.discoveryFetcher.fetch({
|
|
108
|
+
snapshotFile: GOOGLE_SNAPSHOT_FILE,
|
|
109
|
+
providerLabel: 'Google',
|
|
110
|
+
listModels: async () => this.toDiscoveredModels(await this.listModels(apiKey)),
|
|
111
|
+
isRetryable: error => this.isRetryableError(error)
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Maps the endpoint's entries onto {@link DiscoveredModel}s. Gemini ids carry no release date
|
|
117
|
+
* today, so collapsing dated variants is a no-op here; it is applied all the same, so a provider
|
|
118
|
+
* that starts pinning releases does not quietly multiply the list.
|
|
119
|
+
*/
|
|
120
|
+
protected toDiscoveredModels(models: Model[]): DiscoveredModel[] {
|
|
121
|
+
const byId = new Map<string, DiscoveredModel>();
|
|
122
|
+
for (const model of models) {
|
|
123
|
+
// Only models usable for chat/content generation; older SDKs may omit the field.
|
|
124
|
+
if (model.supportedActions && !model.supportedActions.includes('generateContent')) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const id = (model.name ?? '').replace(/^models\//, '');
|
|
128
|
+
if (id.length > 0 && this.isChatModelId(id) && !byId.has(id)) {
|
|
129
|
+
byId.set(id, { id, label: model.displayName, description: model.description });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return DiscoveredModels.withUndatedAliases([...byId.values()]);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Heuristic for the Gemini chat models among the entries that are left once {@link toDiscoveredModels}
|
|
137
|
+
* has dropped what cannot generate content at all. Two things remain to be excluded, and both
|
|
138
|
+
* report `generateContent` like a chat model: a model that generates something other than text
|
|
139
|
+
* (an image, speech), and one that answers through an agentic API of its own (deep research,
|
|
140
|
+
* computer use, robotics). Everything outside the `gemini-*` family goes too — Gemma, LearnLM —
|
|
141
|
+
* since being able to generate content does not make a model one of the models this provider is
|
|
142
|
+
* about. Anything this misses can be configured as a custom endpoint.
|
|
143
|
+
*
|
|
144
|
+
* The Live API models need no term of their own: they report `bidiGenerateContent`, so the
|
|
145
|
+
* capability check has already left them out.
|
|
146
|
+
*
|
|
147
|
+
* Without a release date to go by, the ranking that decides which models the chat input offers
|
|
148
|
+
* falls back to the numbers in the id, and those only mean a version within this family: a
|
|
149
|
+
* parameter count (`gemma-3-27b-it`) or an experiment date would otherwise read as the newest
|
|
150
|
+
* model there is.
|
|
151
|
+
*/
|
|
152
|
+
protected isChatModelId(id: string): boolean {
|
|
153
|
+
if (!/^gemini-/.test(id)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
return !/(image|tts|deep-research|computer-use|robotics)/.test(id);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Retry transient network errors; auth/quota errors fail fast. The SDK reports them as plain
|
|
161
|
+
* errors, so there is nothing to match on but the message.
|
|
162
|
+
*/
|
|
163
|
+
protected isRetryableError(error: unknown): boolean {
|
|
164
|
+
const message = (error instanceof Error ? `${error.name} ${error.message}` : String(error)).toLowerCase();
|
|
165
|
+
return /econn|etimedout|enotfound|network|fetch failed|socket|timeout|aborted/.test(message);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Iterates the (auto-paginated) `/v1beta/models` endpoint. Overridable for testing. */
|
|
169
|
+
protected async listModels(apiKey: string): Promise<Model[]> {
|
|
170
|
+
const genAI = createGoogleClient({ apiKey });
|
|
171
|
+
const models: Model[] = [];
|
|
172
|
+
for await (const model of await genAI.models.list()) {
|
|
173
|
+
models.push(model);
|
|
174
|
+
}
|
|
175
|
+
return models;
|
|
76
176
|
}
|
|
77
177
|
|
|
78
178
|
protected calculateStatus(effectiveApiKey: string | undefined): LanguageModelStatus {
|
|
@@ -168,7 +268,7 @@ export class GoogleLanguageModelsManagerImpl implements GoogleLanguageModelsMana
|
|
|
168
268
|
}
|
|
169
269
|
|
|
170
270
|
protected retrieveModelInfo(modelDescription: GoogleModelDescription, apiKey: string): Promise<Model> {
|
|
171
|
-
const genAI =
|
|
271
|
+
const genAI = createGoogleClient({ apiKey });
|
|
172
272
|
return genAI.models.get({ model: modelDescription.model });
|
|
173
273
|
}
|
|
174
274
|
|
|
@@ -184,6 +284,10 @@ export class GoogleLanguageModelsManagerImpl implements GoogleLanguageModelsMana
|
|
|
184
284
|
this.languageModelRegistry.removeLanguageModels(modelIds);
|
|
185
285
|
}
|
|
186
286
|
|
|
287
|
+
setAllowEnvironmentApiKey(allowed: boolean): void {
|
|
288
|
+
this._allowEnvironmentApiKey = allowed;
|
|
289
|
+
}
|
|
290
|
+
|
|
187
291
|
setApiKey(apiKey: string | undefined): void {
|
|
188
292
|
if (apiKey) {
|
|
189
293
|
this._apiKey = apiKey;
|