c307cd3a7c
- 扩展 AgentMode 类型添加 'internal' 模式 - 新增 summary agent preset (claude-3-5-haiku) - AgentRegistry 添加 getInternal/listInternalAgents 方法 - CompressionManager 添加 setSummaryModelFromAgentConfig - Agent 构造函数改用 Registry 配置初始化 Summary 模型 - 清理旧的 SummaryConfig 配置系统 - UI AgentsPanel 分离显示 System/Preset/Custom agents - UI AgentEditor 为 internal agent 显示简化编辑界面
172 lines
5.0 KiB
TypeScript
172 lines
5.0 KiB
TypeScript
import * as fs from 'fs';
|
||
import * as path from 'path';
|
||
import * as os from 'os';
|
||
import type { AgentConfig, ProviderType } from '../types/index.js';
|
||
import { providerRegistry, resolveApiKey } from '../provider/index.js';
|
||
|
||
const CONFIG_DIR = path.join(os.homedir(), '.ai-terminal-assistant');
|
||
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
||
|
||
interface StoredConfig {
|
||
provider?: ProviderType;
|
||
model?: string;
|
||
maxTokens?: number;
|
||
tavilyApiKey?: string;
|
||
/** 自定义 API 基础 URL(用于 OpenAI 兼容服务,如阿里云百炼) */
|
||
baseUrl?: string;
|
||
// Vision 配置
|
||
visionProvider?: ProviderType;
|
||
visionModel?: string;
|
||
/** Vision 专用的 Base URL(用于 OpenAI 兼容的 Vision 服务) */
|
||
visionBaseUrl?: string;
|
||
}
|
||
|
||
// Vision 配置接口
|
||
export interface VisionConfig {
|
||
provider: ProviderType;
|
||
apiKey: string;
|
||
model: string;
|
||
/** 自定义 Base URL(用于 OpenAI 兼容的 Vision 服务) */
|
||
baseUrl?: string;
|
||
}
|
||
|
||
// 默认模型配置
|
||
const DEFAULT_MODELS: Record<ProviderType, string> = {
|
||
anthropic: 'claude-sonnet-4-20250514',
|
||
deepseek: 'deepseek-chat',
|
||
openai: 'gpt-4o',
|
||
};
|
||
|
||
// 默认 Vision 模型(需要支持图片理解)
|
||
const DEFAULT_VISION_MODELS: Record<ProviderType, string> = {
|
||
anthropic: 'claude-sonnet-4-20250514',
|
||
deepseek: 'deepseek-chat', // DeepSeek 暂不支持 vision,占位用
|
||
openai: 'gpt-4o',
|
||
};
|
||
|
||
// 默认系统提示词
|
||
const DEFAULT_SYSTEM_PROMPT = `你是一个运行在终端中的 AI 编程助手。你可以帮助用户:
|
||
- 读取和写入文件
|
||
- 执行 bash 命令
|
||
- 搜索代码和文件
|
||
- 回答编程问题
|
||
|
||
使用工具时请注意:
|
||
1. 在修改文件前,先读取文件内容
|
||
2. 执行可能有风险的命令前,先向用户确认
|
||
3. 给出清晰、简洁的回答
|
||
|
||
重要的工具使用规则:
|
||
- 创建或修改文件时,必须使用 write_file 或 edit_file 工具,不要使用 bash 命令(如 cat、echo 等)
|
||
- write_file 和 edit_file 工具集成了代码诊断功能,可以在写入后自动检查代码错误
|
||
- bash 工具仅用于运行命令、安装依赖、执行脚本等操作,不要用于文件内容的创建和修改
|
||
|
||
当前工作目录: ${process.cwd()}
|
||
操作系统: ${process.platform}`;
|
||
|
||
// 获取原始配置(包含所有字段)
|
||
export function getConfig(): StoredConfig {
|
||
if (fs.existsSync(CONFIG_FILE)) {
|
||
try {
|
||
const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
||
return JSON.parse(content);
|
||
} catch {
|
||
return {};
|
||
}
|
||
}
|
||
return {};
|
||
}
|
||
|
||
// 加载配置
|
||
export function loadConfig(): AgentConfig {
|
||
// 从配置文件读取
|
||
const storedConfig = getConfig();
|
||
|
||
// 确定最终的 provider
|
||
const finalProvider = storedConfig.provider || 'anthropic';
|
||
|
||
// 通过 ProviderRegistry 获取 API Key
|
||
const providerConfig = providerRegistry.getConfig(finalProvider);
|
||
const finalApiKey = resolveApiKey(providerConfig);
|
||
|
||
if (!finalApiKey) {
|
||
console.error(`❌ 错误: 未配置 API Key`);
|
||
console.error(`请在设置中配置 ${finalProvider} 的 API Key`);
|
||
process.exit(1);
|
||
}
|
||
|
||
// 确定模型
|
||
const finalModel = storedConfig.model || DEFAULT_MODELS[finalProvider];
|
||
|
||
// 确定 baseUrl
|
||
const finalBaseUrl = storedConfig.baseUrl || providerConfig?.baseUrl;
|
||
|
||
return {
|
||
provider: finalProvider,
|
||
apiKey: finalApiKey,
|
||
model: finalModel,
|
||
maxTokens: storedConfig.maxTokens || 4096,
|
||
systemPrompt: DEFAULT_SYSTEM_PROMPT,
|
||
baseUrl: finalBaseUrl,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 加载 Vision 配置
|
||
* Vision 用于图片理解,当主模型不支持 vision 时使用
|
||
* 通过 ProviderRegistry 获取 API Key
|
||
*/
|
||
export function loadVisionConfig(): VisionConfig | null {
|
||
// 从配置文件读取
|
||
const storedConfig = getConfig();
|
||
|
||
// 确定 vision provider(默认使用 anthropic,因为 Claude 支持 vision)
|
||
const finalProvider = storedConfig.visionProvider || 'anthropic';
|
||
|
||
// 通过 ProviderRegistry 获取 API Key
|
||
const providerConfig = providerRegistry.getConfig(finalProvider);
|
||
const finalApiKey = resolveApiKey(providerConfig);
|
||
|
||
// 如果没有 API Key,返回 null
|
||
if (!finalApiKey) {
|
||
return null;
|
||
}
|
||
|
||
// 确定模型
|
||
const finalModel = storedConfig.visionModel || DEFAULT_VISION_MODELS[finalProvider];
|
||
|
||
// 确定 baseUrl
|
||
const finalBaseUrl = storedConfig.visionBaseUrl || providerConfig?.baseUrl;
|
||
|
||
return {
|
||
provider: finalProvider,
|
||
apiKey: finalApiKey,
|
||
model: finalModel,
|
||
baseUrl: finalBaseUrl,
|
||
};
|
||
}
|
||
|
||
// 保存配置
|
||
export function saveConfig(config: Partial<StoredConfig>): void {
|
||
// 确保目录存在
|
||
if (!fs.existsSync(CONFIG_DIR)) {
|
||
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
||
}
|
||
|
||
// 读取现有配置
|
||
let existingConfig: StoredConfig = {};
|
||
if (fs.existsSync(CONFIG_FILE)) {
|
||
try {
|
||
const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
||
existingConfig = JSON.parse(content);
|
||
} catch {
|
||
// 忽略
|
||
}
|
||
}
|
||
|
||
// 合并并保存
|
||
const newConfig = { ...existingConfig, ...config };
|
||
fs.writeFileSync(CONFIG_FILE, JSON.stringify(newConfig, null, 2));
|
||
}
|
||
|