# 核心模块设计 — API 多提供商路由 > 所属项目: free-code .NET 10 重写 > 原始代码来源: `../../src/services/api/` > 原始设计意图: 支持五种 API 提供商(Anthropic、OpenAI Codex、AWS Bedrock、Google Vertex、Anthropic Foundry)的统一路由,通过环境变量自动检测活跃提供商,并为每个提供商实现 SSE 流式解析 > 上级文档: [核心模块设计总览](核心模块设计.md) --- ## 概述 API 多提供商路由模块是 free-code 多后端能力的核心。它将具体 API 调用细节从 `QueryEngine` 中解耦出来,使查询引擎无需关心当前使用的是哪家服务商的 API。 原始 TypeScript 实现通过 `../../src/services/api/` 目录下的多个文件分别实现各提供商的客户端。.NET 重写引入 `IApiProvider` 接口统一抽象,通过 `ApiProviderRouter` 在运行时选择具体实现。 --- ## 9.1 提供商枚举 ```csharp public enum ApiProviderType { Anthropic, // 默认,直连 Anthropic API OpenAICodex, // CLAUDE_CODE_USE_OPENAI=1 AwsBedrock, // CLAUDE_CODE_USE_BEDROCK=1 GoogleVertex, // CLAUDE_CODE_USE_VERTEX=1 AnthropicFoundry // CLAUDE_CODE_USE_FOUNDRY=1 } ``` --- ## 9.2 IApiProvider 接口 ```csharp /// API提供商抽象接口 public interface IApiProvider { /// 发起流式 API 请求,返回 SSE 消息流 IAsyncEnumerable StreamAsync( ApiRequest request, CancellationToken ct = default); } public record ApiRequest( string SystemPrompt, IReadOnlyList Messages, IReadOnlyList Tools, string? Model = null ); ``` --- ## 9.3 ApiProviderRouter — 路由器 `ApiProviderRouter` 在构造时检测环境变量,确定活跃提供商,后续每次调用 `GetActiveProvider()` 都返回对应的实例。 **原始设计意图:** 原始代码通过 `process.env` 检查选择不同的 API 客户端模块。.NET 版本将这一逻辑集中在路由器的构造函数中,避免散布在代码各处的条件判断。 ```csharp public class ApiProviderRouter : IApiProviderRouter { private readonly IServiceProvider _services; private ApiProviderType _activeProvider; public ApiProviderRouter(IServiceProvider services) { _services = services; _activeProvider = DetectProvider(); // 从环境变量自动检测 } private static ApiProviderType DetectProvider() { if (Env("CLAUDE_CODE_USE_OPENAI") == "1") return ApiProviderType.OpenAICodex; if (Env("CLAUDE_CODE_USE_BEDROCK") == "1") return ApiProviderType.AwsBedrock; if (Env("CLAUDE_CODE_USE_VERTEX") == "1") return ApiProviderType.GoogleVertex; if (Env("CLAUDE_CODE_USE_FOUNDRY") == "1") return ApiProviderType.AnthropicFoundry; return ApiProviderType.Anthropic; } public IApiProvider GetActiveProvider() => _activeProvider switch { ApiProviderType.Anthropic => _services.GetRequiredService(), ApiProviderType.OpenAICodex => _services.GetRequiredService(), ApiProviderType.AwsBedrock => _services.GetRequiredService(), ApiProviderType.GoogleVertex => _services.GetRequiredService(), ApiProviderType.AnthropicFoundry => _services.GetRequiredService(), _ => throw new InvalidOperationException() }; private static string? Env(string name) => Environment.GetEnvironmentVariable(name); } ``` 检测优先级为:OpenAI > Bedrock > Vertex > Foundry > Anthropic(默认)。若同时设置多个环境变量,优先级靠前的生效。 --- ## 9.4 AnthropicProvider — SSE 流式解析 `AnthropicProvider` 实现直连 Anthropic Messages API 的 SSE 流式解析,是五个提供商中最核心的实现。 **原始设计意图:** 原始 `claude.ts` 使用 Anthropic TypeScript SDK 处理流式响应。.NET 版本直接使用 `HttpClient` 发送 HTTP 请求,手动解析 SSE 事件行,避免引入额外 SDK 依赖。 ```csharp public class AnthropicProvider : IApiProvider { private readonly HttpClient _httpClient; public async IAsyncEnumerable StreamAsync( ApiRequest request, [EnumeratorCancellation] CancellationToken ct = default) { var payload = new { model = request.Model ?? "claude-sonnet-4-6", max_tokens = 16384, stream = true, system = request.SystemPrompt, messages = request.Messages, tools = request.Tools }; var httpReq = new HttpRequestMessage(HttpMethod.Post, $"{GetBaseUrl()}/v1/messages") { Content = JsonContent.Create(payload) }; httpReq.Headers.Add("x-api-key", GetApiKey()); httpReq.Headers.Add("anthropic-version", "2023-06-01"); using var response = await _httpClient.SendAsync( httpReq, HttpCompletionOption.ResponseHeadersRead, ct); response.EnsureSuccessStatusCode(); // SSE 流式解析 using var stream = await response.Content.ReadAsStreamAsync(ct); using var reader = new StreamReader(stream); while (!reader.EndOfStream && !ct.IsCancellationRequested) { var line = await reader.ReadLineAsync(ct); if (string.IsNullOrEmpty(line) || !line.StartsWith("data: ")) continue; var json = line[6..]; var doc = JsonDocument.Parse(json); var type = doc.RootElement.GetProperty("type").GetString(); switch (type) { case "content_block_delta": yield return new SDKMessage.StreamingDelta( doc.RootElement.GetProperty("delta") .GetProperty("text").GetString()!); break; case "content_block_start": var block = doc.RootElement.GetProperty("content_block"); if (block.GetProperty("type").GetString() == "tool_use") yield return new SDKMessage.ToolUseStart( block.GetProperty("id").GetString()!, block.GetProperty("name").GetString()!, block.GetProperty("input")); break; case "message_stop": yield break; } } } } ``` ### SSE 事件类型映射 | SSE `type` | 映射到 `SDKMessage` 子类 | 说明 | |------------|--------------------------|------| | `content_block_delta` | `SDKMessage.StreamingDelta` | 文本流式增量 | | `content_block_start` (tool_use) | `SDKMessage.ToolUseStart` | 工具调用开始 | | `message_stop` | `yield break` | 流结束 | | 其他 | 忽略 | `ping`、`message_start` 等元数据事件 | `HttpCompletionOption.ResponseHeadersRead` 确保在响应头返回后立即开始读取流,而不是等待整个响应体下载完成,这是 SSE 流式解析的关键设置。 --- ## 9.5 提供商配置汇总 | 提供商 | 环境变量 | 认证方式 | 端点 | |--------|----------|----------|------| | Anthropic | 默认 | `ANTHROPIC_API_KEY` 或 OAuth | `https://api.anthropic.com` | | OpenAI Codex | `CLAUDE_CODE_USE_OPENAI=1` | OAuth via OpenAI | `https://api.openai.com` | | AWS Bedrock | `CLAUDE_CODE_USE_BEDROCK=1` | AWS 标准凭证链 | `AWS_REGION` 对应端点 | | Google Vertex | `CLAUDE_CODE_USE_VERTEX=1` | GCP ADC | GCP 项目对应端点 | | Anthropic Foundry | `CLAUDE_CODE_USE_FOUNDRY=1` | `ANTHROPIC_FOUNDRY_API_KEY` | 自定义部署端点 | --- ## 参考资料 - [核心模块设计总览](核心模块设计.md) - [查询引擎 (QueryEngine)](核心模块设计-查询引擎-QueryEngine.md) - [原始代码映射 — 核心模块](reference/原始代码映射-核心模块.md)