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 @@
+