feat: 初始化 AI Gateway 项目
实现支持 OpenAI 和 Anthropic 双协议的统一大模型 API 网关 MVP 版本,包含: - OpenAI 和 Anthropic 协议代理 - 供应商和模型管理 - 用量统计 - 前端配置界面
This commit is contained in:
86
backend/internal/protocol/openai/adapter.go
Normal file
86
backend/internal/protocol/openai/adapter.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package openai
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Adapter OpenAI 协议适配器(透传)
|
||||
type Adapter struct{}
|
||||
|
||||
// NewAdapter 创建 OpenAI 适配器
|
||||
func NewAdapter() *Adapter {
|
||||
return &Adapter{}
|
||||
}
|
||||
|
||||
// PrepareRequest 准备发送给供应商的请求(透传)
|
||||
func (a *Adapter) PrepareRequest(req *ChatCompletionRequest, apiKey, baseURL string) (*http.Request, error) {
|
||||
// 序列化请求体
|
||||
body, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 调试日志:打印请求体
|
||||
fmt.Printf("[DEBUG] 请求Body: %s\n", string(body))
|
||||
|
||||
// 创建 HTTP 请求
|
||||
// baseURL 已包含版本路径(如 /v1 或 /v4),只需添加端点路径
|
||||
httpReq, err := http.NewRequest("POST", baseURL+"/chat/completions", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
httpReq.Header.Set("Authorization", "Bearer "+apiKey)
|
||||
|
||||
return httpReq, nil
|
||||
}
|
||||
|
||||
// ParseResponse 解析供应商响应(透传)
|
||||
func (a *Adapter) ParseResponse(resp *http.Response) (*ChatCompletionResponse, error) {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result ChatCompletionResponse
|
||||
err = json.Unmarshal(body, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// ParseErrorResponse 解析错误响应
|
||||
func (a *Adapter) ParseErrorResponse(resp *http.Response) (*ErrorResponse, error) {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result ErrorResponse
|
||||
err = json.Unmarshal(body, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// ParseStreamChunk 解析流式响应块
|
||||
func (a *Adapter) ParseStreamChunk(data []byte) (*StreamChunk, error) {
|
||||
var chunk StreamChunk
|
||||
err := json.Unmarshal(data, &chunk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &chunk, nil
|
||||
}
|
||||
131
backend/internal/protocol/openai/types.go
Normal file
131
backend/internal/protocol/openai/types.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package openai
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// ChatCompletionRequest OpenAI Chat Completions API 请求结构
|
||||
type ChatCompletionRequest struct {
|
||||
Model string `json:"model"`
|
||||
Messages []Message `json:"messages"`
|
||||
Temperature *float64 `json:"temperature,omitempty"`
|
||||
MaxTokens *int `json:"max_tokens,omitempty"`
|
||||
TopP *float64 `json:"top_p,omitempty"`
|
||||
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"`
|
||||
PresencePenalty *float64 `json:"presence_penalty,omitempty"`
|
||||
Stop interface{} `json:"stop,omitempty"` // 可以是字符串或字符串数组
|
||||
N *int `json:"n,omitempty"`
|
||||
Stream bool `json:"stream,omitempty"`
|
||||
Tools []Tool `json:"tools,omitempty"`
|
||||
ToolChoice interface{} `json:"tool_choice,omitempty"` // 可以是字符串或对象
|
||||
User string `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
// Message OpenAI 消息结构
|
||||
type Message struct {
|
||||
Role string `json:"role"`
|
||||
Content interface{} `json:"content"` // 可以是字符串或数组(多模态,MVP不支持)
|
||||
Name string `json:"name,omitempty"`
|
||||
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
||||
ToolCallID string `json:"tool_call_id,omitempty"` // 用于 role="tool" 的消息
|
||||
}
|
||||
|
||||
// Tool OpenAI 工具定义
|
||||
type Tool struct {
|
||||
Type string `json:"type"` // 目前只有 "function"
|
||||
Function FunctionDefinition `json:"function"`
|
||||
}
|
||||
|
||||
// FunctionDefinition 函数定义
|
||||
type FunctionDefinition struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Parameters map[string]interface{} `json:"parameters,omitempty"`
|
||||
}
|
||||
|
||||
// ToolCall 工具调用
|
||||
type ToolCall struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"` // "function"
|
||||
Function FunctionCall `json:"function"`
|
||||
}
|
||||
|
||||
// FunctionCall 函数调用
|
||||
type FunctionCall struct {
|
||||
Name string `json:"name"`
|
||||
Arguments string `json:"arguments"` // JSON 字符串
|
||||
}
|
||||
|
||||
// ChatCompletionResponse OpenAI Chat Completions API 响应结构
|
||||
type ChatCompletionResponse struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Created int64 `json:"created"`
|
||||
Model string `json:"model"`
|
||||
Choices []Choice `json:"choices"`
|
||||
Usage Usage `json:"usage"`
|
||||
}
|
||||
|
||||
// Choice 响应选项
|
||||
type Choice struct {
|
||||
Index int `json:"index"`
|
||||
Message *Message `json:"message,omitempty"`
|
||||
Delta *Delta `json:"delta,omitempty"` // 用于流式响应
|
||||
FinishReason string `json:"finish_reason"`
|
||||
}
|
||||
|
||||
// Delta 流式响应增量
|
||||
type Delta struct {
|
||||
Role string `json:"role,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
||||
}
|
||||
|
||||
// Usage Token 使用统计
|
||||
type Usage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
}
|
||||
|
||||
// StreamChunk 流式响应块
|
||||
type StreamChunk struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Created int64 `json:"created"`
|
||||
Model string `json:"model"`
|
||||
Choices []StreamChoice `json:"choices"`
|
||||
}
|
||||
|
||||
// StreamChoice 流式响应选项
|
||||
type StreamChoice struct {
|
||||
Index int `json:"index"`
|
||||
Delta Delta `json:"delta"`
|
||||
FinishReason string `json:"finish_reason,omitempty"`
|
||||
}
|
||||
|
||||
// ErrorResponse OpenAI 错误响应
|
||||
type ErrorResponse struct {
|
||||
Error ErrorDetail `json:"error"`
|
||||
}
|
||||
|
||||
// ErrorDetail 错误详情
|
||||
type ErrorDetail struct {
|
||||
Message string `json:"message"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Code string `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
// ParseToolCallArguments 解析 tool_call 的 arguments(从 JSON 字符串转为 map)
|
||||
func (tc *ToolCall) ParseToolCallArguments() (map[string]interface{}, error) {
|
||||
var args map[string]interface{}
|
||||
err := json.Unmarshal([]byte(tc.Function.Arguments), &args)
|
||||
return args, err
|
||||
}
|
||||
|
||||
// SerializeToolCallArguments 序列化 tool_call 的 arguments(从 map 转为 JSON 字符串)
|
||||
func SerializeToolCallArguments(args map[string]interface{}) (string, error) {
|
||||
bytes, err := json.Marshal(args)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
Reference in New Issue
Block a user