diff --git a/run.bat b/run.bat index 810a1d3..06a847a 100644 --- a/run.bat +++ b/run.bat @@ -12,7 +12,18 @@ REM === 抑制 Maven JVM 的 JDK25 兼容性警告 === set MAVEN_OPTS=--enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow REM === AI API 配置(按需修改) === +REM 选择 API 提供者:openai(默认)或 anthropic +REM set CLAUDE_CODE_PROVIDER=openai +REM set CLAUDE_CODE_PROVIDER=anthropic + +REM OpenAI 兼容 API 配置(默认) +REM set AI_API_KEY=your-api-key-here +REM set AI_BASE_URL=https://api.openai.com +REM set AI_OPENAI_MODEL=gpt-4o + +REM Anthropic 原生 API 配置 REM set ANTHROPIC_API_KEY=your-api-key-here +REM set ANTHROPIC_BASE_URL=https://api.anthropic.com REM set AI_MODEL=claude-sonnet-4-20250514 REM === 设置控制台 UTF-8 编码(支持 emoji 等字符) === diff --git a/run.ps1 b/run.ps1 index f22896e..19681df 100644 --- a/run.ps1 +++ b/run.ps1 @@ -11,7 +11,18 @@ $env:Path = "D:\Dev\jdk-25\bin;$env:Path" $env:MAVEN_OPTS = "--enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow" # === AI API 配置(按需修改) === +# 选择 API 提供者:openai(默认)或 anthropic +# $env:CLAUDE_CODE_PROVIDER = "openai" # 使用 OpenAI 兼容 API(支持代理) +# $env:CLAUDE_CODE_PROVIDER = "anthropic" # 使用 Anthropic 原生 API + +# OpenAI 兼容 API 配置(默认) +# $env:AI_API_KEY = "your-api-key-here" +# $env:AI_BASE_URL = "https://api.openai.com" +# $env:AI_OPENAI_MODEL = "gpt-4o" + +# Anthropic 原生 API 配置 # $env:ANTHROPIC_API_KEY = "your-api-key-here" +# $env:ANTHROPIC_BASE_URL = "https://api.anthropic.com" # $env:AI_MODEL = "claude-sonnet-4-20250514" # === 设置控制台 UTF-8 编码(支持 emoji 等字符) === diff --git a/src/main/java/com/claudecode/config/AppConfig.java b/src/main/java/com/claudecode/config/AppConfig.java index a736339..a5ec364 100644 --- a/src/main/java/com/claudecode/config/AppConfig.java +++ b/src/main/java/com/claudecode/config/AppConfig.java @@ -12,21 +12,31 @@ import com.claudecode.repl.ReplSession; import com.claudecode.tool.ToolContext; import com.claudecode.tool.ToolRegistry; import com.claudecode.tool.impl.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ai.chat.model.ChatModel; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.nio.file.Path; +import java.util.Map; /** * 应用配置类 —— Spring Bean 装配。 *
* 集中管理所有组件的创建和依赖注入。
+ * 通过 claude-code.provider 配置切换 API 提供者(openai / anthropic)。
*/
@Configuration
public class AppConfig {
+ private static final Logger log = LoggerFactory.getLogger(AppConfig.class);
+
+ @Value("${claude-code.provider:openai}")
+ private String provider;
+
@Bean
public ToolContext toolContext() {
return ToolContext.defaultContext();
@@ -69,11 +79,45 @@ public class AppConfig {
return registry;
}
+ /**
+ * 根据 claude-code.provider 配置选择 ChatModel。
+ * - "anthropic" → 使用 anthropicChatModel
+ * - "openai"(默认)→ 使用 openAiChatModel
+ */
+ @Bean
+ public ChatModel activeChatModel(
+ @Qualifier("openAiChatModel") ChatModel openAiModel,
+ @Qualifier("anthropicChatModel") ChatModel anthropicModel) {
+
+ if ("anthropic".equalsIgnoreCase(provider)) {
+ log.info("使用 Anthropic 原生 API");
+ return anthropicModel;
+ } else {
+ log.info("使用 OpenAI 兼容 API");
+ return openAiModel;
+ }
+ }
+
@Bean
- public TokenTracker tokenTracker() {
+ public ProviderInfo providerInfo() {
+ String baseUrl;
+ String model;
+
+ if ("anthropic".equalsIgnoreCase(provider)) {
+ baseUrl = System.getenv().getOrDefault("ANTHROPIC_BASE_URL", "https://api.anthropic.com");
+ model = System.getenv().getOrDefault("AI_MODEL", "claude-sonnet-4-20250514");
+ } else {
+ baseUrl = System.getenv().getOrDefault("AI_BASE_URL", "https://api.openai.com");
+ model = System.getenv().getOrDefault("AI_OPENAI_MODEL", "gpt-4o");
+ }
+
+ return new ProviderInfo(provider, baseUrl, model);
+ }
+
+ @Bean
+ public TokenTracker tokenTracker(ProviderInfo info) {
TokenTracker tracker = new TokenTracker();
- String model = System.getenv().getOrDefault("AI_MODEL", "claude-sonnet-4-20250514");
- tracker.setModel(model);
+ tracker.setModel(info.model());
return tracker;
}
@@ -81,16 +125,13 @@ public class AppConfig {
public String systemPrompt() {
Path projectDir = Path.of(System.getProperty("user.dir"));
- // 加载 CLAUDE.md
ClaudeMdLoader claudeLoader = new ClaudeMdLoader(projectDir);
String claudeMd = claudeLoader.load();
- // 加载 Skills
SkillLoader skillLoader = new SkillLoader(projectDir);
skillLoader.loadAll();
String skillsSummary = skillLoader.buildSkillsSummary();
- // 收集 Git 上下文
GitContext gitContext = new GitContext(projectDir).collect();
String gitSummary = gitContext.buildSummary();
@@ -102,14 +143,14 @@ public class AppConfig {
}
@Bean
- public AgentLoop agentLoop(@Qualifier("anthropicChatModel") ChatModel chatModel, ToolRegistry toolRegistry,
+ public AgentLoop agentLoop(ChatModel activeChatModel, ToolRegistry toolRegistry,
ToolContext toolContext, String systemPrompt, TokenTracker tokenTracker) {
- AgentLoop mainLoop = new AgentLoop(chatModel, toolRegistry, toolContext, systemPrompt, tokenTracker);
+ AgentLoop mainLoop = new AgentLoop(activeChatModel, toolRegistry, toolContext, systemPrompt, tokenTracker);
- // 注册子 Agent 工厂到 ToolContext,使 AgentTool 能创建独立的 AgentLoop
+ // 注册子 Agent 工厂
toolContext.set(AgentTool.AGENT_FACTORY_KEY,
(java.util.function.Function