Files
ai-terminal-assistant/packages/core/src/utils/config.ts
T
kurihada 9376887995 refactor(core): 统一配置系统,移除 config.json
- 移除 config.json,所有配置统一从 agents.json 和 providers.json 读取
- config-loader.ts 从全局目录 ~/.ai-terminal-assistant/ 加载配置
- loadConfig() 从 agentRegistry.getGlobalConfig() 获取 defaults.model
- 添加 loadVisionConfig() 支持 Vision 模型配置
- Tavily API Key 仅从环境变量读取
- UI AgentDefaultsEditor 添加 Vision 模型配置界面
- 更新相关测试
2025-12-16 00:33:29 +08:00

213 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Agent 配置加载
*
* 从 agents.json 的 defaults.model 读取全局模型配置
* 从 providers.json 读取 API Key 和 baseUrl
*/
import type { AgentConfig, ProviderType } from '../types/index.js';
import { providerRegistry, resolveApiKey } from '../provider/index.js';
import { agentRegistry } from '../agent/registry.js';
import type { AgentModelConfig } from '../agent/types.js';
/**
* 配置错误异常
*
* 当配置缺失或无效时抛出,用于替代 process.exit()
* 允许调用方优雅地处理错误并向用户显示友好提示
*/
export class ConfigurationError extends Error {
constructor(
message: string,
public readonly provider: string,
public readonly missingKey: 'apiKey' | 'provider'
) {
super(message);
this.name = 'ConfigurationError';
}
}
// 默认模型配置
const DEFAULT_MODELS: Record<ProviderType, string> = {
anthropic: 'claude-sonnet-4-20250514',
deepseek: 'deepseek-chat',
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}`;
/**
* 查找第一个已配置 API Key 的 Provider
* 作为未配置 defaults.model 时的 fallback
*/
function findFirstConfiguredProvider(): ProviderType | null {
const providers: ProviderType[] = ['anthropic', 'openai', 'deepseek'];
for (const providerId of providers) {
const config = providerRegistry.getConfig(providerId);
const apiKey = resolveApiKey(config);
if (apiKey) {
return providerId;
}
}
return null;
}
/**
* 加载 Agent 配置
*
* 配置来源优先级:
* 1. agents.json 的 defaults.model(用户配置的全局默认模型)
* 2. 第一个已配置 API Key 的 Providerfallback
* 3. 抛出 ConfigurationError(无可用配置)
*/
export function loadConfig(): AgentConfig {
// 1. 从 AgentRegistry 获取 defaults.model 配置
const globalConfig = agentRegistry.getGlobalConfig();
const modelConfig = globalConfig?.model;
// 2. 确定 provider(优先使用配置,否则使用第一个有 API Key 的 provider
let provider: ProviderType;
let model: string;
if (modelConfig?.provider) {
// 用户配置了 defaults.model.provider
provider = modelConfig.provider;
model = modelConfig.model || DEFAULT_MODELS[provider];
} else {
// 未配置,尝试找第一个有 API Key 的 provider
const fallbackProvider = findFirstConfiguredProvider();
if (!fallbackProvider) {
throw new ConfigurationError(
'请先在 Providers 面板配置一个模型提供商的 API Key',
'unknown',
'apiKey'
);
}
provider = fallbackProvider;
model = DEFAULT_MODELS[provider];
}
// 3. 从 ProviderRegistry 获取 API Key 和 baseUrl
const providerConfig = providerRegistry.getConfig(provider);
const apiKey = resolveApiKey(providerConfig);
if (!apiKey) {
throw new ConfigurationError(
`请在 Providers 面板配置 ${provider} 的 API Key`,
provider,
'apiKey'
);
}
// 4. 获取模型的 contextWindow
const modelInfo = providerRegistry.getModelInfo(provider, model);
const contextWindow = modelInfo?.contextWindow;
// 5. 获取 maxTokens(从 modelConfig 或默认值)
const maxTokens = modelConfig?.maxTokens || 4096;
return {
provider,
apiKey,
model,
maxTokens,
systemPrompt: DEFAULT_SYSTEM_PROMPT,
baseUrl: providerConfig?.baseUrl,
contextWindow,
};
}
// Vision 配置接口
export interface VisionConfig {
provider: ProviderType;
apiKey: string;
model: string;
baseUrl?: string;
}
/**
* 加载 Vision 配置
*
* 从 agents.json 的 defaults.vision 读取配置
* Vision 用于图片理解,当主模型不支持 vision 时使用
*/
export function loadVisionConfig(): VisionConfig | null {
// 从 AgentRegistry 获取 defaults.vision 配置
const globalConfig = agentRegistry.getGlobalConfig();
const visionConfig = globalConfig?.vision;
if (!visionConfig?.provider) {
// 未配置 vision,返回 null
return null;
}
// 从 ProviderRegistry 获取 API Key
const providerConfig = providerRegistry.getConfig(visionConfig.provider);
const apiKey = resolveApiKey(providerConfig);
if (!apiKey) {
// 没有 API Key,返回 null
return null;
}
// 确定模型(使用配置的或默认值)
const model = visionConfig.model || DEFAULT_MODELS[visionConfig.provider];
return {
provider: visionConfig.provider,
apiKey,
model,
baseUrl: providerConfig?.baseUrl,
};
}
/**
* 从 AgentModelConfig 构建完整配置
* 用于 Task 工具动态构建 Agent 配置
*/
export function buildConfigFromModelConfig(modelConfig: AgentModelConfig): AgentConfig | null {
if (!modelConfig.provider) {
return null;
}
const providerConfig = providerRegistry.getConfig(modelConfig.provider);
const apiKey = resolveApiKey(providerConfig);
if (!apiKey) {
return null;
}
const model = modelConfig.model || DEFAULT_MODELS[modelConfig.provider];
const modelInfo = providerRegistry.getModelInfo(modelConfig.provider, model);
return {
provider: modelConfig.provider,
apiKey,
model,
maxTokens: modelConfig.maxTokens || 4096,
systemPrompt: DEFAULT_SYSTEM_PROMPT,
baseUrl: providerConfig?.baseUrl,
contextWindow: modelInfo?.contextWindow,
};
}