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() }