Skip to content
Merged
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
12 changes: 10 additions & 2 deletions packages/cli/src/acp/acpSessionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ export class AcpSessionManager {
);

const authType =
loadedSettings.merged.security.auth.selectedType || AuthType.USE_GEMINI;
loadedSettings.merged.security.auth.selectedType ||
(authDetails.baseUrl || process.env['GOOGLE_GEMINI_BASE_URL']
? AuthType.GATEWAY
: AuthType.USE_GEMINI);
Comment thread
sripasg marked this conversation as resolved.

let isAuthenticated = false;
let authErrorMessage = '';
Expand Down Expand Up @@ -231,7 +234,12 @@ export class AcpSessionManager {
mcpServers: acp.McpServer[],
authDetails: AuthDetails,
): Promise<Config> {
const selectedAuthType = this.settings.merged.security.auth.selectedType;
const selectedAuthType =
this.settings.merged.security.auth.selectedType ||
(authDetails.baseUrl || process.env['GOOGLE_GEMINI_BASE_URL']
? AuthType.GATEWAY
: undefined);

if (!selectedAuthType) {
throw acp.RequestError.authRequired();
}
Expand Down
95 changes: 89 additions & 6 deletions packages/core/src/core/contentGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
createContentGenerator,
AuthType,
createContentGeneratorConfig,
getAuthTypeFromEnv,
type ContentGenerator,
} from './contentGenerator.js';
import { createCodeAssistContentGenerator } from '../code_assist/codeAssist.js';
Expand All @@ -35,6 +36,45 @@ const mockConfig = {
getClientName: vi.fn().mockReturnValue(undefined),
} as unknown as Config;

describe('getAuthTypeFromEnv', () => {
beforeEach(() => {
vi.stubEnv('GEMINI_API_KEY', '');
});

afterEach(() => {
vi.unstubAllEnvs();
});

it('should detect LOGIN_WITH_GOOGLE when GOOGLE_GENAI_USE_GCA is true', () => {
vi.stubEnv('GOOGLE_GENAI_USE_GCA', 'true');
expect(getAuthTypeFromEnv()).toBe(AuthType.LOGIN_WITH_GOOGLE);
});

it('should detect USE_VERTEX_AI when GOOGLE_GENAI_USE_VERTEXAI is true', () => {
vi.stubEnv('GOOGLE_GENAI_USE_VERTEXAI', 'true');
expect(getAuthTypeFromEnv()).toBe(AuthType.USE_VERTEX_AI);
});

it('should detect GATEWAY when GOOGLE_GEMINI_BASE_URL is present', () => {
vi.stubEnv('GOOGLE_GEMINI_BASE_URL', 'https://gateway.example.com');
expect(getAuthTypeFromEnv()).toBe(AuthType.GATEWAY);
});

it('should detect USE_GEMINI when GEMINI_API_KEY is present', () => {
vi.stubEnv('GEMINI_API_KEY', 'fake-key');
expect(getAuthTypeFromEnv()).toBe(AuthType.USE_GEMINI);
});

it('should detect COMPUTE_ADC when CLOUD_SHELL is true', () => {
vi.stubEnv('CLOUD_SHELL', 'true');
expect(getAuthTypeFromEnv()).toBe(AuthType.COMPUTE_ADC);
});

it('should return undefined when no matching env variables are set', () => {
expect(getAuthTypeFromEnv()).toBeUndefined();
});
});

describe('createContentGenerator', () => {
beforeEach(() => {
resetVersionCache();
Expand Down Expand Up @@ -851,6 +891,40 @@ describe('createContentGenerator', () => {
),
).rejects.toThrow('Invalid custom base URL: not-a-url');
});

it('should set empty x-goog-api-key header for GATEWAY auth when apiKey is empty string', async () => {
const mockConfig = {
getModel: vi.fn().mockReturnValue('gemini-pro'),
getProxy: vi.fn().mockReturnValue(undefined),
getUsageStatisticsEnabled: () => false,
getClientName: vi.fn().mockReturnValue(undefined),
} as unknown as Config;

const mockGenerator = {
models: {},
} as unknown as GoogleGenAI;
vi.mocked(GoogleGenAI).mockImplementation(() => mockGenerator as never);

await createContentGenerator(
{
apiKey: '',
authType: AuthType.GATEWAY,
baseUrl: 'https://gateway.test.local',
},
mockConfig,
);

expect(GoogleGenAI).toHaveBeenCalledWith(
expect.objectContaining({
apiKey: '',
httpOptions: expect.objectContaining({
headers: expect.objectContaining({
'x-goog-api-key': '',
}),
}),
}),
);
});
});

