7.8 KiB
核心模块设计 — API 多提供商路由
所属项目: free-code .NET 10 重写 原始代码来源:
../../src/services/api/原始设计意图: 支持五种 API 提供商(Anthropic、OpenAI Codex、AWS Bedrock、Google Vertex、Anthropic Foundry)的统一路由,通过环境变量自动检测活跃提供商,并为每个提供商实现 SSE 流式解析 上级文档: 核心模块设计总览
概述
API 多提供商路由模块是 free-code 多后端能力的核心。它将具体 API 调用细节从 QueryEngine 中解耦出来,使查询引擎无需关心当前使用的是哪家服务商的 API。
原始 TypeScript 实现通过 ../../src/services/api/ 目录下的多个文件分别实现各提供商的客户端。.NET 重写引入 IApiProvider 接口统一抽象,通过 ApiProviderRouter 在运行时选择具体实现。
9.1 提供商枚举
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 接口
/// <summary>API提供商抽象接口</summary>
public interface IApiProvider
{
/// <summary>发起流式 API 请求,返回 SSE 消息流</summary>
IAsyncEnumerable<SDKMessage> StreamAsync(
ApiRequest request,
CancellationToken ct = default);
}
public record ApiRequest(
string SystemPrompt,
IReadOnlyList<ApiMessage> Messages,
IReadOnlyList<ITool> Tools,
string? Model = null
);
9.3 ApiProviderRouter — 路由器
ApiProviderRouter 在构造时检测环境变量,确定活跃提供商,后续每次调用 GetActiveProvider() 都返回对应的实例。
原始设计意图: 原始代码通过 process.env 检查选择不同的 API 客户端模块。.NET 版本将这一逻辑集中在路由器的构造函数中,避免散布在代码各处的条件判断。
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<AnthropicProvider>(),
ApiProviderType.OpenAICodex => _services.GetRequiredService<CodexProvider>(),
ApiProviderType.AwsBedrock => _services.GetRequiredService<BedrockProvider>(),
ApiProviderType.GoogleVertex => _services.GetRequiredService<VertexProvider>(),
ApiProviderType.AnthropicFoundry => _services.GetRequiredService<FoundryProvider>(),
_ => 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 依赖。
public class AnthropicProvider : IApiProvider
{
private readonly HttpClient _httpClient;
public async IAsyncEnumerable<SDKMessage> 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 |
自定义部署端点 |