feat(core): use experiment flags for default fetch timeouts (#24261)

This commit is contained in:
Yuna Seol
2026-04-07 18:35:04 -04:00
committed by GitHub
parent 986293bd38
commit 3c5b5db034
10 changed files with 164 additions and 20 deletions
+62
View File
@@ -644,6 +644,58 @@ describe('Server Config (config.ts)', () => {
},
);
});
describe('getRequestTimeoutMs', () => {
it('should return undefined if the flag is not set', () => {
const config = new Config(baseParams);
expect(config.getRequestTimeoutMs()).toBeUndefined();
});
it('should return timeout in milliseconds if flag is set', () => {
const config = new Config({
...baseParams,
experiments: {
flags: {
[ExperimentFlags.DEFAULT_REQUEST_TIMEOUT]: {
intValue: '30',
},
},
experimentIds: [],
},
} as unknown as ConfigParameters);
expect(config.getRequestTimeoutMs()).toBe(30000);
});
it('should return undefined if intValue is not a valid integer', () => {
const config = new Config({
...baseParams,
experiments: {
flags: {
[ExperimentFlags.DEFAULT_REQUEST_TIMEOUT]: {
intValue: 'abc',
},
},
experimentIds: [],
},
} as unknown as ConfigParameters);
expect(config.getRequestTimeoutMs()).toBeUndefined();
});
it('should return undefined if intValue is negative', () => {
const config = new Config({
...baseParams,
experiments: {
flags: {
[ExperimentFlags.DEFAULT_REQUEST_TIMEOUT]: {
intValue: '-10',
},
},
experimentIds: [],
},
} as unknown as ConfigParameters);
expect(config.getRequestTimeoutMs()).toBeUndefined();
});
});
});
describe('refreshAuth', () => {
@@ -2078,8 +2130,18 @@ describe('BaseLlmClient Lifecycle', () => {
usageStatisticsEnabled: false,
};
it('should throw an error if getBaseLlmClient is called before experiments have been fetched', () => {
const config = new Config(baseParams);
// By default on a new Config instance, experiments are undefined
expect(() => config.getBaseLlmClient()).toThrow(
'BaseLlmClient not initialized. Ensure experiments have been fetched and configuration is ready.',
);
});
it('should throw an error if getBaseLlmClient is called before refreshAuth', () => {
const config = new Config(baseParams);
// Explicitly set experiments to avoid triggering the new missing-experiments error
config.setExperiments({ flags: {}, experimentIds: [] });
expect(() => config.getBaseLlmClient()).toThrow(
'BaseLlmClient not initialized. Ensure authentication has occurred and ContentGenerator is ready.',
);
+32 -7
View File
@@ -160,7 +160,7 @@ import {
} from '../code_assist/experiments/experiments.js';
import { AgentRegistry } from '../agents/registry.js';
import { AcknowledgedAgentsService } from '../agents/acknowledgedAgents.js';
import { setGlobalProxy } from '../utils/fetch.js';
import { setGlobalProxy, updateGlobalFetchTimeouts } from '../utils/fetch.js';
import { SubagentTool } from '../agents/subagent-tool.js';
import { ExperimentFlags } from '../code_assist/experiments/flagNames.js';
import { debugLogger } from '../utils/debugLogger.js';
@@ -1548,9 +1548,6 @@ export class Config implements McpContext, AgentLoopContext {
// Only assign to instance properties after successful initialization
this.contentGeneratorConfig = newContentGeneratorConfig;
// Initialize BaseLlmClient now that the ContentGenerator is available
this.baseLlmClient = new BaseLlmClient(this.contentGenerator, this);
const codeAssistServer = getCodeAssistServer(this);
const quotaPromise = codeAssistServer?.projectId
? this.refreshUserQuota()
@@ -1566,6 +1563,17 @@ export class Config implements McpContext, AgentLoopContext {
return undefined;
});
// Fetch experiments and update timeouts before continuing initialization
const experiments = await this.experimentsPromise;
const requestTimeoutMs = this.getRequestTimeoutMs();
if (requestTimeoutMs !== undefined) {
updateGlobalFetchTimeouts(requestTimeoutMs);
}
// Initialize BaseLlmClient now that the ContentGenerator and experiments are available
this.baseLlmClient = new BaseLlmClient(this.contentGenerator, this);
await quotaPromise;
const authType = this.contentGeneratorConfig.authType;
@@ -1585,9 +1593,6 @@ export class Config implements McpContext, AgentLoopContext {
this.setModel(DEFAULT_GEMINI_MODEL_AUTO);
}
// Fetch admin controls
const experiments = await this.experimentsPromise;
const adminControlsEnabled =
experiments?.flags[ExperimentFlags.ENABLE_ADMIN_CONTROLS]?.boolValue ??
false;
@@ -1633,6 +1638,11 @@ export class Config implements McpContext, AgentLoopContext {
getBaseLlmClient(): BaseLlmClient {
if (!this.baseLlmClient) {
// Handle cases where initialization might be deferred or authentication failed
if (!this.experiments) {
throw new Error(
'BaseLlmClient not initialized. Ensure experiments have been fetched and configuration is ready.',
);
}
if (this.contentGenerator) {
this.baseLlmClient = new BaseLlmClient(
this.getContentGenerator(),
@@ -3153,6 +3163,21 @@ export class Config implements McpContext, AgentLoopContext {
);
}
/**
* Returns the configured default request timeout in milliseconds.
*/
getRequestTimeoutMs(): number | undefined {
const flag =
this.experiments?.flags?.[ExperimentFlags.DEFAULT_REQUEST_TIMEOUT];
if (flag?.intValue !== undefined) {
const seconds = parseInt(flag.intValue, 10);
if (Number.isInteger(seconds) && seconds >= 0) {
return seconds * 1000; // Convert seconds to milliseconds
}
}
return undefined;
}
/**
* Returns whether Gemini 3.1 Flash Lite has been launched.
*