Introduce ContentGeneratorConfig (#826)
This commit is contained in:
parent
e95a6086fc
commit
389907ce65
|
@ -8,7 +8,17 @@ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { tmpdir } from 'os';
|
import { tmpdir } from 'os';
|
||||||
import { Config, ConfigParameters } from '@gemini-cli/core';
|
import {
|
||||||
|
Config,
|
||||||
|
ConfigParameters,
|
||||||
|
ContentGeneratorConfig,
|
||||||
|
} from '@gemini-cli/core';
|
||||||
|
|
||||||
|
const TEST_CONTENT_GENERATOR_CONFIG: ContentGeneratorConfig = {
|
||||||
|
apiKey: 'test-key',
|
||||||
|
model: 'test-model',
|
||||||
|
userAgent: 'test-agent',
|
||||||
|
};
|
||||||
|
|
||||||
// Mock file discovery service and tool registry
|
// Mock file discovery service and tool registry
|
||||||
vi.mock('@gemini-cli/core', async () => {
|
vi.mock('@gemini-cli/core', async () => {
|
||||||
|
@ -43,12 +53,11 @@ describe('Configuration Integration Tests', () => {
|
||||||
describe('File Filtering Configuration', () => {
|
describe('File Filtering Configuration', () => {
|
||||||
it('should load default file filtering settings', async () => {
|
it('should load default file filtering settings', async () => {
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
apiKey: 'test-key',
|
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
|
||||||
model: 'test-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: tempDir,
|
targetDir: tempDir,
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'test-agent',
|
|
||||||
fileFilteringRespectGitIgnore: undefined, // Should default to true
|
fileFilteringRespectGitIgnore: undefined, // Should default to true
|
||||||
fileFilteringAllowBuildArtifacts: undefined, // Should default to false
|
fileFilteringAllowBuildArtifacts: undefined, // Should default to false
|
||||||
};
|
};
|
||||||
|
@ -61,12 +70,11 @@ describe('Configuration Integration Tests', () => {
|
||||||
|
|
||||||
it('should load custom file filtering settings from configuration', async () => {
|
it('should load custom file filtering settings from configuration', async () => {
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
apiKey: 'test-key',
|
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
|
||||||
model: 'test-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: tempDir,
|
targetDir: tempDir,
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'test-agent',
|
|
||||||
fileFilteringRespectGitIgnore: false,
|
fileFilteringRespectGitIgnore: false,
|
||||||
fileFilteringAllowBuildArtifacts: true,
|
fileFilteringAllowBuildArtifacts: true,
|
||||||
};
|
};
|
||||||
|
@ -79,12 +87,11 @@ describe('Configuration Integration Tests', () => {
|
||||||
|
|
||||||
it('should merge user and workspace file filtering settings', async () => {
|
it('should merge user and workspace file filtering settings', async () => {
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
apiKey: 'test-key',
|
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
|
||||||
model: 'test-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: tempDir,
|
targetDir: tempDir,
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'test-agent',
|
|
||||||
fileFilteringRespectGitIgnore: true,
|
fileFilteringRespectGitIgnore: true,
|
||||||
fileFilteringAllowBuildArtifacts: true,
|
fileFilteringAllowBuildArtifacts: true,
|
||||||
};
|
};
|
||||||
|
@ -99,12 +106,11 @@ describe('Configuration Integration Tests', () => {
|
||||||
describe('Configuration Integration', () => {
|
describe('Configuration Integration', () => {
|
||||||
it('should handle partial configuration objects gracefully', async () => {
|
it('should handle partial configuration objects gracefully', async () => {
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
apiKey: 'test-key',
|
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
|
||||||
model: 'test-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: tempDir,
|
targetDir: tempDir,
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'test-agent',
|
|
||||||
fileFilteringRespectGitIgnore: false,
|
fileFilteringRespectGitIgnore: false,
|
||||||
fileFilteringAllowBuildArtifacts: undefined, // Should default to false
|
fileFilteringAllowBuildArtifacts: undefined, // Should default to false
|
||||||
};
|
};
|
||||||
|
@ -120,12 +126,11 @@ describe('Configuration Integration Tests', () => {
|
||||||
|
|
||||||
it('should handle empty configuration objects gracefully', async () => {
|
it('should handle empty configuration objects gracefully', async () => {
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
apiKey: 'test-key',
|
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
|
||||||
model: 'test-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: tempDir,
|
targetDir: tempDir,
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'test-agent',
|
|
||||||
fileFilteringRespectGitIgnore: undefined,
|
fileFilteringRespectGitIgnore: undefined,
|
||||||
fileFilteringAllowBuildArtifacts: undefined,
|
fileFilteringAllowBuildArtifacts: undefined,
|
||||||
};
|
};
|
||||||
|
@ -139,12 +144,11 @@ describe('Configuration Integration Tests', () => {
|
||||||
|
|
||||||
it('should handle missing configuration sections gracefully', async () => {
|
it('should handle missing configuration sections gracefully', async () => {
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
apiKey: 'test-key',
|
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
|
||||||
model: 'test-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: tempDir,
|
targetDir: tempDir,
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'test-agent',
|
|
||||||
// Missing fileFiltering configuration
|
// Missing fileFiltering configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,12 +163,11 @@ describe('Configuration Integration Tests', () => {
|
||||||
describe('Real-world Configuration Scenarios', () => {
|
describe('Real-world Configuration Scenarios', () => {
|
||||||
it('should handle a security-focused configuration', async () => {
|
it('should handle a security-focused configuration', async () => {
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
apiKey: 'test-key',
|
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
|
||||||
model: 'test-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: tempDir,
|
targetDir: tempDir,
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'test-agent',
|
|
||||||
fileFilteringRespectGitIgnore: true,
|
fileFilteringRespectGitIgnore: true,
|
||||||
fileFilteringAllowBuildArtifacts: false,
|
fileFilteringAllowBuildArtifacts: false,
|
||||||
};
|
};
|
||||||
|
@ -177,12 +180,11 @@ describe('Configuration Integration Tests', () => {
|
||||||
|
|
||||||
it('should handle a development-focused configuration', async () => {
|
it('should handle a development-focused configuration', async () => {
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
apiKey: 'test-key',
|
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
|
||||||
model: 'test-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: tempDir,
|
targetDir: tempDir,
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'test-agent',
|
|
||||||
fileFilteringRespectGitIgnore: true,
|
fileFilteringRespectGitIgnore: true,
|
||||||
fileFilteringAllowBuildArtifacts: true,
|
fileFilteringAllowBuildArtifacts: true,
|
||||||
};
|
};
|
||||||
|
@ -194,12 +196,11 @@ describe('Configuration Integration Tests', () => {
|
||||||
|
|
||||||
it('should handle a CI/CD environment configuration', async () => {
|
it('should handle a CI/CD environment configuration', async () => {
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
apiKey: 'test-key',
|
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
|
||||||
model: 'test-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: tempDir,
|
targetDir: tempDir,
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'test-agent',
|
|
||||||
fileFilteringRespectGitIgnore: false, // CI might need to see all files
|
fileFilteringRespectGitIgnore: false, // CI might need to see all files
|
||||||
fileFilteringAllowBuildArtifacts: true,
|
fileFilteringAllowBuildArtifacts: true,
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
setGeminiMdFilename as setServerGeminiMdFilename,
|
setGeminiMdFilename as setServerGeminiMdFilename,
|
||||||
getCurrentGeminiMdFilename,
|
getCurrentGeminiMdFilename,
|
||||||
ApprovalMode,
|
ApprovalMode,
|
||||||
|
ContentGeneratorConfig,
|
||||||
} from '@gemini-cli/core';
|
} from '@gemini-cli/core';
|
||||||
import { Settings } from './settings.js';
|
import { Settings } from './settings.js';
|
||||||
import { getEffectiveModel } from '../utils/modelCheck.js';
|
import { getEffectiveModel } from '../utils/modelCheck.js';
|
||||||
|
@ -121,6 +122,62 @@ export async function loadCliConfig(
|
||||||
): Promise<Config> {
|
): Promise<Config> {
|
||||||
loadEnvironment();
|
loadEnvironment();
|
||||||
|
|
||||||
|
const argv = await parseArguments();
|
||||||
|
const debugMode = argv.debug || false;
|
||||||
|
|
||||||
|
// Set the context filename in the server's memoryTool module BEFORE loading memory
|
||||||
|
// TODO(b/343434939): This is a bit of a hack. The contextFileName should ideally be passed
|
||||||
|
// directly to the Config constructor in core, and have core handle setGeminiMdFilename.
|
||||||
|
// However, loadHierarchicalGeminiMemory is called *before* createServerConfig.
|
||||||
|
if (settings.contextFileName) {
|
||||||
|
setServerGeminiMdFilename(settings.contextFileName);
|
||||||
|
} else {
|
||||||
|
// Reset to default if not provided in settings.
|
||||||
|
setServerGeminiMdFilename(getCurrentGeminiMdFilename());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the (now wrapper) loadHierarchicalGeminiMemory which calls the server's version
|
||||||
|
const { memoryContent, fileCount } = await loadHierarchicalGeminiMemory(
|
||||||
|
process.cwd(),
|
||||||
|
debugMode,
|
||||||
|
);
|
||||||
|
|
||||||
|
const contentGeneratorConfig = await createContentGeneratorConfig(argv);
|
||||||
|
|
||||||
|
return new Config({
|
||||||
|
contentGeneratorConfig,
|
||||||
|
embeddingModel: DEFAULT_GEMINI_EMBEDDING_MODEL,
|
||||||
|
sandbox: argv.sandbox ?? settings.sandbox ?? argv.yolo ?? false,
|
||||||
|
targetDir: process.cwd(),
|
||||||
|
debugMode,
|
||||||
|
question: argv.prompt || '',
|
||||||
|
fullContext: argv.all_files || false,
|
||||||
|
coreTools: settings.coreTools || undefined,
|
||||||
|
toolDiscoveryCommand: settings.toolDiscoveryCommand,
|
||||||
|
toolCallCommand: settings.toolCallCommand,
|
||||||
|
mcpServerCommand: settings.mcpServerCommand,
|
||||||
|
mcpServers: settings.mcpServers,
|
||||||
|
userMemory: memoryContent,
|
||||||
|
geminiMdFileCount: fileCount,
|
||||||
|
approvalMode: argv.yolo || false ? ApprovalMode.YOLO : ApprovalMode.DEFAULT,
|
||||||
|
showMemoryUsage:
|
||||||
|
argv.show_memory_usage || settings.showMemoryUsage || false,
|
||||||
|
geminiIgnorePatterns,
|
||||||
|
accessibility: settings.accessibility,
|
||||||
|
telemetry:
|
||||||
|
argv.telemetry !== undefined
|
||||||
|
? argv.telemetry
|
||||||
|
: (settings.telemetry ?? false),
|
||||||
|
// Git-aware file filtering settings
|
||||||
|
fileFilteringRespectGitIgnore: settings.fileFiltering?.respectGitIgnore,
|
||||||
|
fileFilteringAllowBuildArtifacts:
|
||||||
|
settings.fileFiltering?.allowBuildArtifacts,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createContentGeneratorConfig(
|
||||||
|
argv: CliArgs,
|
||||||
|
): Promise<ContentGeneratorConfig> {
|
||||||
const geminiApiKey = process.env.GEMINI_API_KEY;
|
const geminiApiKey = process.env.GEMINI_API_KEY;
|
||||||
const googleApiKey = process.env.GOOGLE_API_KEY;
|
const googleApiKey = process.env.GOOGLE_API_KEY;
|
||||||
const googleCloudProject = process.env.GOOGLE_CLOUD_PROJECT;
|
const googleCloudProject = process.env.GOOGLE_CLOUD_PROJECT;
|
||||||
|
@ -144,65 +201,16 @@ export async function loadCliConfig(
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const argv = await parseArguments();
|
const config: ContentGeneratorConfig = {
|
||||||
const debugMode = argv.debug || false;
|
model: argv.model || DEFAULT_GEMINI_MODEL,
|
||||||
|
apiKey: geminiApiKey || googleApiKey || '',
|
||||||
|
vertexai: hasGeminiApiKey ? false : undefined,
|
||||||
|
userAgent: `GeminiCLI/${getCliVersion()}/(${process.platform}; ${process.arch})`,
|
||||||
|
};
|
||||||
|
|
||||||
// Set the context filename in the server's memoryTool module BEFORE loading memory
|
if (config.apiKey) {
|
||||||
// TODO(b/343434939): This is a bit of a hack. The contextFileName should ideally be passed
|
config.model = await getEffectiveModel(config.apiKey, config.model);
|
||||||
// directly to the Config constructor in core, and have core handle setGeminiMdFilename.
|
|
||||||
// However, loadHierarchicalGeminiMemory is called *before* createServerConfig.
|
|
||||||
if (settings.contextFileName) {
|
|
||||||
setServerGeminiMdFilename(settings.contextFileName);
|
|
||||||
} else {
|
|
||||||
// Reset to default if not provided in settings.
|
|
||||||
setServerGeminiMdFilename(getCurrentGeminiMdFilename());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the (now wrapper) loadHierarchicalGeminiMemory which calls the server's version
|
return config;
|
||||||
const { memoryContent, fileCount } = await loadHierarchicalGeminiMemory(
|
|
||||||
process.cwd(),
|
|
||||||
debugMode,
|
|
||||||
);
|
|
||||||
|
|
||||||
const userAgent = `GeminiCLI/${getCliVersion()}/(${process.platform}; ${process.arch})`;
|
|
||||||
const apiKeyForServer = geminiApiKey || googleApiKey || '';
|
|
||||||
const useVertexAI = hasGeminiApiKey ? false : undefined;
|
|
||||||
|
|
||||||
let modelToUse = argv.model || DEFAULT_GEMINI_MODEL;
|
|
||||||
if (apiKeyForServer) {
|
|
||||||
modelToUse = await getEffectiveModel(apiKeyForServer, modelToUse);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Config({
|
|
||||||
apiKey: apiKeyForServer,
|
|
||||||
model: modelToUse,
|
|
||||||
embeddingModel: DEFAULT_GEMINI_EMBEDDING_MODEL,
|
|
||||||
sandbox: argv.sandbox ?? settings.sandbox ?? argv.yolo ?? false,
|
|
||||||
targetDir: process.cwd(),
|
|
||||||
debugMode,
|
|
||||||
question: argv.prompt || '',
|
|
||||||
fullContext: argv.all_files || false,
|
|
||||||
coreTools: settings.coreTools || undefined,
|
|
||||||
toolDiscoveryCommand: settings.toolDiscoveryCommand,
|
|
||||||
toolCallCommand: settings.toolCallCommand,
|
|
||||||
mcpServerCommand: settings.mcpServerCommand,
|
|
||||||
mcpServers: settings.mcpServers,
|
|
||||||
userAgent,
|
|
||||||
userMemory: memoryContent,
|
|
||||||
geminiMdFileCount: fileCount,
|
|
||||||
approvalMode: argv.yolo || false ? ApprovalMode.YOLO : ApprovalMode.DEFAULT,
|
|
||||||
vertexai: useVertexAI,
|
|
||||||
showMemoryUsage:
|
|
||||||
argv.show_memory_usage || settings.showMemoryUsage || false,
|
|
||||||
geminiIgnorePatterns,
|
|
||||||
accessibility: settings.accessibility,
|
|
||||||
telemetry:
|
|
||||||
argv.telemetry !== undefined
|
|
||||||
? argv.telemetry
|
|
||||||
: (settings.telemetry ?? false),
|
|
||||||
// Git-aware file filtering settings
|
|
||||||
fileFilteringRespectGitIgnore: settings.fileFiltering?.respectGitIgnore,
|
|
||||||
fileFilteringAllowBuildArtifacts:
|
|
||||||
settings.fileFiltering?.allowBuildArtifacts,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,13 +179,15 @@ describe('App UI', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const ServerConfigMocked = vi.mocked(ServerConfig, true);
|
const ServerConfigMocked = vi.mocked(ServerConfig, true);
|
||||||
mockConfig = new ServerConfigMocked({
|
mockConfig = new ServerConfigMocked({
|
||||||
|
contentGeneratorConfig: {
|
||||||
apiKey: 'test-key',
|
apiKey: 'test-key',
|
||||||
model: 'test-model',
|
model: 'test-model',
|
||||||
|
userAgent: 'test-agent',
|
||||||
|
},
|
||||||
embeddingModel: 'test-embedding-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: '/test/dir',
|
targetDir: '/test/dir',
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'test-agent',
|
|
||||||
userMemory: '',
|
userMemory: '',
|
||||||
geminiMdFileCount: 0,
|
geminiMdFileCount: 0,
|
||||||
showMemoryUsage: false,
|
showMemoryUsage: false,
|
||||||
|
|
|
@ -50,15 +50,17 @@ describe('Server Config (config.ts)', () => {
|
||||||
const TELEMETRY = false;
|
const TELEMETRY = false;
|
||||||
const EMBEDDING_MODEL = 'gemini-embedding';
|
const EMBEDDING_MODEL = 'gemini-embedding';
|
||||||
const baseParams: ConfigParameters = {
|
const baseParams: ConfigParameters = {
|
||||||
|
contentGeneratorConfig: {
|
||||||
apiKey: API_KEY,
|
apiKey: API_KEY,
|
||||||
model: MODEL,
|
model: MODEL,
|
||||||
|
userAgent: USER_AGENT,
|
||||||
|
},
|
||||||
embeddingModel: EMBEDDING_MODEL,
|
embeddingModel: EMBEDDING_MODEL,
|
||||||
sandbox: SANDBOX,
|
sandbox: SANDBOX,
|
||||||
targetDir: TARGET_DIR,
|
targetDir: TARGET_DIR,
|
||||||
debugMode: DEBUG_MODE,
|
debugMode: DEBUG_MODE,
|
||||||
question: QUESTION,
|
question: QUESTION,
|
||||||
fullContext: FULL_CONTEXT,
|
fullContext: FULL_CONTEXT,
|
||||||
userAgent: USER_AGENT,
|
|
||||||
userMemory: USER_MEMORY,
|
userMemory: USER_MEMORY,
|
||||||
telemetry: TELEMETRY,
|
telemetry: TELEMETRY,
|
||||||
};
|
};
|
||||||
|
@ -73,10 +75,7 @@ describe('Server Config (config.ts)', () => {
|
||||||
|
|
||||||
expect(config.getUserMemory()).toBe(USER_MEMORY);
|
expect(config.getUserMemory()).toBe(USER_MEMORY);
|
||||||
// Verify other getters if needed
|
// Verify other getters if needed
|
||||||
expect(config.getApiKey()).toBe(API_KEY);
|
|
||||||
expect(config.getModel()).toBe(MODEL);
|
|
||||||
expect(config.getTargetDir()).toBe(path.resolve(TARGET_DIR)); // Check resolved path
|
expect(config.getTargetDir()).toBe(path.resolve(TARGET_DIR)); // Check resolved path
|
||||||
expect(config.getUserAgent()).toBe(USER_AGENT);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Config constructor should default userMemory to empty string if not provided', () => {
|
it('Config constructor should default userMemory to empty string if not provided', () => {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import * as fs from 'node:fs';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import * as os from 'node:os';
|
import * as os from 'node:os';
|
||||||
|
import { ContentGeneratorConfig } from '../core/contentGenerator.js';
|
||||||
import { ToolRegistry } from '../tools/tool-registry.js';
|
import { ToolRegistry } from '../tools/tool-registry.js';
|
||||||
import { CodeParserTool } from '../tools/code_parser.js'; // Added CodeParserTool
|
import { CodeParserTool } from '../tools/code_parser.js'; // Added CodeParserTool
|
||||||
import { LSTool } from '../tools/ls.js';
|
import { LSTool } from '../tools/ls.js';
|
||||||
|
@ -17,7 +18,6 @@ import { GrepTool } from '../tools/grep.js';
|
||||||
import { GlobTool } from '../tools/glob.js';
|
import { GlobTool } from '../tools/glob.js';
|
||||||
import { EditTool } from '../tools/edit.js';
|
import { EditTool } from '../tools/edit.js';
|
||||||
import { ShellTool } from '../tools/shell.js';
|
import { ShellTool } from '../tools/shell.js';
|
||||||
|
|
||||||
import { WebFetchTool } from '../tools/web-fetch.js';
|
import { WebFetchTool } from '../tools/web-fetch.js';
|
||||||
import { ReadManyFilesTool } from '../tools/read-many-files.js';
|
import { ReadManyFilesTool } from '../tools/read-many-files.js';
|
||||||
import { MemoryTool, setGeminiMdFilename } from '../tools/memoryTool.js';
|
import { MemoryTool, setGeminiMdFilename } from '../tools/memoryTool.js';
|
||||||
|
@ -55,8 +55,7 @@ export class MCPServerConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfigParameters {
|
export interface ConfigParameters {
|
||||||
apiKey: string;
|
contentGeneratorConfig: ContentGeneratorConfig;
|
||||||
model: string;
|
|
||||||
embeddingModel: string;
|
embeddingModel: string;
|
||||||
sandbox: boolean | string;
|
sandbox: boolean | string;
|
||||||
targetDir: string;
|
targetDir: string;
|
||||||
|
@ -68,11 +67,9 @@ export interface ConfigParameters {
|
||||||
toolCallCommand?: string;
|
toolCallCommand?: string;
|
||||||
mcpServerCommand?: string;
|
mcpServerCommand?: string;
|
||||||
mcpServers?: Record<string, MCPServerConfig>;
|
mcpServers?: Record<string, MCPServerConfig>;
|
||||||
userAgent: string;
|
|
||||||
userMemory?: string;
|
userMemory?: string;
|
||||||
geminiMdFileCount?: number;
|
geminiMdFileCount?: number;
|
||||||
approvalMode?: ApprovalMode;
|
approvalMode?: ApprovalMode;
|
||||||
vertexai?: boolean;
|
|
||||||
showMemoryUsage?: boolean;
|
showMemoryUsage?: boolean;
|
||||||
contextFileName?: string;
|
contextFileName?: string;
|
||||||
geminiIgnorePatterns?: string[];
|
geminiIgnorePatterns?: string[];
|
||||||
|
@ -85,8 +82,7 @@ export interface ConfigParameters {
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
private toolRegistry: Promise<ToolRegistry>;
|
private toolRegistry: Promise<ToolRegistry>;
|
||||||
private readonly apiKey: string;
|
private readonly contentGeneratorConfig: ContentGeneratorConfig;
|
||||||
private readonly model: string;
|
|
||||||
private readonly embeddingModel: string;
|
private readonly embeddingModel: string;
|
||||||
private readonly sandbox: boolean | string;
|
private readonly sandbox: boolean | string;
|
||||||
private readonly targetDir: string;
|
private readonly targetDir: string;
|
||||||
|
@ -98,11 +94,9 @@ export class Config {
|
||||||
private readonly toolCallCommand: string | undefined;
|
private readonly toolCallCommand: string | undefined;
|
||||||
private readonly mcpServerCommand: string | undefined;
|
private readonly mcpServerCommand: string | undefined;
|
||||||
private readonly mcpServers: Record<string, MCPServerConfig> | undefined;
|
private readonly mcpServers: Record<string, MCPServerConfig> | undefined;
|
||||||
private readonly userAgent: string;
|
|
||||||
private userMemory: string;
|
private userMemory: string;
|
||||||
private geminiMdFileCount: number;
|
private geminiMdFileCount: number;
|
||||||
private approvalMode: ApprovalMode;
|
private approvalMode: ApprovalMode;
|
||||||
private readonly vertexai: boolean | undefined;
|
|
||||||
private readonly showMemoryUsage: boolean;
|
private readonly showMemoryUsage: boolean;
|
||||||
private readonly accessibility: AccessibilitySettings;
|
private readonly accessibility: AccessibilitySettings;
|
||||||
private readonly telemetry: boolean;
|
private readonly telemetry: boolean;
|
||||||
|
@ -115,8 +109,7 @@ export class Config {
|
||||||
private fileDiscoveryService: FileDiscoveryService | null = null;
|
private fileDiscoveryService: FileDiscoveryService | null = null;
|
||||||
|
|
||||||
constructor(params: ConfigParameters) {
|
constructor(params: ConfigParameters) {
|
||||||
this.apiKey = params.apiKey;
|
this.contentGeneratorConfig = params.contentGeneratorConfig;
|
||||||
this.model = params.model;
|
|
||||||
this.embeddingModel = params.embeddingModel;
|
this.embeddingModel = params.embeddingModel;
|
||||||
this.sandbox = params.sandbox;
|
this.sandbox = params.sandbox;
|
||||||
this.targetDir = path.resolve(params.targetDir);
|
this.targetDir = path.resolve(params.targetDir);
|
||||||
|
@ -128,11 +121,9 @@ export class Config {
|
||||||
this.toolCallCommand = params.toolCallCommand;
|
this.toolCallCommand = params.toolCallCommand;
|
||||||
this.mcpServerCommand = params.mcpServerCommand;
|
this.mcpServerCommand = params.mcpServerCommand;
|
||||||
this.mcpServers = params.mcpServers;
|
this.mcpServers = params.mcpServers;
|
||||||
this.userAgent = params.userAgent ?? 'GeminiCLI/unknown';
|
|
||||||
this.userMemory = params.userMemory ?? '';
|
this.userMemory = params.userMemory ?? '';
|
||||||
this.geminiMdFileCount = params.geminiMdFileCount ?? 0;
|
this.geminiMdFileCount = params.geminiMdFileCount ?? 0;
|
||||||
this.approvalMode = params.approvalMode ?? ApprovalMode.DEFAULT;
|
this.approvalMode = params.approvalMode ?? ApprovalMode.DEFAULT;
|
||||||
this.vertexai = params.vertexai;
|
|
||||||
this.showMemoryUsage = params.showMemoryUsage ?? false;
|
this.showMemoryUsage = params.showMemoryUsage ?? false;
|
||||||
this.accessibility = params.accessibility ?? {};
|
this.accessibility = params.accessibility ?? {};
|
||||||
this.telemetry = params.telemetry ?? false;
|
this.telemetry = params.telemetry ?? false;
|
||||||
|
@ -160,12 +151,12 @@ export class Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getApiKey(): string {
|
getContentGeneratorConfig(): ContentGeneratorConfig {
|
||||||
return this.apiKey;
|
return this.contentGeneratorConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
getModel(): string {
|
getModel(): string {
|
||||||
return this.model;
|
return this.contentGeneratorConfig.model;
|
||||||
}
|
}
|
||||||
|
|
||||||
getEmbeddingModel(): string {
|
getEmbeddingModel(): string {
|
||||||
|
@ -215,10 +206,6 @@ export class Config {
|
||||||
return this.mcpServers;
|
return this.mcpServers;
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserAgent(): string {
|
|
||||||
return this.userAgent;
|
|
||||||
}
|
|
||||||
|
|
||||||
getUserMemory(): string {
|
getUserMemory(): string {
|
||||||
return this.userMemory;
|
return this.userMemory;
|
||||||
}
|
}
|
||||||
|
@ -243,10 +230,6 @@ export class Config {
|
||||||
this.approvalMode = mode;
|
this.approvalMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
getVertexAI(): boolean | undefined {
|
|
||||||
return this.vertexai;
|
|
||||||
}
|
|
||||||
|
|
||||||
getShowMemoryUsage(): boolean {
|
getShowMemoryUsage(): boolean {
|
||||||
return this.showMemoryUsage;
|
return this.showMemoryUsage;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,11 @@ describe('Gemini Client (client.ts)', () => {
|
||||||
const MockedConfig = vi.mocked(Config, true);
|
const MockedConfig = vi.mocked(Config, true);
|
||||||
MockedConfig.mockImplementation(() => {
|
MockedConfig.mockImplementation(() => {
|
||||||
const mock = {
|
const mock = {
|
||||||
|
getContentGeneratorConfig: vi.fn().mockReturnValue({
|
||||||
|
model: 'test-model',
|
||||||
|
apiKey: 'test-key',
|
||||||
|
vertexai: false,
|
||||||
|
}),
|
||||||
getToolRegistry: vi.fn().mockResolvedValue(mockToolRegistry),
|
getToolRegistry: vi.fn().mockResolvedValue(mockToolRegistry),
|
||||||
getModel: vi.fn().mockReturnValue('test-model'),
|
getModel: vi.fn().mockReturnValue('test-model'),
|
||||||
getEmbeddingModel: vi.fn().mockReturnValue('test-embedding-model'),
|
getEmbeddingModel: vi.fn().mockReturnValue('test-embedding-model'),
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {
|
||||||
EmbedContentResponse,
|
EmbedContentResponse,
|
||||||
EmbedContentParameters,
|
EmbedContentParameters,
|
||||||
GenerateContentConfig,
|
GenerateContentConfig,
|
||||||
GoogleGenAI,
|
|
||||||
Part,
|
Part,
|
||||||
SchemaUnion,
|
SchemaUnion,
|
||||||
PartListUnion,
|
PartListUnion,
|
||||||
|
@ -34,7 +33,10 @@ import {
|
||||||
logApiResponse,
|
logApiResponse,
|
||||||
logApiError,
|
logApiError,
|
||||||
} from '../telemetry/index.js';
|
} from '../telemetry/index.js';
|
||||||
import { ContentGenerator } from './contentGenerator.js';
|
import {
|
||||||
|
ContentGenerator,
|
||||||
|
createContentGenerator,
|
||||||
|
} from './contentGenerator.js';
|
||||||
|
|
||||||
export class GeminiClient {
|
export class GeminiClient {
|
||||||
private chat: Promise<GeminiChat>;
|
private chat: Promise<GeminiChat>;
|
||||||
|
@ -48,20 +50,9 @@ export class GeminiClient {
|
||||||
private readonly MAX_TURNS = 100;
|
private readonly MAX_TURNS = 100;
|
||||||
|
|
||||||
constructor(private config: Config) {
|
constructor(private config: Config) {
|
||||||
const userAgent = config.getUserAgent();
|
this.contentGenerator = createContentGenerator(
|
||||||
const apiKeyFromConfig = config.getApiKey();
|
this.config.getContentGeneratorConfig(),
|
||||||
const vertexaiFlag = config.getVertexAI();
|
);
|
||||||
|
|
||||||
const googleGenAI = new GoogleGenAI({
|
|
||||||
apiKey: apiKeyFromConfig === '' ? undefined : apiKeyFromConfig,
|
|
||||||
vertexai: vertexaiFlag,
|
|
||||||
httpOptions: {
|
|
||||||
headers: {
|
|
||||||
'User-Agent': userAgent,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.contentGenerator = googleGenAI.models;
|
|
||||||
this.model = config.getModel();
|
this.model = config.getModel();
|
||||||
this.embeddingModel = config.getEmbeddingModel();
|
this.embeddingModel = config.getEmbeddingModel();
|
||||||
this.chat = this.startChat();
|
this.chat = this.startChat();
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
CountTokensParameters,
|
CountTokensParameters,
|
||||||
EmbedContentResponse,
|
EmbedContentResponse,
|
||||||
EmbedContentParameters,
|
EmbedContentParameters,
|
||||||
|
GoogleGenAI,
|
||||||
} from '@google/genai';
|
} from '@google/genai';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,3 +30,25 @@ export interface ContentGenerator {
|
||||||
|
|
||||||
embedContent(request: EmbedContentParameters): Promise<EmbedContentResponse>;
|
embedContent(request: EmbedContentParameters): Promise<EmbedContentResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ContentGeneratorConfig = {
|
||||||
|
model: string;
|
||||||
|
apiKey?: string;
|
||||||
|
vertexai?: boolean;
|
||||||
|
userAgent: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createContentGenerator(
|
||||||
|
config: ContentGeneratorConfig,
|
||||||
|
): ContentGenerator {
|
||||||
|
const googleGenAI = new GoogleGenAI({
|
||||||
|
apiKey: config.apiKey === '' ? undefined : config.apiKey,
|
||||||
|
vertexai: config.vertexai,
|
||||||
|
httpOptions: {
|
||||||
|
headers: {
|
||||||
|
'User-Agent': config.userAgent,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return googleGenAI.models;
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ export * from './config/config.js';
|
||||||
|
|
||||||
// Export Core Logic
|
// Export Core Logic
|
||||||
export * from './core/client.js';
|
export * from './core/client.js';
|
||||||
|
export * from './core/contentGenerator.js';
|
||||||
export * from './core/geminiChat.js';
|
export * from './core/geminiChat.js';
|
||||||
export * from './core/logger.js';
|
export * from './core/logger.js';
|
||||||
export * from './core/prompts.js';
|
export * from './core/prompts.js';
|
||||||
|
|
|
@ -45,7 +45,7 @@ export function logCliConfiguration(config: Config): void {
|
||||||
typeof config.getSandbox() === 'string' ? true : config.getSandbox(),
|
typeof config.getSandbox() === 'string' ? true : config.getSandbox(),
|
||||||
core_tools_enabled: (config.getCoreTools() ?? []).join(','),
|
core_tools_enabled: (config.getCoreTools() ?? []).join(','),
|
||||||
approval_mode: config.getApprovalMode(),
|
approval_mode: config.getApprovalMode(),
|
||||||
vertex_ai_enabled: config.getVertexAI() ?? false,
|
vertex_ai_enabled: !!config.getContentGeneratorConfig().vertexai,
|
||||||
log_user_prompts_enabled: config.getTelemetryLogUserPromptsEnabled(),
|
log_user_prompts_enabled: config.getTelemetryLogUserPromptsEnabled(),
|
||||||
file_filtering_respect_git_ignore:
|
file_filtering_respect_git_ignore:
|
||||||
config.getFileFilteringRespectGitIgnore(),
|
config.getFileFilteringRespectGitIgnore(),
|
||||||
|
|
|
@ -64,7 +64,7 @@ export function initializeTelemetry(config: Config): void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const geminiCliVersion = config.getUserAgent() || 'unknown';
|
const geminiCliVersion = config.getContentGeneratorConfig().userAgent;
|
||||||
const resource = new Resource({
|
const resource = new Resource({
|
||||||
[SemanticResourceAttributes.SERVICE_NAME]: SERVICE_NAME,
|
[SemanticResourceAttributes.SERVICE_NAME]: SERVICE_NAME,
|
||||||
[SemanticResourceAttributes.SERVICE_VERSION]: geminiCliVersion,
|
[SemanticResourceAttributes.SERVICE_VERSION]: geminiCliVersion,
|
||||||
|
|
|
@ -124,17 +124,19 @@ class MockTool extends BaseTool<{ param: string }, ToolResult> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseConfigParams: ConfigParameters = {
|
const baseConfigParams: ConfigParameters = {
|
||||||
apiKey: 'test-api-key',
|
contentGeneratorConfig: {
|
||||||
model: 'test-model',
|
model: 'test-model',
|
||||||
|
apiKey: 'test-api-key',
|
||||||
|
vertexai: false,
|
||||||
|
userAgent: 'TestAgent/1.0',
|
||||||
|
},
|
||||||
embeddingModel: 'test-embedding-model',
|
embeddingModel: 'test-embedding-model',
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
targetDir: '/test/dir',
|
targetDir: '/test/dir',
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
userAgent: 'TestAgent/1.0',
|
|
||||||
userMemory: '',
|
userMemory: '',
|
||||||
geminiMdFileCount: 0,
|
geminiMdFileCount: 0,
|
||||||
approvalMode: ApprovalMode.DEFAULT,
|
approvalMode: ApprovalMode.DEFAULT,
|
||||||
vertexai: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('ToolRegistry', () => {
|
describe('ToolRegistry', () => {
|
||||||
|
|
Loading…
Reference in New Issue