describe('createContentGeneratorConfig', () => {
Expand Down Expand Up @@ -955,24 +1029,33 @@ describe('createContentGeneratorConfig', () => {
expect(config.apiKey).toBeUndefined();
expect(config.vertexai).toBeUndefined();
});
it('should configure for GATEWAY using dummy placeholder if GEMINI_API_KEY is set', async () => {
vi.stubEnv('GEMINI_API_KEY', 'env-gemini-key');
it('should configure for GATEWAY using provided apiKey if available', async () => {
const config = await createContentGeneratorConfig(
mockConfig,
AuthType.GATEWAY,
'custom-gateway-key',
);
expect(config.apiKey).toBe('custom-gateway-key');
expect(config.vertexai).toBe(false);
});

it('should configure for GATEWAY using GEMINI_API_KEY from environment if set', async () => {
vi.stubEnv('GEMINI_API_KEY', 'env-gateway-key');
const config = await createContentGeneratorConfig(
mockConfig,
AuthType.GATEWAY,
);
expect(config.apiKey).toBe('gateway-placeholder-key');
expect(config.apiKey).toBe('env-gateway-key');
expect(config.vertexai).toBe(false);
});

it('should configure for GATEWAY using dummy placeholder if GEMINI_API_KEY is not set', async () => {
it('should configure for GATEWAY using empty string if no apiKey is provided', async () => {
vi.stubEnv('GEMINI_API_KEY', '');
vi.mocked(loadApiKey).mockResolvedValue(null);
const config = await createContentGeneratorConfig(
mockConfig,
AuthType.GATEWAY,
);
expect(config.apiKey).toBe('gateway-placeholder-key');
expect(config.apiKey).toBe('');
expect(config.vertexai).toBe(false);
});
});
16 changes: 14 additions & 2 deletions packages/core/src/core/contentGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ export function getAuthTypeFromEnv(): AuthType | undefined {
if (process.env['GOOGLE_GENAI_USE_VERTEXAI'] === 'true') {
return AuthType.USE_VERTEX_AI;
}
if (process.env['GOOGLE_GEMINI_BASE_URL']) {
return AuthType.GATEWAY;
}
if (process.env['GEMINI_API_KEY']) {
return AuthType.USE_GEMINI;
}
Expand Down Expand Up @@ -178,7 +181,8 @@ export async function createContentGeneratorConfig(
}

if (authType === AuthType.GATEWAY) {
contentGeneratorConfig.apiKey = apiKey || 'gateway-placeholder-key';
contentGeneratorConfig.apiKey =
apiKey || process.env['GEMINI_API_KEY'] || '';
contentGeneratorConfig.vertexai = false;

return contentGeneratorConfig;
Expand Down Expand Up @@ -313,6 +317,9 @@ export async function createContentGenerator(
'x-gemini-api-privileged-user-id': `${installationId}`,
};
}
if (config.authType === AuthType.GATEWAY && config.apiKey === '') {
headers['x-goog-api-key'] = '';
}
Comment thread
sripasg marked this conversation as resolved.
let baseUrl = config.baseUrl;
if (!baseUrl) {
const envBaseUrl =
Expand All @@ -337,7 +344,12 @@ export async function createContentGenerator(
}

const googleGenAI = new GoogleGenAI({
apiKey: config.apiKey === '' ? undefined : config.apiKey,
apiKey:
config.authType === AuthType.GATEWAY
? config.apiKey
: config.apiKey === ''
? undefined
: config.apiKey,
vertexai: config.vertexai ?? config.authType === AuthType.USE_VERTEX_AI,
httpOptions,
...(apiVersionEnv && { apiVersion: apiVersionEnv }),
Expand Down
Loading