diff --git a/pkg/azureopenai/azureopenai.go b/pkg/azureopenai/azureopenai.go index c5a8647..61dbc5d 100644 --- a/pkg/azureopenai/azureopenai.go +++ b/pkg/azureopenai/azureopenai.go @@ -16,6 +16,9 @@ https://learn.microsoft.com/zh-cn/rest/api/cognitiveservices/azureopenaistable/m -H "Content-Type: application/json" \ -H "api-key: $AZURE_OPENAI_KEY" \ +> GPT-4 Turbo +https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/azure-openai-service-launches-gpt-4-turbo-and-gpt-3-5-turbo-1106/ba-p/3985962 + */ package azureopenai diff --git a/pkg/openai/tts.go b/pkg/openai/tts.go new file mode 100644 index 0000000..579f13b --- /dev/null +++ b/pkg/openai/tts.go @@ -0,0 +1,93 @@ +package openai + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "net/http/httputil" + "net/url" + "opencatd-open/pkg/tokenizer" + "opencatd-open/store" + + "github.com/gin-gonic/gin" +) + +const ( + SpeechEndpoint = "https://api.openai.com/v1/audio/speech" +) + +type SpeechRequest struct { + Model string `json:"model"` + Input string `json:"input"` + Voice string `json:"voice"` +} + +func SpeechHandler(c *gin.Context) { + var chatreq SpeechRequest + if err := c.ShouldBindJSON(&chatreq); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + var chatlog store.Tokens + chatlog.Model = chatreq.Model + chatlog.CompletionCount = len(chatreq.Input) + + token, _ := c.Get("localuser") + + lu, err := store.GetUserByToken(token.(string)) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": gin.H{ + "message": err.Error(), + }, + }) + return + } + chatlog.UserID = int(lu.ID) + + key, err := store.SelectKeyCache("openai") + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": gin.H{ + "message": err.Error(), + }, + }) + return + } + + targetURL, _ := url.Parse(SpeechEndpoint) + proxy := httputil.NewSingleHostReverseProxy(targetURL) + + proxy.Director = func(req *http.Request) { + req.Header = c.Request.Header + req.Header["Authorization"] = []string{"Bearer " + key.Key} + req.Host = targetURL.Host + req.URL.Scheme = targetURL.Scheme + req.URL.Host = targetURL.Host + req.URL.Path = targetURL.Path + req.URL.RawPath = targetURL.RawPath + + reqBytes, _ := json.Marshal(chatreq) + req.Body = io.NopCloser(bytes.NewReader(reqBytes)) + req.ContentLength = int64(len(reqBytes)) + + } + proxy.ModifyResponse = func(resp *http.Response) error { + if resp.StatusCode == http.StatusOK { + chatlog.TotalTokens = chatlog.PromptCount + chatlog.CompletionCount + chatlog.Cost = fmt.Sprintf("%.6f", tokenizer.Cost(chatlog.Model, chatlog.PromptCount, chatlog.CompletionCount)) + if err := store.Record(&chatlog); err != nil { + log.Println(err) + } + if err := store.SumDaily(chatlog.UserID); err != nil { + log.Println(err) + } + } + return nil + } + proxy.ServeHTTP(c.Writer, c.Request) +} diff --git a/pkg/tokenizer/tokenizer.go b/pkg/tokenizer/tokenizer.go index 4e628a0..aaa9c98 100644 --- a/pkg/tokenizer/tokenizer.go +++ b/pkg/tokenizer/tokenizer.go @@ -107,7 +107,7 @@ func Cost(model string, promptCount, completionCount int) float64 { cost = 11.02/1000000*float64(prompt) + (32.68/1000000)*float64(completion) case "claude-instant-v1", "claude-instant-v1-100k": cost = (1.63/1000000)*float64(prompt) + (5.51/1000000)*float64(completion) - case "claude-2": + case "claude-2", "claude-2.1": cost = (11.02/1000000)*float64(prompt) + (32.68/1000000)*float64(completion) default: if strings.Contains(model, "gpt-3.5-turbo") { diff --git a/router/router.go b/router/router.go index 0ddb29f..b619d4c 100644 --- a/router/router.go +++ b/router/router.go @@ -17,6 +17,7 @@ import ( "net/url" "opencatd-open/pkg/azureopenai" "opencatd-open/pkg/claude" + oai "opencatd-open/pkg/openai" "opencatd-open/pkg/tokenizer" "opencatd-open/store" "os" @@ -480,6 +481,10 @@ func HandleProy(c *gin.Context) { WhisperProxy(c) return } + if c.Request.URL.Path == "/v1/audio/speech" { + oai.SpeechHandler(c) + return + } if c.Request.URL.Path == "/v1/chat/completions" && localuser { if store.KeysCache.ItemCount() == 0 {