feat(core): Resolve GEMINI_SYSTEM_MD case-insensitively (#4538)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
parent
e306b34a6e
commit
5dce1df5db
|
@ -4,9 +4,13 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { getCoreSystemPrompt } from './prompts.js';
|
||||
import { isGitRepository } from '../utils/gitUtils.js';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { GEMINI_CONFIG_DIR } from '../tools/memoryTool.js';
|
||||
|
||||
// Mock tool names if they are dynamically generated or complex
|
||||
vi.mock('../tools/ls', () => ({ LSTool: { Name: 'list_directory' } }));
|
||||
|
@ -26,8 +30,15 @@ vi.mock('../tools/write-file', () => ({
|
|||
vi.mock('../utils/gitUtils', () => ({
|
||||
isGitRepository: vi.fn(),
|
||||
}));
|
||||
vi.mock('node:fs');
|
||||
|
||||
describe('Core System Prompt (prompts.ts)', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.stubEnv('GEMINI_SYSTEM_MD', undefined);
|
||||
vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', undefined);
|
||||
});
|
||||
|
||||
it('should return the base prompt when no userMemory is provided', () => {
|
||||
vi.stubEnv('SANDBOX', undefined);
|
||||
const prompt = getCoreSystemPrompt();
|
||||
|
@ -105,4 +116,157 @@ describe('Core System Prompt (prompts.ts)', () => {
|
|||
expect(prompt).not.toContain('# Git Repository');
|
||||
expect(prompt).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('GEMINI_SYSTEM_MD environment variable', () => {
|
||||
it('should use default prompt when GEMINI_SYSTEM_MD is "false"', () => {
|
||||
vi.stubEnv('GEMINI_SYSTEM_MD', 'false');
|
||||
const prompt = getCoreSystemPrompt();
|
||||
expect(fs.readFileSync).not.toHaveBeenCalled();
|
||||
expect(prompt).not.toContain('custom system prompt');
|
||||
});
|
||||
|
||||
it('should use default prompt when GEMINI_SYSTEM_MD is "0"', () => {
|
||||
vi.stubEnv('GEMINI_SYSTEM_MD', '0');
|
||||
const prompt = getCoreSystemPrompt();
|
||||
expect(fs.readFileSync).not.toHaveBeenCalled();
|
||||
expect(prompt).not.toContain('custom system prompt');
|
||||
});
|
||||
|
||||
it('should throw error if GEMINI_SYSTEM_MD points to a non-existent file', () => {
|
||||
const customPath = '/non/existent/path/system.md';
|
||||
vi.stubEnv('GEMINI_SYSTEM_MD', customPath);
|
||||
vi.mocked(fs.existsSync).mockReturnValue(false);
|
||||
expect(() => getCoreSystemPrompt()).toThrow(
|
||||
`missing system prompt file '${path.resolve(customPath)}'`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should read from default path when GEMINI_SYSTEM_MD is "true"', () => {
|
||||
const defaultPath = path.resolve(
|
||||
path.join(GEMINI_CONFIG_DIR, 'system.md'),
|
||||
);
|
||||
vi.stubEnv('GEMINI_SYSTEM_MD', 'true');
|
||||
vi.mocked(fs.existsSync).mockReturnValue(true);
|
||||
vi.mocked(fs.readFileSync).mockReturnValue('custom system prompt');
|
||||
|
||||
const prompt = getCoreSystemPrompt();
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith(defaultPath, 'utf8');
|
||||
expect(prompt).toBe('custom system prompt');
|
||||
});
|
||||
|
||||
it('should read from default path when GEMINI_SYSTEM_MD is "1"', () => {
|
||||
const defaultPath = path.resolve(
|
||||
path.join(GEMINI_CONFIG_DIR, 'system.md'),
|
||||
);
|
||||
vi.stubEnv('GEMINI_SYSTEM_MD', '1');
|
||||
vi.mocked(fs.existsSync).mockReturnValue(true);
|
||||
vi.mocked(fs.readFileSync).mockReturnValue('custom system prompt');
|
||||
|
||||
const prompt = getCoreSystemPrompt();
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith(defaultPath, 'utf8');
|
||||
expect(prompt).toBe('custom system prompt');
|
||||
});
|
||||
|
||||
it('should read from custom path when GEMINI_SYSTEM_MD provides one, preserving case', () => {
|
||||
const customPath = path.resolve('/custom/path/SyStEm.Md');
|
||||
vi.stubEnv('GEMINI_SYSTEM_MD', customPath);
|
||||
vi.mocked(fs.existsSync).mockReturnValue(true);
|
||||
vi.mocked(fs.readFileSync).mockReturnValue('custom system prompt');
|
||||
|
||||
const prompt = getCoreSystemPrompt();
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith(customPath, 'utf8');
|
||||
expect(prompt).toBe('custom system prompt');
|
||||
});
|
||||
|
||||
it('should expand tilde in custom path when GEMINI_SYSTEM_MD is set', () => {
|
||||
const homeDir = '/Users/test';
|
||||
vi.spyOn(os, 'homedir').mockReturnValue(homeDir);
|
||||
const customPath = '~/custom/system.md';
|
||||
const expectedPath = path.join(homeDir, 'custom/system.md');
|
||||
vi.stubEnv('GEMINI_SYSTEM_MD', customPath);
|
||||
vi.mocked(fs.existsSync).mockReturnValue(true);
|
||||
vi.mocked(fs.readFileSync).mockReturnValue('custom system prompt');
|
||||
|
||||
const prompt = getCoreSystemPrompt();
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith(
|
||||
path.resolve(expectedPath),
|
||||
'utf8',
|
||||
);
|
||||
expect(prompt).toBe('custom system prompt');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GEMINI_WRITE_SYSTEM_MD environment variable', () => {
|
||||
it('should not write to file when GEMINI_WRITE_SYSTEM_MD is "false"', () => {
|
||||
vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', 'false');
|
||||
getCoreSystemPrompt();
|
||||
expect(fs.writeFileSync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not write to file when GEMINI_WRITE_SYSTEM_MD is "0"', () => {
|
||||
vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', '0');
|
||||
getCoreSystemPrompt();
|
||||
expect(fs.writeFileSync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should write to default path when GEMINI_WRITE_SYSTEM_MD is "true"', () => {
|
||||
const defaultPath = path.resolve(
|
||||
path.join(GEMINI_CONFIG_DIR, 'system.md'),
|
||||
);
|
||||
vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', 'true');
|
||||
getCoreSystemPrompt();
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
defaultPath,
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
|
||||
it('should write to default path when GEMINI_WRITE_SYSTEM_MD is "1"', () => {
|
||||
const defaultPath = path.resolve(
|
||||
path.join(GEMINI_CONFIG_DIR, 'system.md'),
|
||||
);
|
||||
vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', '1');
|
||||
getCoreSystemPrompt();
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
defaultPath,
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
|
||||
it('should write to custom path when GEMINI_WRITE_SYSTEM_MD provides one', () => {
|
||||
const customPath = path.resolve('/custom/path/system.md');
|
||||
vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', customPath);
|
||||
getCoreSystemPrompt();
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
customPath,
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
|
||||
it('should expand tilde in custom path when GEMINI_WRITE_SYSTEM_MD is set', () => {
|
||||
const homeDir = '/Users/test';
|
||||
vi.spyOn(os, 'homedir').mockReturnValue(homeDir);
|
||||
const customPath = '~/custom/system.md';
|
||||
const expectedPath = path.join(homeDir, 'custom/system.md');
|
||||
vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', customPath);
|
||||
getCoreSystemPrompt();
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
path.resolve(expectedPath),
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
|
||||
it('should expand tilde in custom path when GEMINI_WRITE_SYSTEM_MD is just ~', () => {
|
||||
const homeDir = '/Users/test';
|
||||
vi.spyOn(os, 'homedir').mockReturnValue(homeDir);
|
||||
const customPath = '~';
|
||||
const expectedPath = homeDir;
|
||||
vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', customPath);
|
||||
getCoreSystemPrompt();
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
path.resolve(expectedPath),
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import { LSTool } from '../tools/ls.js';
|
||||
import { EditTool } from '../tools/edit.js';
|
||||
import { GlobTool } from '../tools/glob.js';
|
||||
|
@ -23,15 +24,24 @@ export function getCoreSystemPrompt(userMemory?: string): string {
|
|||
// default path is .gemini/system.md but can be modified via custom path in GEMINI_SYSTEM_MD
|
||||
let systemMdEnabled = false;
|
||||
let systemMdPath = path.resolve(path.join(GEMINI_CONFIG_DIR, 'system.md'));
|
||||
const systemMdVar = process.env.GEMINI_SYSTEM_MD?.toLowerCase();
|
||||
if (systemMdVar && !['0', 'false'].includes(systemMdVar)) {
|
||||
systemMdEnabled = true; // enable system prompt override
|
||||
if (!['1', 'true'].includes(systemMdVar)) {
|
||||
systemMdPath = path.resolve(systemMdVar); // use custom path from GEMINI_SYSTEM_MD
|
||||
}
|
||||
// require file to exist when override is enabled
|
||||
if (!fs.existsSync(systemMdPath)) {
|
||||
throw new Error(`missing system prompt file '${systemMdPath}'`);
|
||||
const systemMdVar = process.env.GEMINI_SYSTEM_MD;
|
||||
if (systemMdVar) {
|
||||
const systemMdVarLower = systemMdVar.toLowerCase();
|
||||
if (!['0', 'false'].includes(systemMdVarLower)) {
|
||||
systemMdEnabled = true; // enable system prompt override
|
||||
if (!['1', 'true'].includes(systemMdVarLower)) {
|
||||
let customPath = systemMdVar;
|
||||
if (customPath.startsWith('~/')) {
|
||||
customPath = path.join(os.homedir(), customPath.slice(2));
|
||||
} else if (customPath === '~') {
|
||||
customPath = os.homedir();
|
||||
}
|
||||
systemMdPath = path.resolve(customPath); // use custom path from GEMINI_SYSTEM_MD
|
||||
}
|
||||
// require file to exist when override is enabled
|
||||
if (!fs.existsSync(systemMdPath)) {
|
||||
throw new Error(`missing system prompt file '${systemMdPath}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
const basePrompt = systemMdEnabled
|
||||
|
@ -256,12 +266,24 @@ Your core function is efficient and safe assistance. Balance extreme conciseness
|
|||
`.trim();
|
||||
|
||||
// if GEMINI_WRITE_SYSTEM_MD is set (and not 0|false), write base system prompt to file
|
||||
const writeSystemMdVar = process.env.GEMINI_WRITE_SYSTEM_MD?.toLowerCase();
|
||||
if (writeSystemMdVar && !['0', 'false'].includes(writeSystemMdVar)) {
|
||||
if (['1', 'true'].includes(writeSystemMdVar)) {
|
||||
fs.writeFileSync(systemMdPath, basePrompt); // write to default path, can be modified via GEMINI_SYSTEM_MD
|
||||
} else {
|
||||
fs.writeFileSync(path.resolve(writeSystemMdVar), basePrompt); // write to custom path from GEMINI_WRITE_SYSTEM_MD
|
||||
const writeSystemMdVar = process.env.GEMINI_WRITE_SYSTEM_MD;
|
||||
if (writeSystemMdVar) {
|
||||
const writeSystemMdVarLower = writeSystemMdVar.toLowerCase();
|
||||
if (!['0', 'false'].includes(writeSystemMdVarLower)) {
|
||||
if (['1', 'true'].includes(writeSystemMdVarLower)) {
|
||||
fs.mkdirSync(path.dirname(systemMdPath), { recursive: true });
|
||||
fs.writeFileSync(systemMdPath, basePrompt); // write to default path, can be modified via GEMINI_SYSTEM_MD
|
||||
} else {
|
||||
let customPath = writeSystemMdVar;
|
||||
if (customPath.startsWith('~/')) {
|
||||
customPath = path.join(os.homedir(), customPath.slice(2));
|
||||
} else if (customPath === '~') {
|
||||
customPath = os.homedir();
|
||||
}
|
||||
const resolvedPath = path.resolve(customPath);
|
||||
fs.mkdirSync(path.dirname(resolvedPath), { recursive: true });
|
||||
fs.writeFileSync(resolvedPath, basePrompt); // write to custom path from GEMINI_WRITE_SYSTEM_MD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue