Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions core/src/models/apigee_llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ export interface ApigeeLlmParams extends GeminiParams {
*/
proxyUrl?: string;
/**
* API key to use. If not provided, it will look for
* the GOOGLE_GENAI_API_KEY or GEMINI_API_KEY environment variable. If gemini
* provider is selected and no key is provided, the fake key "-" will be
* used for the "x-goog-api-key" header.
* API key to use. If not provided, it will look for the
* GOOGLE_GENAI_API_KEY, GOOGLE_API_KEY or GEMINI_API_KEY environment
* variable, in that order. If gemini provider is selected and no key is
* provided, the fake key "-" will be used for the "x-goog-api-key" header.
*/
apiKey?: string;
}
Expand Down
11 changes: 8 additions & 3 deletions core/src/models/google_llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export interface GeminiParams {
model?: string;
/**
* The API key to use for the Gemini API. If not provided, it will look for
* the GOOGLE_GENAI_API_KEY or GEMINI_API_KEY environment variable.
* the GOOGLE_GENAI_API_KEY, GOOGLE_API_KEY or GEMINI_API_KEY environment
* variable, in that order.
*/
apiKey?: string;
/**
Expand Down Expand Up @@ -117,7 +118,7 @@ export class Gemini extends BaseLlm {
});
if (!params.vertexai && !params.apiKey) {
throw new Error(
'API key must be provided via constructor or GOOGLE_GENAI_API_KEY or GEMINI_API_KEY environment variable.',
'API key must be provided via constructor or GOOGLE_GENAI_API_KEY, GOOGLE_API_KEY or GEMINI_API_KEY environment variable.',
);
}
this.project = params.project;
Expand Down Expand Up @@ -395,8 +396,12 @@ export function geminiInitParams({
}
} else {
if (!params.apiKey && !isBrowser()) {
// GOOGLE_API_KEY is what `adk create` writes and what @google/genai
// itself reads; it precedes GEMINI_API_KEY to match the SDK's ordering.
params.apiKey =
process.env['GOOGLE_GENAI_API_KEY'] || process.env['GEMINI_API_KEY'];
process.env['GOOGLE_GENAI_API_KEY'] ||
process.env['GOOGLE_API_KEY'] ||
process.env['GEMINI_API_KEY'];
}
}
return params;
Expand Down
80 changes: 79 additions & 1 deletion core/test/models/google_llm_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('GoogleLlm', () => {
delete process.env['GOOGLE_CLOUD_PROJECT'];
delete process.env['GOOGLE_CLOUD_LOCATION'];
delete process.env['GOOGLE_GENAI_API_KEY'];
delete process.env['GOOGLE_API_KEY'];
delete process.env['GEMINI_API_KEY'];
delete process.env['GOOGLE_GENAI_USE_VERTEXAI'];
delete process.env['GOOGLE_CLOUD_AGENT_ENGINE_ID'];
Expand All @@ -66,7 +67,21 @@ describe('GoogleLlm', () => {

it('should throw error if apiKey is missing in constructor', () => {
expect(() => new TestGemini({model: 'gemini-1.5-flash'})).toThrow(
/API key must be provided/,
/API key must be provided via constructor or GOOGLE_GENAI_API_KEY, GOOGLE_API_KEY or GEMINI_API_KEY environment variable\./,
);
});

it('should construct from the .env written by `adk create`', () => {
// The exact pair of lines generateEnvFile() emits for a Gemini API key in
// dev/src/cli/cli_create.ts.
process.env['GOOGLE_API_KEY'] = 'cli-key';
process.env['GOOGLE_GENAI_USE_VERTEXAI'] = '0';

const llm = new TestGemini({model: 'gemini-1.5-flash'});

expect(llm.apiClient.vertexai).toBe(false);
expect(vi.mocked(GoogleGenAI)).toHaveBeenLastCalledWith(
expect.objectContaining({apiKey: 'cli-key'}),
);
});

Expand Down Expand Up @@ -294,6 +309,69 @@ describe('GoogleLlm', () => {
expect(params.apiKey).toBeUndefined();
});

// GOOGLE_API_KEY sits ahead of GEMINI_API_KEY to match the ordering
// @google/genai itself applies in getApiKeyFromEnv().
const apiKeyEnvPrecedence: Array<{
description: string;
env: Record<string, string>;
expected: string;
}> = [
{
description: 'GOOGLE_API_KEY when it is the only key set',
env: {'GOOGLE_API_KEY': 'google-api-key'},
expected: 'google-api-key',
},
{
description: 'GOOGLE_GENAI_API_KEY over GOOGLE_API_KEY',
env: {
'GOOGLE_GENAI_API_KEY': 'genai-api-key',
'GOOGLE_API_KEY': 'google-api-key',
},
expected: 'genai-api-key',
},
{
description: 'GOOGLE_API_KEY over GEMINI_API_KEY',
env: {
'GOOGLE_API_KEY': 'google-api-key',
'GEMINI_API_KEY': 'gemini-api-key',
},
expected: 'google-api-key',
},
{
description: 'GEMINI_API_KEY when it is the only key set',
env: {'GEMINI_API_KEY': 'gemini-api-key'},
expected: 'gemini-api-key',
},
];

apiKeyEnvPrecedence.forEach(({description, env, expected}) => {
it(`should resolve ${description}`, () => {
Object.assign(process.env, env);
const params = geminiInitParams({model: 'gemini-1.5-flash'});
expect(params.apiKey).toBe(expected);
});
});

it('should prefer the constructor apiKey over GOOGLE_API_KEY', () => {
process.env['GOOGLE_API_KEY'] = 'google-api-key';
const params = geminiInitParams({
model: 'gemini-1.5-flash',
apiKey: 'explicit-key',
});
expect(params.apiKey).toBe('explicit-key');
});

it('should not use GOOGLE_API_KEY on the Vertex AI path', () => {
process.env['GOOGLE_API_KEY'] = 'google-api-key';
const params = geminiInitParams({
model: 'gemini-1.5-flash',
vertexai: true,
project: 'test-project',
location: 'us-central1',
});
expect(params.apiKey).toBeUndefined();
});

it('should initialize params for Vertex AI', () => {
const input = {
model: 'gemini-1.5-flash',
Expand Down
4 changes: 4 additions & 0 deletions dev/test/cli/cli_create_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ describe('createAgent', () => {
apiKey: 'my-api-key',
});

// Pins the API key env var name `adk create` writes. It MUST stay one of
// the names geminiInitParams() resolves in core/src/models/google_llm.ts,
// or the scaffolded agent cannot construct its own model; see the core
// test 'should construct from the .env written by `adk create`'.
expect(saveToFile).toHaveBeenCalledWith(
expect.stringContaining('.env'),
expect.stringContaining('GOOGLE_API_KEY=my-api-key'),
Expand Down
Loading