201 lines
7.8 KiB
Markdown
201 lines
7.8 KiB
Markdown
# 核心模块设计 — 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
|
||
/// <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 版本将这一逻辑集中在路由器的构造函数中,避免散布在代码各处的条件判断。
|
||
|
||
```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<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 依赖。
|
||
|
||
```csharp
|
||
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` | 自定义部署端点 |
|
||
|
||
---
|
||
|
||
## 参考资料
|
||
|
||
- [核心模块设计总览](核心模块设计.md)
|
||
- [查询引擎 (QueryEngine)](核心模块设计-查询引擎-QueryEngine.md)
|
||
- [原始代码映射 — 核心模块](reference/原始代码映射-核心模块.md)
|