diff --git a/Authentification.go b/Authentification.go index bcd18bd..0849bd2 100644 --- a/Authentification.go +++ b/Authentification.go @@ -233,7 +233,7 @@ func handleCallbackSignup(c *fiber.Ctx) error { stripCustID := CreateNewStripeCustomer(providerName, providerEmail) - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` + err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": tokenResponse.AuthToken}).Execute(edgeCtx, ` INSERT User { stripe_id := $0, email := $1, diff --git a/Chat.go b/Chat.go index c93864e..ebfb2cb 100644 --- a/Chat.go +++ b/Chat.go @@ -717,7 +717,7 @@ func LoadUsageKPIHandler(c *fiber.Ctx) error { } func GenerateModelPopoverHTML(refresh bool, c *fiber.Ctx) string { - openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists := getExistingKeys(c) + openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists, perplexityExists := getExistingKeys(c) var llms []LLM err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Query(edgeCtx, ` @@ -747,18 +747,19 @@ func GenerateModelPopoverHTML(refresh bool, c *fiber.Ctx) string { isPremium, isBasic := IsCurrentUserSubscribed(c) out, err := modelPopoverTmpl.Execute(pongo2.Context{ - "IsLogin": checkIfLogin(c), - "OpenaiExists": openaiExists, - "AnthropicExists": anthropicExists, - "MistralExists": mistralExists, - "GroqExists": groqExists, - "GooseaiExists": gooseaiExists, - "GoogleExists": googleExists, - "AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists, - "LLMs": llms, - "ModelInfos": modelInfos, - "DeleteUpdate": refresh, - "IsSub": isPremium || isBasic, + "IsLogin": checkIfLogin(c), + "OpenaiExists": openaiExists, + "AnthropicExists": anthropicExists, + "MistralExists": mistralExists, + "GroqExists": groqExists, + "GooseaiExists": gooseaiExists, + "GoogleExists": googleExists, + "PerplexityExists": perplexityExists, + "AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists || perplexityExists, + "LLMs": llms, + "ModelInfos": modelInfos, + "DeleteUpdate": refresh, + "IsSub": isPremium || isBasic, }) if err != nil { fmt.Println("Error generating model popover") @@ -845,21 +846,22 @@ func LoadSettingsHandler(c *fiber.Ctx) error { stripeSubLink := "https://billing.stripe.com/p/login/6oE6sc0PTfvq1Hi288?prefilled_email=" + user.Email - openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists := getExistingKeys(c) + openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists, perplexityExists := getExistingKeys(c) isPremium, isBasic := IsCurrentUserSubscribed(c) out, err := settingPopoverTmpl.Execute(pongo2.Context{ - "IsLogin": checkIfLogin(c), - "OpenaiExists": openaiExists, - "AnthropicExists": anthropicExists, - "MistralExists": mistralExists, - "GroqExists": groqExists, - "GooseaiExists": gooseaiExists, - "GoogleExists": googleExists, - "AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists, - "isPremium": isPremium, - "isBasic": isBasic, - "StripeSubLink": stripeSubLink, + "IsLogin": checkIfLogin(c), + "OpenaiExists": openaiExists, + "AnthropicExists": anthropicExists, + "MistralExists": mistralExists, + "GroqExists": groqExists, + "GooseaiExists": gooseaiExists, + "GoogleExists": googleExists, + "PerplexityExists": perplexityExists, + "AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists || perplexityExists, + "isPremium": isPremium, + "isBasic": isBasic, + "StripeSubLink": stripeSubLink, }) if err != nil { fmt.Println("Error loading settings") diff --git a/EdgeDatabase.go b/EdgeDatabase.go index 2c0a61a..b43187d 100644 --- a/EdgeDatabase.go +++ b/EdgeDatabase.go @@ -12,11 +12,6 @@ import ( "github.com/gofiber/fiber/v2" ) -// So I have one client and one context for the database. All query use the same and it work well so far. -var edgeCtx context.Context -var edgeGlobalClient *edgedb.Client - -// I will not put a comment on all type, I think they are self-explaining. type Identity struct { ID edgedb.UUID `edgedb:"id"` Issuer string `edgedb:"issuer"` @@ -54,10 +49,6 @@ type Conversation struct { User User `edgedb:"user"` } -// An area is in between messages and conversation. -// In a normal chat, you have a list of message, easy. By here you need to add, kind of a new dimension. -// All "message" can have multiple messages. So I created a new type named Area. -// A conversation is a list of Area and an Area is a list of Message. Easy enough. type Area struct { ID edgedb.UUID `edgedb:"id"` Position int64 `edgedb:"position"` @@ -68,7 +59,7 @@ type Message struct { ID edgedb.UUID `edgedb:"id"` Content string `edgedb:"content"` Role string `edgedb:"role"` - Selected bool `edgedb:"selected"` // Selected can also be seen as "Active". This is the message that will be use for the request. + Selected bool `edgedb:"selected"` Date time.Time `edgedb:"date"` Area Area `edgedb:"area"` Conv Conversation `edgedb:"conversation"` @@ -85,9 +76,6 @@ type Usage struct { OutputToken int32 `edgedb:"output_token"` } -// A LLM is a bad name but I like it. -// It is more one instance of a model with it parameters. -// Maybe I will add more options later. type LLM struct { ID edgedb.UUID `edgedb:"id"` Name string `edgedb:"name"` @@ -120,6 +108,9 @@ type CompanyInfo struct { Icon string `edgedb:"icon"` } +var edgeCtx context.Context +var edgeGlobalClient *edgedb.Client + func init() { var ctx = context.Background() client, err := edgedb.CreateClient(ctx, edgedb.Options{}) @@ -288,7 +279,8 @@ func getAllSelectedMessages(c *fiber.Ctx) []Message { func checkIfHaveKey(c *fiber.Ctx) bool { var keys []Key - err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Query(edgeCtx, "SELECT global currentUser.setting.keys", &keys) + err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Query(edgeCtx, + "SELECT global currentUser.setting.keys", &keys) if err != nil { panic(err) } diff --git a/MyUtils.go b/MyUtils.go index 9b1d5bd..e83ef14 100644 --- a/MyUtils.go +++ b/MyUtils.go @@ -78,18 +78,19 @@ func addCodeHeader(htmlContent string, languages []string) string { return updatedHTML } -func getExistingKeys(c *fiber.Ctx) (bool, bool, bool, bool, bool, bool) { +func getExistingKeys(c *fiber.Ctx) (bool, bool, bool, bool, bool, bool, bool) { if edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}) == nil { - return false, false, false, false, false, false + return false, false, false, false, false, false, false } var ( - openaiExists bool - anthropicExists bool - mistralExists bool - groqExists bool - gooseaiExists bool - googleExists bool + openaiExists bool + anthropicExists bool + mistralExists bool + groqExists bool + gooseaiExists bool + googleExists bool + perplexityExists bool ) err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, ` @@ -158,7 +159,18 @@ func getExistingKeys(c *fiber.Ctx) (bool, bool, bool, bool, bool, bool) { panic(err) } - return openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists + err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, ` + select exists ( + select global currentUser.setting.keys + filter .company.name = "perplexity" + ); + `, &perplexityExists) + if err != nil { + fmt.Println("Error checking if Perplexity key exists") + panic(err) + } + + return openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists, perplexityExists } func Message2RequestMessage(messages []Message, context string) []RequestMessage { @@ -218,7 +230,9 @@ func GetAvailableModels(c *fiber.Ctx) []ModelInfo { name, icon } - } FILTER .modelID != 'none' AND .company.name != 'huggingface' AND .company IN global currentUser.setting.keys.company + } + FILTER .modelID != 'none' AND .company.name != 'huggingface' AND .company IN global currentUser.setting.keys.company + ORDER BY .company.name ASC THEN .name ASC `, &models) if err != nil { fmt.Println("Error getting models") diff --git a/Request.go b/Request.go index d84ad61..80d2e22 100644 --- a/Request.go +++ b/Request.go @@ -137,6 +137,8 @@ func GenerateMultipleMessagesHandler(c *fiber.Ctx) error { addMessageFunc = addHuggingfaceMessage case "google": addMessageFunc = addGoogleMessage + case "perplexity": + addMessageFunc = addPerplexityMessage } var messageID edgedb.UUID @@ -203,6 +205,7 @@ func GenerateMultipleMessagesHandler(c *fiber.Ctx) error { outIcon := `User Image` go func() { + // I do a ping because of sse size limit sendEvent( "swapContent-"+fmt.Sprintf("%d", message.Area.Position), ``, diff --git a/RequestPerplexity.go b/RequestPerplexity.go new file mode 100644 index 0000000..e902548 --- /dev/null +++ b/RequestPerplexity.go @@ -0,0 +1,184 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/edgedb/edgedb-go" + "github.com/gofiber/fiber/v2" +) + +type PerplexityChatCompletionRequest struct { + Model string `json:"model"` + Messages []RequestMessage `json:"messages"` + Temperature float64 `json:"temperature"` +} + +type PerplexityChatCompletionResponse struct { + ID string `json:"id"` + Object string `json:"object"` + Created int64 `json:"created"` + Model string `json:"model"` + Usage PerplexityUsage `json:"usage"` + Choices []PerplexityChoice `json:"choices"` +} + +type PerplexityUsage struct { + PromptTokens int32 `json:"prompt_tokens"` + CompletionTokens int32 `json:"completion_tokens"` + TotalTokens int32 `json:"total_tokens"` +} + +type PerplexityChoice struct { + Message Message `json:"message"` + FinishReason string `json:"finish_reason"` + Index int `json:"index"` +} + +func addPerplexityMessage(c *fiber.Ctx, llm LLM, selected bool) edgedb.UUID { + Messages := getAllSelectedMessages(c) + + chatCompletion, err := RequestPerplexity(c, llm.Model.ModelID, Messages, float64(llm.Temperature), llm.Context) + if err != nil { + fmt.Println("Error fetching user profile") + panic(err) + } else if len(chatCompletion.Choices) == 0 { + fmt.Println("No response from Perplexity") + id := insertBotMessage(c, "No response from Perplexity", selected, llm.ID) + return id + } else { + Content := chatCompletion.Choices[0].Message.Content + id := insertBotMessage(c, Content, selected, llm.ID) + return id + } +} + +func TestPerplexityKey(apiKey string) bool { + url := "https://api.perplexity.ai/chat/completions" + + // Convert messages to OpenAI format + perplexityMessages := []RequestMessage{ + { + Role: "user", + Content: "Hello", + }, + } + + requestBody := PerplexityChatCompletionRequest{ + Model: "llama-3-8b-instruct", + Messages: perplexityMessages, + 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 PerplexityChatCompletionResponse + err = json.Unmarshal(body, &chatCompletionResponse) + if err != nil { + return false + } + if chatCompletionResponse.Usage.CompletionTokens == 0 { + return false + } + return true +} + +func RequestPerplexity(c *fiber.Ctx, model string, messages []Message, temperature float64, context string) (PerplexityChatCompletionResponse, error) { + var apiKey string + err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, ` + with + filtered_keys := ( + select Key { + key + } filter .company.name = $0 AND .$0 + LIMIT 1 + `, &usedModelInfo, model) + if err != nil { + return PerplexityChatCompletionResponse{}, fmt.Errorf("error getting model info: %w", err) + } + + var inputCost float32 = float32(chatCompletionResponse.Usage.PromptTokens) * usedModelInfo.InputPrice + var outputCost float32 = float32(chatCompletionResponse.Usage.CompletionTokens) * usedModelInfo.OutputPrice + addUsage(c, inputCost, outputCost, chatCompletionResponse.Usage.PromptTokens, chatCompletionResponse.Usage.CompletionTokens, model) + + return chatCompletionResponse, nil +} diff --git a/main.go b/main.go index cc0427f..9cf5599 100644 --- a/main.go +++ b/main.go @@ -94,7 +94,6 @@ func main() { app.Post("/clearChat", ClearChatHandler) app.Get("/userMessage", GetUserMessageHandler) app.Post("/editMessage", EditMessageHandler) - app.Post("/archiveDefaultConversation", ArchiveDefaultConversationHandler) app.Get("/help", generateHelpChatHandler) // Settings routes @@ -103,14 +102,16 @@ func main() { // Popovers app.Get("/loadModelSelection", LoadModelSelectionHandler) app.Get("/loadConversationSelection", LoadConversationSelectionHandler) - app.Get("/refreshConversationSelection", RefreshConversationSelectionHandler) app.Get("/loadUsageKPI", LoadUsageKPIHandler) app.Get("/loadSettings", LoadSettingsHandler) - app.Post("/updateLLMPositionBatch", updateLLMPositionBatch) + app.Get("/refreshConversationSelection", RefreshConversationSelectionHandler) + + // Conversation routes app.Get("/createConversation", CreateConversationHandler) app.Get("/deleteConversation", DeleteConversationHandler) app.Get("/selectConversation", SelectConversationHandler) app.Post("/updateConversationPositionBatch", updateConversationPositionBatch) + app.Post("/archiveDefaultConversation", ArchiveDefaultConversationHandler) // Authentication app.Get("/signin", handleUiSignIn) @@ -119,8 +120,9 @@ func main() { app.Get("/callbackSignup", handleCallbackSignup) // LLM - app.Get("deleteLLM", deleteLLM) + app.Get("/deleteLLM", deleteLLM) app.Post("/createLLM", createLLM) + app.Post("/updateLLMPositionBatch", updateLLMPositionBatch) // Add static files app.Static("/", "./static") @@ -170,310 +172,73 @@ func main() { } 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") - gooseaiKey := c.FormValue("goose_key") - googleKey := c.FormValue("google_key") - var Exists bool - - // Handle OpenAI key - if openaiKey != "" { - if !TestOpenaiKey(openaiKey) { - return c.SendString("Invalid OpenAI API Key\n") - } - - // Check if the company key already exists - err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, ` - select exists ( - select global currentUser.setting.keys - filter .company.name = "openai" - ); - `, &Exists) - if err != nil { - fmt.Println("Error checking if OpenAI key exists") - panic(err) - } - - if Exists { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - UPDATE Key filter .company.name = $0 AND .key = $1 - SET { - key := $1, - } - `, "openai", openaiKey) - if err != nil { - fmt.Println("Error updating OpenAI key") - panic(err) - } - } else { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - WITH - c := (SELECT Company FILTER .name = "openai" LIMIT 1) - UPDATE global currentUser.setting - SET { - keys += ( - INSERT Key { - company := c, - key := $0, - name := "OpenAI API Key", - } - ) - }`, openaiKey) - if err != nil { - fmt.Println("Error adding OpenAI key") - panic(err) - } - } + keys := map[string]string{ + "openai": c.FormValue("openai_key"), + "anthropic": c.FormValue("anthropic_key"), + "mistral": c.FormValue("mistral_key"), + "groq": c.FormValue("groq_key"), + "gooseai": c.FormValue("goose_key"), + "google": c.FormValue("google_key"), + "perplexity": c.FormValue("perplexity_key"), } - // Handle Anthropic key - if anthropicKey != "" { - if !TestAnthropicKey(anthropicKey) { - return c.SendString("Invalid Anthropic API Key\n") - } - - // Check if the company key already exists - err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, ` - select exists ( - select global currentUser.setting.keys - filter .company.name = "anthropic" - ); - `, &Exists) - if err != nil { - fmt.Println("Error checking if Anthropic key exists") - panic(err) - } - - if Exists { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - UPDATE Key filter .company.name = "anthropic" AND .key = $0 - SET { - key := $0, - } - `, anthropicKey) - if err != nil { - fmt.Println("Error updating Anthropic key") - panic(err) - } - } else { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - WITH - c := (SELECT Company FILTER .name = "anthropic" LIMIT 1) - UPDATE global currentUser.setting - SET { - keys += ( - INSERT Key { - company := c, - key := $0, - name := "Anthropic API Key", - } - ) - }`, anthropicKey) - if err != nil { - fmt.Println("Error adding Anthropic key") - panic(err) - } - } + testFunctions := map[string]func(string) bool{ + "openai": TestOpenaiKey, + "anthropic": TestAnthropicKey, + "mistral": TestMistralKey, + "groq": TestGroqKey, + "gooseai": TestGooseaiKey, + "google": TestGoogleKey, + "perplexity": TestPerplexityKey, } - // Handle Mistral key - if mistralKey != "" { - if !TestMistralKey(mistralKey) { - return c.SendString("Invalid Mistral API Key\n") - } - - // Check if the company key already exists - err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, ` - select exists ( - select global currentUser.setting.keys - filter .company.name = "mistral" - ); - `, &Exists) - if err != nil { - fmt.Println("Error checking if Mistral key exists") - panic(err) - } - - if Exists { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - UPDATE Key filter .company.name = "mistral" AND .key = $0 - SET { - key := $0, + for company, key := range keys { + if key != "" { + if !testFunctions[company](key) { + return c.SendString(fmt.Sprintf("Invalid %s API Key\n", company)) } - `, mistralKey) + + var Exists bool + err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, ` + select exists ( + select global currentUser.setting.keys + filter .company.name = $0 + ); + `, &Exists, company) if err != nil { - fmt.Println("Error updating Mistral key") + fmt.Println("Error checking if key exists") panic(err) } - } else { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - WITH - c := (SELECT Company FILTER .name = "mistral" LIMIT 1) - UPDATE global currentUser.setting - SET { - keys += ( - INSERT Key { - company := c, - key := $0, - name := "Mistral API Key", - } - ) - }`, mistralKey) - if err != nil { - fmt.Println("Error adding Mistral key") - panic(err) - } - } - } - // Handle Groq key - if groqKey != "" { - if !TestGroqKey(groqKey) { - return c.SendString("Invalid Groq API Key\n") - } - - // Check if the company key already exists - err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, ` - select exists ( - select global currentUser.setting.keys - filter .company.name = "groq" - ); - `, &Exists) - if err != nil { - fmt.Println("Error checking if Groq key exists") - panic(err) - } - - if Exists { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - UPDATE Key filter .company.name = "groq" AND .key = $0 - SET { - key := $0, - } - `, groqKey) - if err != nil { - fmt.Println("Error updating Groq key") - panic(err) - } - } else { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - WITH - c := (SELECT Company FILTER .name = "groq" LIMIT 1) - UPDATE global currentUser.setting - SET { - keys += ( - INSERT Key { - company := c, - key := $0, - name := "Groq API Key", - } - ) - }`, groqKey) - if err != nil { - fmt.Println("Error adding Groq key") - panic(err) - } - } - } - - // Handle Gooseai key - if gooseaiKey != "" { - if !TestGooseaiKey(gooseaiKey) { - return c.SendString("Invalid Gooseai API Key\n") - } - - // Check if the company key already exists - err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, ` - select exists ( - select global currentUser.setting.keys - filter .company.name = "gooseai" - ); - `, &Exists) - if err != nil { - fmt.Println("Error checking if Gooseai key exists") - panic(err) - } - - if Exists { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - UPDATE Key filter .company.name = "gooseai" AND .key = $0 - SET { - key := $0, - } - `, gooseaiKey) - if err != nil { - fmt.Println("Error updating Gooseai key") - panic(err) - } - } else { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - WITH - c := (SELECT Company FILTER .name = "gooseai" LIMIT 1) - UPDATE global currentUser.setting - SET { - keys += ( - INSERT Key { - company := c, - key := $0, - name := "Gooseai API Key", - } - ) - }`, gooseaiKey) - if err != nil { - fmt.Println("Error adding Gooseai key") - panic(err) - } - } - } - - // Handle Google key - if googleKey != "" { - if !TestGoogleKey(googleKey) { - return c.SendString("Invalid Google API Key\n") - } - - // Check if the company key already exists - err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, ` - select exists ( - select global currentUser.setting.keys - filter .company.name = "google" - ); - `, &Exists) - if err != nil { - fmt.Println("Error checking if Google key exists") - panic(err) - } - - if Exists { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - UPDATE Key filter .company.name = "google" AND .key = $0 - SET { - key := $0, - } - `, googleKey) - if err != nil { - fmt.Println("Error updating Google key") - panic(err) - } - } else { - err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` - WITH - c := (SELECT Company FILTER .name = "google" LIMIT 1) - UPDATE global currentUser.setting - SET { - keys += ( - INSERT Key { - company := c, - key := $0, - name := "Google API Key", - } - ) - }`, googleKey) - if err != nil { - fmt.Println("Error adding Google key") - panic(err) + if Exists { + err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` + UPDATE Key filter .company.name = $0 AND .key = $1 + SET { + key := $1, + } + `, company, key) + if err != nil { + fmt.Println("Error updating key") + panic(err) + } + } else { + err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, ` + WITH + c := (SELECT Company FILTER .name = $0 LIMIT 1) + UPDATE global currentUser.setting + SET { + keys += ( + INSERT Key { + company := c, + key := $1, + name := $2 ++ " API Key", + } + ) + }`, company, key, company) + if err != nil { + fmt.Println("Error adding key") + panic(err) + } } } } diff --git a/static/icons/image.png b/static/icons/image.png new file mode 100644 index 0000000..252ffb8 Binary files /dev/null and b/static/icons/image.png differ diff --git a/static/icons/perplexity.png b/static/icons/perplexity.png new file mode 100644 index 0000000..f8de7fd Binary files /dev/null and b/static/icons/perplexity.png differ diff --git a/static/icons/perplexity2.png b/static/icons/perplexity2.png new file mode 100644 index 0000000..a34538e Binary files /dev/null and b/static/icons/perplexity2.png differ diff --git a/views/partials/chat-input.html b/views/partials/chat-input.html index d8ff82a..fd61775 100644 --- a/views/partials/chat-input.html +++ b/views/partials/chat-input.html @@ -86,7 +86,9 @@ textarea.addEventListener('keydown', handleTextareaKeydown); function toggleSendButton() { - document.getElementById('chat-input-send-btn').disabled = textarea.value.trim().length === 0 || document.getElementsByClassName('selected icon-llm').length === 0; + // check if generate-multiple-messages exists + var generateMultipleMessages = document.getElementById('generate-multiple-messages'); + document.getElementById('chat-input-send-btn').disabled = textarea.value.trim().length === 0 || document.getElementsByClassName('selected icon-llm').length === 0 || generateMultipleMessages !== null; } function clearTextArea() { diff --git a/views/partials/explain-llm-conv-chat.html b/views/partials/explain-llm-conv-chat.html index ab96567..3e4e8f6 100644 --- a/views/partials/explain-llm-conv-chat.html +++ b/views/partials/explain-llm-conv-chat.html @@ -1,4 +1,4 @@ -

JADE: The First Multi-Model Chatbot

+

JADE: The First Multi-Model Chatbot

JADE was built with simplicity in mind. The goal is to have a minimalist chatbot that supports all models without unnecessary features like importing files or images. This focus on simplicity allows us to improve how we use AI chatbots in other ways.

@@ -78,6 +78,7 @@
  • llama3-8b-8192
  • llama3-70b-8192
  • gemma-7b-it
  • +
  • mixtral-8x7b-32768
  • Google: @@ -88,4 +89,7 @@
  • Inference Endpoints (More custom to come)
  • - \ No newline at end of file + + +

    You can contact me at adrien.bouvais.pro@gmail.com if you want to add a new provider or if you have any questions. +

    \ No newline at end of file diff --git a/views/partials/message-bot.html b/views/partials/message-bot.html index c3a374a..aae867e 100644 --- a/views/partials/message-bot.html +++ b/views/partials/message-bot.html @@ -73,7 +73,7 @@
    - +

    Waiting... diff --git a/views/partials/popover-models.html b/views/partials/popover-models.html index 91a8907..5bebde5 100644 --- a/views/partials/popover-models.html +++ b/views/partials/popover-models.html @@ -28,11 +28,11 @@

    -