# 核心模块设计 — 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)