aaxaxax's picture
force update files
1de7911
package service
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"github.com/google/uuid"
"zencoder-2api/internal/model"
)
const (
ZencoderChatURL = "https://api.zencoder.ai/v1/chat/completions"
MaxRetries = 3
ZencoderVersion = "3.24.0"
)
type ZencoderService struct{}
func NewZencoderService() *ZencoderService {
return &ZencoderService{}
}
func setZencoderHeaders(req *http.Request, token, modelID string) {
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("User-Agent", "zen-cli/0.9.0-windows-x64")
req.Header.Set("zen-model-id", modelID)
req.Header.Set("zencoder-arch", "x64")
req.Header.Set("zencoder-os", "windows")
req.Header.Set("zencoder-version", ZencoderVersion)
req.Header.Set("zencoder-client-type", "vscode")
req.Header.Set("zencoder-operation-id", uuid.New().String())
req.Header.Set("zencoder-operation-type", "agent_call")
}
func (s *ZencoderService) Chat(req *model.ChatCompletionRequest) (*model.ChatCompletionResponse, error) {
// 检查模型是否存在于模型字典中
zenModel, exists := model.GetZenModel(req.Model)
if !exists {
return nil, ErrNoAvailableAccount
}
var lastErr error
for i := 0; i < MaxRetries; i++ {
account, err := GetNextAccountForModel(req.Model)
if err != nil {
return nil, err
}
resp, err := s.doRequest(account, req)
if err != nil {
MarkAccountError(account)
lastErr = err
continue
}
ResetAccountError(account)
// ZenCoder服务没有HTTP响应,只能使用模型倍率
UseCredit(account, zenModel.Multiplier)
return resp, nil
}
return nil, fmt.Errorf("all retries failed: %w", lastErr)
}
func (s *ZencoderService) doRequest(account *model.Account, req *model.ChatCompletionRequest) (*model.ChatCompletionResponse, error) {
token, err := GetToken(account)
if err != nil {
return nil, err
}
// 获取模型映射
zenModel, exists := model.GetZenModel(req.Model)
if !exists {
return nil, ErrNoAvailableAccount
}
body, err := json.Marshal(req)
if err != nil {
return nil, err
}
client := createHTTPClient(account.Proxy)
httpReq, err := http.NewRequest("POST", ZencoderChatURL, bytes.NewReader(body))
if err != nil {
return nil, err
}
setZencoderHeaders(httpReq, token, zenModel.ID)
resp, err := client.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(respBody))
}
var chatResp model.ChatCompletionResponse
if err := json.Unmarshal(respBody, &chatResp); err != nil {
return nil, err
}
return &chatResp, nil
}
func (s *ZencoderService) ChatStream(req *model.ChatCompletionRequest, writer http.ResponseWriter) error {
// 检查模型是否存在于模型字典中
zenModel, exists := model.GetZenModel(req.Model)
if !exists {
return ErrNoAvailableAccount
}
var lastErr error
for i := 0; i < MaxRetries; i++ {
account, err := GetNextAccountForModel(req.Model)
if err != nil {
return err
}
err = s.doStreamRequest(account, req, writer)
if err != nil {
MarkAccountError(account)
lastErr = err
continue
}
ResetAccountError(account)
// 流式响应,使用模型倍率
UseCredit(account, zenModel.Multiplier)
return nil
}
return fmt.Errorf("all retries failed: %w", lastErr)
}
func (s *ZencoderService) doStreamRequest(account *model.Account, req *model.ChatCompletionRequest, writer http.ResponseWriter) error {
token, err := GetToken(account)
if err != nil {
return err
}
// 获取模型映射
zenModel, exists := model.GetZenModel(req.Model)
if !exists {
return ErrNoAvailableAccount
}
req.Stream = true
body, err := json.Marshal(req)
if err != nil {
return err
}
client := createHTTPClient(account.Proxy)
client.Timeout = 5 * time.Minute
httpReq, err := http.NewRequest("POST", ZencoderChatURL, bytes.NewReader(body))
if err != nil {
return err
}
setZencoderHeaders(httpReq, token, zenModel.ID)
resp, err := client.Do(httpReq)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
respBody, _ := io.ReadAll(resp.Body)
return fmt.Errorf("status %d: %s", resp.StatusCode, string(respBody))
}
return s.streamResponse(resp.Body, writer)
}
func (s *ZencoderService) streamResponse(body io.Reader, writer http.ResponseWriter) error {
flusher, ok := writer.(http.Flusher)
if !ok {
return fmt.Errorf("streaming not supported")
}
writer.Header().Set("Content-Type", "text/event-stream")
writer.Header().Set("Cache-Control", "no-cache")
writer.Header().Set("Connection", "keep-alive")
scanner := bufio.NewScanner(body)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
continue
}
fmt.Fprintf(writer, "%s\n\n", line)
flusher.Flush()
}
return scanner.Err()
}