mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 08:47:44 +00:00
Merge tag 'v0.1.21' of github.com:google-gemini/gemini-cli into chore/sync-gemini-cli-v0.1.21
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { Mock } from 'vitest';
|
||||
import { Config, ConfigParameters, SandboxConfig } from './config.js';
|
||||
import * as path from 'path';
|
||||
import { setGeminiMdFilename as mockSetGeminiMdFilename } from '../tools/memoryTool.js';
|
||||
@@ -14,10 +15,12 @@ import {
|
||||
} from '../telemetry/index.js';
|
||||
import {
|
||||
AuthType,
|
||||
ContentGeneratorConfig,
|
||||
createContentGeneratorConfig,
|
||||
} from '../core/contentGenerator.js';
|
||||
import { GeminiClient } from '../core/client.js';
|
||||
import { GitService } from '../services/gitService.js';
|
||||
import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
|
||||
|
||||
vi.mock('fs', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('fs')>();
|
||||
@@ -131,11 +134,16 @@ describe('Server Config (config.ts)', () => {
|
||||
telemetry: TELEMETRY_SETTINGS,
|
||||
sessionId: SESSION_ID,
|
||||
model: MODEL,
|
||||
usageStatisticsEnabled: false,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset mocks if necessary
|
||||
vi.clearAllMocks();
|
||||
vi.spyOn(
|
||||
ClearcutLogger.prototype,
|
||||
'logStartSessionEvent',
|
||||
).mockImplementation(() => undefined);
|
||||
});
|
||||
|
||||
describe('initialize', () => {
|
||||
@@ -254,6 +262,7 @@ describe('Server Config (config.ts)', () => {
|
||||
// Verify that history was restored to the new client
|
||||
expect(mockNewClient.setHistory).toHaveBeenCalledWith(
|
||||
mockExistingHistory,
|
||||
{ stripThoughts: false },
|
||||
);
|
||||
});
|
||||
|
||||
@@ -287,6 +296,92 @@ describe('Server Config (config.ts)', () => {
|
||||
// Verify that setHistory was not called since there was no existing history
|
||||
expect(mockNewClient.setHistory).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should strip thoughts when switching from GenAI to Vertex', async () => {
|
||||
const config = new Config(baseParams);
|
||||
const mockContentConfig = {
|
||||
model: 'gemini-pro',
|
||||
apiKey: 'test-key',
|
||||
authType: AuthType.USE_GEMINI,
|
||||
};
|
||||
(
|
||||
config as unknown as { contentGeneratorConfig: ContentGeneratorConfig }
|
||||
).contentGeneratorConfig = mockContentConfig;
|
||||
|
||||
(createContentGeneratorConfig as Mock).mockReturnValue({
|
||||
...mockContentConfig,
|
||||
authType: AuthType.LOGIN_WITH_GOOGLE,
|
||||
});
|
||||
|
||||
const mockExistingHistory = [
|
||||
{ role: 'user', parts: [{ text: 'Hello' }] },
|
||||
];
|
||||
const mockExistingClient = {
|
||||
isInitialized: vi.fn().mockReturnValue(true),
|
||||
getHistory: vi.fn().mockReturnValue(mockExistingHistory),
|
||||
};
|
||||
const mockNewClient = {
|
||||
isInitialized: vi.fn().mockReturnValue(true),
|
||||
getHistory: vi.fn().mockReturnValue([]),
|
||||
setHistory: vi.fn(),
|
||||
initialize: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
(
|
||||
config as unknown as { geminiClient: typeof mockExistingClient }
|
||||
).geminiClient = mockExistingClient;
|
||||
(GeminiClient as Mock).mockImplementation(() => mockNewClient);
|
||||
|
||||
await config.refreshAuth(AuthType.LOGIN_WITH_GOOGLE);
|
||||
|
||||
expect(mockNewClient.setHistory).toHaveBeenCalledWith(
|
||||
mockExistingHistory,
|
||||
{ stripThoughts: true },
|
||||
);
|
||||
});
|
||||
|
||||
it('should not strip thoughts when switching from Vertex to GenAI', async () => {
|
||||
const config = new Config(baseParams);
|
||||
const mockContentConfig = {
|
||||
model: 'gemini-pro',
|
||||
apiKey: 'test-key',
|
||||
authType: AuthType.LOGIN_WITH_GOOGLE,
|
||||
};
|
||||
(
|
||||
config as unknown as { contentGeneratorConfig: ContentGeneratorConfig }
|
||||
).contentGeneratorConfig = mockContentConfig;
|
||||
|
||||
(createContentGeneratorConfig as Mock).mockReturnValue({
|
||||
...mockContentConfig,
|
||||
authType: AuthType.USE_GEMINI,
|
||||
});
|
||||
|
||||
const mockExistingHistory = [
|
||||
{ role: 'user', parts: [{ text: 'Hello' }] },
|
||||
];
|
||||
const mockExistingClient = {
|
||||
isInitialized: vi.fn().mockReturnValue(true),
|
||||
getHistory: vi.fn().mockReturnValue(mockExistingHistory),
|
||||
};
|
||||
const mockNewClient = {
|
||||
isInitialized: vi.fn().mockReturnValue(true),
|
||||
getHistory: vi.fn().mockReturnValue([]),
|
||||
setHistory: vi.fn(),
|
||||
initialize: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
(
|
||||
config as unknown as { geminiClient: typeof mockExistingClient }
|
||||
).geminiClient = mockExistingClient;
|
||||
(GeminiClient as Mock).mockImplementation(() => mockNewClient);
|
||||
|
||||
await config.refreshAuth(AuthType.USE_GEMINI);
|
||||
|
||||
expect(mockNewClient.setHistory).toHaveBeenCalledWith(
|
||||
mockExistingHistory,
|
||||
{ stripThoughts: false },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Config constructor should store userMemory correctly', () => {
|
||||
@@ -384,6 +479,39 @@ describe('Server Config (config.ts)', () => {
|
||||
expect(fileService).toBeDefined();
|
||||
});
|
||||
|
||||
describe('Usage Statistics', () => {
|
||||
it('defaults usage statistics to enabled if not specified', () => {
|
||||
const config = new Config({
|
||||
...baseParams,
|
||||
usageStatisticsEnabled: undefined,
|
||||
});
|
||||
|
||||
expect(config.getUsageStatisticsEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it.each([{ enabled: true }, { enabled: false }])(
|
||||
'sets usage statistics based on the provided value (enabled: $enabled)',
|
||||
({ enabled }) => {
|
||||
const config = new Config({
|
||||
...baseParams,
|
||||
usageStatisticsEnabled: enabled,
|
||||
});
|
||||
expect(config.getUsageStatisticsEnabled()).toBe(enabled);
|
||||
},
|
||||
);
|
||||
|
||||
it('logs the session start event', () => {
|
||||
new Config({
|
||||
...baseParams,
|
||||
usageStatisticsEnabled: true,
|
||||
});
|
||||
|
||||
expect(
|
||||
ClearcutLogger.prototype.logStartSessionEvent,
|
||||
).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Telemetry Settings', () => {
|
||||
it('should return default telemetry target if not provided', () => {
|
||||
const params: ConfigParameters = {
|
||||
|
||||
@@ -193,13 +193,12 @@ export interface ConfigParameters {
|
||||
extensionContextFilePaths?: string[];
|
||||
maxSessionTurns?: number;
|
||||
sessionTokenLimit?: number;
|
||||
experimentalAcp?: boolean;
|
||||
experimentalZedIntegration?: boolean;
|
||||
listExtensions?: boolean;
|
||||
extensions?: GeminiCLIExtension[];
|
||||
blockedMcpServers?: Array<{ name: string; extensionName: string }>;
|
||||
noBrowser?: boolean;
|
||||
summarizeToolOutput?: Record<string, SummarizeToolOutputSettings>;
|
||||
ideModeFeature?: boolean;
|
||||
folderTrustFeature?: boolean;
|
||||
folderTrust?: boolean;
|
||||
ideMode?: boolean;
|
||||
@@ -220,6 +219,7 @@ export interface ConfigParameters {
|
||||
tavilyApiKey?: string;
|
||||
chatCompression?: ChatCompressionSettings;
|
||||
interactive?: boolean;
|
||||
trustedFolder?: boolean;
|
||||
}
|
||||
|
||||
export class Config {
|
||||
@@ -263,7 +263,6 @@ export class Config {
|
||||
private readonly model: string;
|
||||
private readonly extensionContextFilePaths: string[];
|
||||
private readonly noBrowser: boolean;
|
||||
private readonly ideModeFeature: boolean;
|
||||
private readonly folderTrustFeature: boolean;
|
||||
private readonly folderTrust: boolean;
|
||||
private ideMode: boolean;
|
||||
@@ -287,7 +286,6 @@ export class Config {
|
||||
private readonly summarizeToolOutput:
|
||||
| Record<string, SummarizeToolOutputSettings>
|
||||
| undefined;
|
||||
private readonly experimentalAcp: boolean = false;
|
||||
private readonly enableOpenAILogging: boolean;
|
||||
private readonly sampling_params?: Record<string, unknown>;
|
||||
private readonly contentGenerator?: {
|
||||
@@ -295,10 +293,12 @@ export class Config {
|
||||
maxRetries?: number;
|
||||
};
|
||||
private readonly cliVersion?: string;
|
||||
private readonly experimentalZedIntegration: boolean = false;
|
||||
private readonly loadMemoryFromIncludeDirectories: boolean = false;
|
||||
private readonly tavilyApiKey?: string;
|
||||
private readonly chatCompression: ChatCompressionSettings | undefined;
|
||||
private readonly interactive: boolean;
|
||||
private readonly trustedFolder: boolean | undefined;
|
||||
private initialized: boolean = false;
|
||||
|
||||
constructor(params: ConfigParameters) {
|
||||
@@ -354,13 +354,13 @@ export class Config {
|
||||
this.extensionContextFilePaths = params.extensionContextFilePaths ?? [];
|
||||
this.maxSessionTurns = params.maxSessionTurns ?? -1;
|
||||
this.sessionTokenLimit = params.sessionTokenLimit ?? -1;
|
||||
this.experimentalAcp = params.experimentalAcp ?? false;
|
||||
this.experimentalZedIntegration =
|
||||
params.experimentalZedIntegration ?? false;
|
||||
this.listExtensions = params.listExtensions ?? false;
|
||||
this._extensions = params.extensions ?? [];
|
||||
this._blockedMcpServers = params.blockedMcpServers ?? [];
|
||||
this.noBrowser = params.noBrowser ?? false;
|
||||
this.summarizeToolOutput = params.summarizeToolOutput;
|
||||
this.ideModeFeature = params.ideModeFeature ?? false;
|
||||
this.folderTrustFeature = params.folderTrustFeature ?? false;
|
||||
this.folderTrust = params.folderTrust ?? false;
|
||||
this.ideMode = params.ideMode ?? false;
|
||||
@@ -375,6 +375,7 @@ export class Config {
|
||||
params.loadMemoryFromIncludeDirectories ?? false;
|
||||
this.chatCompression = params.chatCompression;
|
||||
this.interactive = params.interactive ?? false;
|
||||
this.trustedFolder = params.trustedFolder;
|
||||
|
||||
// Web search
|
||||
this.tavilyApiKey = params.tavilyApiKey;
|
||||
@@ -430,13 +431,21 @@ export class Config {
|
||||
const newGeminiClient = new GeminiClient(this);
|
||||
await newGeminiClient.initialize(newContentGeneratorConfig);
|
||||
|
||||
// Vertex and Genai have incompatible encryption and sending history with
|
||||
// throughtSignature from Genai to Vertex will fail, we need to strip them
|
||||
const fromGenaiToVertex =
|
||||
this.contentGeneratorConfig?.authType === AuthType.USE_GEMINI &&
|
||||
authMethod === AuthType.LOGIN_WITH_GOOGLE;
|
||||
|
||||
// Only assign to instance properties after successful initialization
|
||||
this.contentGeneratorConfig = newContentGeneratorConfig;
|
||||
this.geminiClient = newGeminiClient;
|
||||
|
||||
// Restore the conversation history to the new client
|
||||
if (existingHistory.length > 0) {
|
||||
this.geminiClient.setHistory(existingHistory);
|
||||
this.geminiClient.setHistory(existingHistory, {
|
||||
stripThoughts: fromGenaiToVertex,
|
||||
});
|
||||
}
|
||||
|
||||
// Reset the session flag since we're explicitly changing auth and using default model
|
||||
@@ -684,8 +693,8 @@ export class Config {
|
||||
return this.extensionContextFilePaths;
|
||||
}
|
||||
|
||||
getExperimentalAcp(): boolean {
|
||||
return this.experimentalAcp;
|
||||
getExperimentalZedIntegration(): boolean {
|
||||
return this.experimentalZedIntegration;
|
||||
}
|
||||
|
||||
getListExtensions(): boolean {
|
||||
@@ -719,10 +728,6 @@ export class Config {
|
||||
return this.tavilyApiKey;
|
||||
}
|
||||
|
||||
getIdeModeFeature(): boolean {
|
||||
return this.ideModeFeature;
|
||||
}
|
||||
|
||||
getIdeClient(): IdeClient {
|
||||
return this.ideClient;
|
||||
}
|
||||
@@ -739,6 +744,10 @@ export class Config {
|
||||
return this.folderTrust;
|
||||
}
|
||||
|
||||
isTrustedFolder(): boolean | undefined {
|
||||
return this.trustedFolder;
|
||||
}
|
||||
|
||||
setIdeMode(value: boolean): void {
|
||||
this.ideMode = value;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user