From d6ebfdc7122ab55968de0b9b357f50f6ab433ae0 Mon Sep 17 00:00:00 2001 From: Adrien Date: Sat, 11 May 2024 18:17:46 +0200 Subject: [PATCH] Added Groq --- Chat.go | 17 +- Request.go | 6 + RequestGroq.go | 255 +++++++++++++++++++++++++++ main.go | 66 +++++++ static/icons/groq.png | Bin 0 -> 1450 bytes utils.go | 21 ++- views/chat.html | 6 +- views/partials/popover-keys.html | 10 ++ views/partials/popover-settings.html | 6 +- 9 files changed, 375 insertions(+), 12 deletions(-) create mode 100644 RequestGroq.go create mode 100644 static/icons/groq.png diff --git a/Chat.go b/Chat.go index e4f90c5..edeae79 100644 --- a/Chat.go +++ b/Chat.go @@ -326,9 +326,9 @@ func LoadUsageKPIHandler(c *fiber.Ctx) error { } func LoadKeysHandler(c *fiber.Ctx) error { - openaiExists, anthropicExists, mistralExists := getExistingKeys() + openaiExists, anthropicExists, mistralExists, groqExists := getExistingKeys() - out, err := pongo2.Must(pongo2.FromFile("views/partials/popover-keys.html")).Execute(pongo2.Context{"IsLogin": checkIfLogin(), "OpenaiExists": openaiExists, "AnthropicExists": anthropicExists, "MistralExists": mistralExists}) + out, err := pongo2.Must(pongo2.FromFile("views/partials/popover-keys.html")).Execute(pongo2.Context{"IsLogin": checkIfLogin(), "OpenaiExists": openaiExists, "AnthropicExists": anthropicExists, "MistralExists": mistralExists, "GroqExists": groqExists}) if err != nil { c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error rendering template", @@ -338,7 +338,7 @@ func LoadKeysHandler(c *fiber.Ctx) error { } func LoadModelSelectionHandler(c *fiber.Ctx) error { - openaiExists, anthropicExists, mistralExists := getExistingKeys() + openaiExists, anthropicExists, mistralExists, groqExists := getExistingKeys() var CompanyInfosAvailable []CompanyInfo @@ -373,6 +373,17 @@ func LoadModelSelectionHandler(c *fiber.Ctx) error { CompanyInfosAvailable = append(CompanyInfosAvailable, mistralCompanyInfo) } + if groqExists { + var groqCompanyInfo CompanyInfo + for _, info := range CompanyInfos { + if info.ID == "groq" { + groqCompanyInfo = info + break + } + } + CompanyInfosAvailable = append(CompanyInfosAvailable, groqCompanyInfo) + } + CheckedModels := []string{"gpt-3.5-turbo"} // Default model out, err := pongo2.Must(pongo2.FromFile("views/partials/popover-models.html")).Execute(pongo2.Context{ "CompanyInfos": CompanyInfosAvailable, diff --git a/Request.go b/Request.go index f96d611..752bd23 100644 --- a/Request.go +++ b/Request.go @@ -88,6 +88,12 @@ func GenerateMultipleMessages(c *fiber.Ctx) error { response := addMistralMessage(lastSelectedModelIds[idx], idx == 0) InsertedIDs = append(InsertedIDs, response) }() + } else if model2Icon(lastSelectedModelIds[i]) == "groq" { + go func() { + defer wg.Done() + response := addGroqMessage(lastSelectedModelIds[idx], idx == 0) + InsertedIDs = append(InsertedIDs, response) + }() } } diff --git a/RequestGroq.go b/RequestGroq.go new file mode 100644 index 0000000..7a929e4 --- /dev/null +++ b/RequestGroq.go @@ -0,0 +1,255 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/edgedb/edgedb-go" +) + +type GroqChatCompletionRequest struct { + Model string `json:"model"` + Messages []GroqMessage `json:"messages"` + Temperature float64 `json:"temperature"` +} + +type GroqMessage struct { + Role string `json:"role"` + Content string `json:"content"` +} + +type GroqChatCompletionResponse struct { + ID string `json:"id"` + Object string `json:"object"` + Created int64 `json:"created"` + Model string `json:"model"` + Usage GroqUsage `json:"usage"` + Choices []GroqChoice `json:"choices"` +} + +type GroqUsage struct { + PromptTokens int32 `json:"prompt_tokens"` + CompletionTokens int32 `json:"completion_tokens"` + TotalTokens int32 `json:"total_tokens"` +} + +type GroqChoice struct { + Message GroqMessage `json:"message"` + FinishReason string `json:"finish_reason"` + Index int `json:"index"` +} + +func init() { + var ModelInfosList = []ModelInfo{} + + modelInfo := ModelInfo{ + ID: "llama3-8b-8192", + Name: "Llama 8B", + Icon: "groq", + MaxToken: 8192, + InputPrice: 0.00 / 1000000, + OutputPrice: 0.00 / 1000000, + } + ModelInfosList = append(ModelInfosList, modelInfo) + ModelsInfos = append(ModelsInfos, modelInfo) + + modelInfo = ModelInfo{ + ID: "llama3-70b-8192", + Name: "Llama 70B", + Icon: "groq", + MaxToken: 8192, + InputPrice: 0.00 / 1000000, + OutputPrice: 0.00 / 1000000, + } + ModelInfosList = append(ModelInfosList, modelInfo) + ModelsInfos = append(ModelsInfos, modelInfo) + + modelInfo = ModelInfo{ + ID: "gemma-7b-it", + Name: "Gemma 7B", + Icon: "groq", + MaxToken: 8192, + InputPrice: 0.00 / 1000000, + OutputPrice: 0.00 / 1000000, + } + ModelInfosList = append(ModelInfosList, modelInfo) + ModelsInfos = append(ModelsInfos, modelInfo) + + companyInfo := CompanyInfo{ + ID: "groq", + Name: "Groq", + Icon: "icons/groq.png", + ModelInfos: ModelInfosList, + } + CompanyInfos = append(CompanyInfos, companyInfo) +} + +func addGroqMessage(modelID string, selected bool) edgedb.UUID { + Messages := getAllMessages() + + chatCompletion, err := RequestGroq(modelID, Messages, 0.7) + if err != nil { + fmt.Println("Error:", err) + } else if len(chatCompletion.Choices) == 0 { + fmt.Println(chatCompletion) + fmt.Println("No response from Groq") + id := insertBotMessage("No response from Groq", selected, modelID) + return id + } else { + Content := chatCompletion.Choices[0].Message.Content + id := insertBotMessage(Content, selected, modelID) + return id + } + return edgedb.UUID{} +} + +func EdgeMessages2GroqMessages(messages []Message) []GroqMessage { + groqMessages := make([]GroqMessage, len(messages)) + for i, msg := range messages { + var role string + switch msg.Role { + case "user": + role = "user" + case "bot": + role = "assistant" + default: + role = "system" + } + groqMessages[i] = GroqMessage{ + Role: role, + Content: msg.Content, + } + } + return groqMessages +} + +func TestGroqKey(apiKey string) bool { + url := "https://api.groq.com/openai/v1/chat/completions" + + // Convert messages to Qroq format + groqMessages := []GroqMessage{ + { + Role: "user", + Content: "Hello", + }, + } + + requestBody := GroqChatCompletionRequest{ + Model: "llama3-8b-8192", + Messages: groqMessages, + Temperature: 0, + } + + jsonBody, err := json.Marshal(requestBody) + if err != nil { + return false + } + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody)) + if err != nil { + return false + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+apiKey) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return false + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return false + } + + var chatCompletionResponse GroqChatCompletionResponse + err = json.Unmarshal(body, &chatCompletionResponse) + fmt.Println(chatCompletionResponse) + if err != nil { + return false + } + if chatCompletionResponse.Usage.CompletionTokens == 0 { + return false + } + return true +} + +func RequestGroq(model string, messages []Message, temperature float64) (GroqChatCompletionResponse, error) { + var apiKey string + err := edgeClient.QuerySingle(edgeCtx, ` + with + filtered_keys := ( + select Key { + key + } filter .company = $0 + ) + select filtered_keys.key limit 1 + `, &apiKey, "groq") + if err != nil { + return GroqChatCompletionResponse{}, fmt.Errorf("error getting Groq API key: %w", err) + } + + fmt.Println("API key:", apiKey) + + url := "https://api.groq.com/openai/v1/chat/completions" + + // Convert messages to Qroq format + groqMessages := EdgeMessages2GroqMessages(messages) + + requestBody := GroqChatCompletionRequest{ + Model: model, + Messages: groqMessages, + Temperature: temperature, + } + + fmt.Println(requestBody) + + jsonBody, err := json.Marshal(requestBody) + if err != nil { + return GroqChatCompletionResponse{}, fmt.Errorf("error marshaling JSON: %w", err) + } + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody)) + if err != nil { + return GroqChatCompletionResponse{}, fmt.Errorf("error creating request: %w", err) + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+apiKey) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return GroqChatCompletionResponse{}, fmt.Errorf("error sending request: %w", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return GroqChatCompletionResponse{}, fmt.Errorf("error reading response body: %w", err) + } + + var chatCompletionResponse GroqChatCompletionResponse + err = json.Unmarshal(body, &chatCompletionResponse) + if err != nil { + return GroqChatCompletionResponse{}, fmt.Errorf("error unmarshaling JSON: %w", err) + } + + var usedModelInfo ModelInfo + for mi := range ModelsInfos { + if ModelsInfos[mi].ID == model { + usedModelInfo = ModelsInfos[mi] + } + } + var inputCost float32 = float32(chatCompletionResponse.Usage.PromptTokens) * usedModelInfo.InputPrice + var outputCost float32 = float32(chatCompletionResponse.Usage.CompletionTokens) * usedModelInfo.OutputPrice + addUsage(inputCost, outputCost, chatCompletionResponse.Usage.PromptTokens, chatCompletionResponse.Usage.CompletionTokens, model) + + return chatCompletionResponse, nil +} diff --git a/main.go b/main.go index 240f569..ca1e114 100644 --- a/main.go +++ b/main.go @@ -75,6 +75,7 @@ func addKeys(c *fiber.Ctx) error { openaiKey := c.FormValue("openai_key") anthropicKey := c.FormValue("anthropic_key") mistralKey := c.FormValue("mistral_key") + groqKey := c.FormValue("groq_key") var Exists bool // Handle OpenAI key @@ -273,5 +274,70 @@ func addKeys(c *fiber.Ctx) error { } } + // Handle Groq key + if groqKey != "" { + // Check if the OpenAI key already exists + err := edgeClient.QuerySingle(edgeCtx, ` + select exists ( + select global currentUser.setting.keys + filter .company = "groq" AND .key = $0 + ); + `, &Exists, openaiKey) + if err != nil { + fmt.Println("Error in edgedb.QuerySingle: in addGroqKey: ", err) + return c.SendString("") + } + if Exists { + fmt.Println("Groq key already exists") + return c.SendString("") + } + + if !TestGroqKey(groqKey) { + fmt.Println("Invalid Groq API Key") + return c.SendString("Invalid Groq API Key\n") + } + + // Check if the company key already exists + err = edgeClient.QuerySingle(edgeCtx, ` + select exists ( + select global currentUser.setting.keys + filter .company = "groq" + ); + `, &Exists) + if err != nil { + fmt.Println("Error in edgedb.QuerySingle: in addGroqKey") + fmt.Println(err) + } + + if Exists { + err = edgeClient.Execute(edgeCtx, ` + UPDATE Key filter .company = "groq" AND .key = $0 + SET { + key := $0, + } + `, groqKey) + if err != nil { + fmt.Println("Error in edgedb.QuerySingle: in addGroqKey") + fmt.Println(err) + } + } else { + err = edgeClient.Execute(edgeCtx, ` + UPDATE global currentUser.setting + SET { + keys += ( + INSERT Key { + company := "groq", + key := $0, + name := "Groq API Key", + } + ) + }`, groqKey) + if err != nil { + fmt.Println("Error in edgedb.QuerySingle: in addGroqKey") + fmt.Println(err) + } + } + } + return c.SendString("") } diff --git a/static/icons/groq.png b/static/icons/groq.png new file mode 100644 index 0000000000000000000000000000000000000000..31564145e1068131f3cf0a49766efbd68a0b4d54 GIT binary patch literal 1450 zcmb7^dotQT$Rm%*&tNegE6tFx zX~(OSS86M*Ff-*bWJY9AB4p4GJG+1FUwh7;bMO7$d%x%2KR;Pc4mL7Uho!{C#AI+F z)C%l{0cSnPN$C?LbJuzsrmhz*qR>*$4wYt2$y}dOzvyq>& zo)P_{qe&3q`RD6y;l$`#fWt;PNp#!cIN&Y%CK2u8;F{Ovd()hO4f|a*!*N&(xA^jf z^3Rles?r(!-?BNJ5YNSJ!lO)dG|ma>xACQ5QGoxX5DA%2OicJ#s)R4HLuc6_j|jx0 z;O7)!mAT@M)~Uxdo)zTtdcmdtSd+ESw}f(9$38TNdkzn8R2Jn?jgJb>(uZWORTk$= zBT@1`oHOpek&s)%o~ZGd`S&W66sobV?>Q~&3ac{WHPfVvd+D-6T6JiIY@JD{sYekt z1a8zYf4aCts~1A4$todQY7yjB#)0Ku3n(^28dnh_x7q0(SbbeDr+WH%Gf24QMe`Tf z0&qE+M2%%+{!Q(>_o1Ui)#3Puyj%mY-%l_`YDj8d+T?MNwg}}ksa~Bl3zi_Faq-1H zCsmj@+;q$&6NP8gy~gcIGS$7?B})_@n?%ZkBP|B~rO029cEiObRKB%BIg~jPJeRg* zhwo|U-d^e+9db65(}A7RbiR70%yu-hxG>EDN9+!CI?M#qfWG+4as2xaUZ1c6G0$V$ z_f$JL7Rvtt_7jn?;NA9-SbZyNOcYPPzDW2;PZ8#>8azpQoRe;J8nF-N?v-We8LF$d z5BANgPH}gse2M^`FK0t}(%)MzCw~zOY$23fldH%M^97>bh&7e6{Yi5@wh)rLWgl0) zwaj^_oO}BP_x447>E+Va1f)9L%)rsGAWL`fJHxqvNNU^68cX17s(q_iqDYb~s`aIk zy-6=X+sw$8^h-(oB zpyN{1OEZ?{p1C$f+=Jz$nJFp^tpq&-F*S`A(*c0_bS-0GReH@J(5|l1SIk zlb9^Pzwdj?3d_e%yyuAH-dS~rL3s8xW>#4G^h;FN%T~+COa3>s+1$gu=lMy}Z~In) zgfvMol0Eozac0k5=H+rvCe$VM$nN97@TSjkJjO6^Xxq&X&W%wpX4a{4@#2EiH)IGy z=?bw?>p-%^>lIPY2 zV!qI2!{>Svz3FRiQ!m@XE9yUU+OA$XWVV4b@3fRzumHCa+Y}Ccj!O>4$HFRXE zZBT5<`L3Jtb;7_M%;xnN?IxvM7wTO4e%X)XntK<_Sft!Kd*c>k?Fnkx1MJU?E*j@2 z!r6lkL4oz0ZG%_E1mdkp--dRAVrP;+MKg{R5h*Lseb|K=B&j4 literal 0 HcmV?d00001 diff --git a/utils.go b/utils.go index 715a378..ecc9cf5 100644 --- a/utils.go +++ b/utils.go @@ -53,6 +53,9 @@ func model2Icon(model string) string { if strings.Contains(model, "mistral") || strings.Contains(model, "mixtral") { return "mistral" } + if strings.Contains(model, "llama3") || strings.Contains(model, "gemma") { + return "groq" + } return "bouvai2" } @@ -65,14 +68,15 @@ func model2Name(model string) string { return "You" } -func getExistingKeys() (bool, bool, bool) { +func getExistingKeys() (bool, bool, bool, bool) { if edgeClient == nil { - return false, false, false + return false, false, false, false } var openaiExists bool var anthropicExists bool var mistralExists bool + var groqExists bool err := edgeClient.QuerySingle(edgeCtx, ` select exists ( @@ -107,5 +111,16 @@ func getExistingKeys() (bool, bool, bool) { mistralExists = false } - return openaiExists, anthropicExists, mistralExists + err = edgeClient.QuerySingle(edgeCtx, ` + select exists ( + select global currentUser.setting.keys + filter .company = "groq" + ); + `, &groqExists) + if err != nil { + fmt.Println("Error in edgedb.QuerySingle checking for mistral: ", err) + groqExists = false + } + + return openaiExists, anthropicExists, mistralExists, groqExists } diff --git a/views/chat.html b/views/chat.html index 7f76330..df975db 100644 --- a/views/chat.html +++ b/views/chat.html @@ -25,8 +25,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/views/partials/popover-keys.html b/views/partials/popover-keys.html index bc490d5..10dfd70 100644 --- a/views/partials/popover-keys.html +++ b/views/partials/popover-keys.html @@ -39,6 +39,16 @@

+
+

+ + + + +

+