fix
This commit is contained in:
parent
419eacc0bb
commit
85e3fc2ec0
@ -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 := <str>$0,
|
||||
email := <str>$1,
|
||||
|
52
Chat.go
52
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")
|
||||
|
@ -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)
|
||||
}
|
||||
|
34
MyUtils.go
34
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")
|
||||
|
@ -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 := `<img src="` + selectedLLMs[idx].Model.Company.Icon + `" alt="User Image" id="selectedIcon-` + fmt.Sprintf("%d", message.Area.Position) + `">`
|
||||
|
||||
go func() {
|
||||
// I do a ping because of sse size limit
|
||||
sendEvent(
|
||||
"swapContent-"+fmt.Sprintf("%d", message.Area.Position),
|
||||
`<hx hx-get="/messageContent?id=`+message.ID.String()+`" hx-trigger="load" hx-swap="outerHTML"></hx>`,
|
||||
|
184
RequestPerplexity.go
Normal file
184
RequestPerplexity.go
Normal file
@ -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 = <str>$0 AND .<keys[is Setting].<setting[is User] = global currentUser
|
||||
)
|
||||
select filtered_keys.key limit 1
|
||||
`, &apiKey, "perplexity")
|
||||
if err != nil {
|
||||
return PerplexityChatCompletionResponse{}, fmt.Errorf("error getting Perplexity API key: %w", err)
|
||||
}
|
||||
|
||||
url := "https://api.perplexity.ai/chat/completions"
|
||||
|
||||
requestBody := PerplexityChatCompletionRequest{
|
||||
Model: model,
|
||||
Messages: Message2RequestMessage(messages, context),
|
||||
Temperature: temperature,
|
||||
}
|
||||
|
||||
jsonBody, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
return PerplexityChatCompletionResponse{}, fmt.Errorf("error marshaling JSON: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody))
|
||||
if err != nil {
|
||||
return PerplexityChatCompletionResponse{}, 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 PerplexityChatCompletionResponse{}, fmt.Errorf("error sending request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return PerplexityChatCompletionResponse{}, fmt.Errorf("error reading response body: %w", err)
|
||||
}
|
||||
|
||||
var chatCompletionResponse PerplexityChatCompletionResponse
|
||||
err = json.Unmarshal(body, &chatCompletionResponse)
|
||||
if err != nil {
|
||||
return PerplexityChatCompletionResponse{}, fmt.Errorf("error unmarshaling JSON: %w", err)
|
||||
}
|
||||
|
||||
var usedModelInfo ModelInfo
|
||||
err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
|
||||
SELECT ModelInfo {
|
||||
inputPrice,
|
||||
outputPrice
|
||||
}
|
||||
FILTER .modelID = <str>$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
|
||||
}
|
363
main.go
363
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 = <str>$0 AND .key = <str>$1
|
||||
SET {
|
||||
key := <str>$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 := <str>$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 = <str>$0
|
||||
SET {
|
||||
key := <str>$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 := <str>$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 = <str>$0
|
||||
SET {
|
||||
key := <str>$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 = <str>$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 := <str>$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 = <str>$0
|
||||
SET {
|
||||
key := <str>$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 := <str>$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 = <str>$0
|
||||
SET {
|
||||
key := <str>$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 := <str>$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 = <str>$0
|
||||
SET {
|
||||
key := <str>$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 := <str>$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 = <str>$0 AND .key = <str>$1
|
||||
SET {
|
||||
key := <str>$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 = <str>$0 LIMIT 1)
|
||||
UPDATE global currentUser.setting
|
||||
SET {
|
||||
keys += (
|
||||
INSERT Key {
|
||||
company := c,
|
||||
key := <str>$1,
|
||||
name := <str>$2 ++ " API Key",
|
||||
}
|
||||
)
|
||||
}`, company, key, company)
|
||||
if err != nil {
|
||||
fmt.Println("Error adding key")
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
static/icons/image.png
Normal file
BIN
static/icons/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
static/icons/perplexity.png
Normal file
BIN
static/icons/perplexity.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
BIN
static/icons/perplexity2.png
Normal file
BIN
static/icons/perplexity2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
@ -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() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
<h2>JADE: The First Multi-Model Chatbot</h2>
|
||||
<h1>JADE: The First Multi-Model Chatbot</h1>
|
||||
<p>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.</p>
|
||||
@ -78,6 +78,7 @@
|
||||
<li>llama3-8b-8192</li>
|
||||
<li>llama3-70b-8192</li>
|
||||
<li>gemma-7b-it</li>
|
||||
<li>mixtral-8x7b-32768</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Google:
|
||||
@ -88,4 +89,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li>Inference Endpoints (More custom to come)</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<p>You can contact me at adrien.bouvais.pro@gmail.com if you want to add a new provider or if you have any questions.
|
||||
</p>
|
@ -73,7 +73,7 @@
|
||||
<div class="is-flex is-align-items-start">
|
||||
<div class="message-content" id="content-{{ ConversationAreaId }}"
|
||||
sse-swap="swapContent-{{ ConversationAreaId }}">
|
||||
<hx hx-trigger="load" hx-get="/generateMultipleMessages"></hx>
|
||||
<hx hx-trigger="load" hx-get="/generateMultipleMessages" id="generate-multiple-messages"></hx>
|
||||
<div class='message-header'>
|
||||
<p>
|
||||
Waiting...
|
||||
|
@ -28,11 +28,11 @@
|
||||
</span>
|
||||
</button>
|
||||
<div>
|
||||
<button disabled class="button is-small is-primary mr-2 ml-5">
|
||||
<!--button disabled class="button is-small is-primary mr-2 ml-5">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-pen"></i>
|
||||
</span>
|
||||
</button>
|
||||
</button-->
|
||||
<button class="button is-small is-success is-outlined" id="create-model-button">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
@ -48,7 +48,7 @@
|
||||
<div class="select is-fullwidth is-small mb-3" id="model-id-input">
|
||||
<select name="selectedLLMId">
|
||||
{% for modelInfo in ModelInfos %}
|
||||
<option value="{{ modelInfo.ModelID }}">{{ modelInfo.ModelID }}</option>
|
||||
<option value="{{ modelInfo.ModelID }}">{{ modelInfo.Name }}</option>
|
||||
{% endfor %}
|
||||
<option value="{% if IsSub %}custom{% else %}none{% endif %}">Inference Endpoints</option>
|
||||
</select>
|
||||
|
@ -51,8 +51,7 @@
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field has-addons is-hidden" title="Gemini is unavailable because of Europe"
|
||||
id="gemini-field">
|
||||
<div class="field has-addons is-hidden" id="gemini-field">
|
||||
<p class="control has-icons-left is-expanded">
|
||||
<input class="input is-small {% if GoogleExists %}is-success{% endif %}" type="text"
|
||||
placeholder="Google API key" name="google_key" autocomplete="off">
|
||||
@ -61,6 +60,15 @@
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field has-addons is-hidden" id="perplexity-field">
|
||||
<p class="control has-icons-left is-expanded">
|
||||
<input class="input is-small {% if PerplexityExists %}is-success{% endif %}" type="text"
|
||||
placeholder="Perplexity API key" name="perplexity_key" autocomplete="off">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-lock"></i>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field has-addons is-hidden" id="goose-field"
|
||||
title="GooseAI chat API will be available soon">
|
||||
<p class="control has-icons-left is-expanded">
|
||||
|
@ -1,20 +1,31 @@
|
||||
<h1>JADE: The First Multi-Model Chatbot</h1>
|
||||
<p>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.</p>
|
||||
<p>The world of Large Language Models (LLMs) is vast and exciting, with each model boasting unique strengths and
|
||||
weaknesses. However, this variety presents a challenge: using all available LLMs is practically impossible due to
|
||||
cost and complexity. Wouldn't it be incredible to have an easy way to experiment with different models, compare
|
||||
their responses, and even choose the best model for a specific task?</p>
|
||||
|
||||
<p>One of my main concerns was the variety of models available, each excelling in different areas. Unfortunately using
|
||||
all of them can be impractical and very expensive.</p>
|
||||
<p>This is precisely why JADE was built. With a focus on simplicity, JADE eliminates unnecessary features like file or
|
||||
image uploads, allowing you to seamlessly interact with a variety of LLMs. This streamlined approach unlocks the
|
||||
potential to compare models, leverage their individual strengths, and even mitigate biases through multi-message
|
||||
conversations.</p>
|
||||
|
||||
<h2>Multi-Models</h2>
|
||||
<p>To address this, I created the first Multi-Model chatbot. The idea is to use multiple models within the same
|
||||
conversation. Here are the key points:</p>
|
||||
|
||||
<p>JADE is the first Multi-Model chatbot. The idea is to use multiple models within the same conversation. Here are the
|
||||
key points:</p>
|
||||
<ol>
|
||||
<li>When asking a question, you can query multiple models and compare their responses to choose the best one.</li>
|
||||
<li>The selected response can be used as the basis for the next message across all models. For example, a response
|
||||
from GPT-4 can be used by Claude Haiku in the next interaction.</li>
|
||||
<li>The selected response can be used as the basis for the next message across all models.</li>
|
||||
</ol>
|
||||
|
||||
<p>For example, a response from GPT-4 can be used by Claude Haiku in the next interaction</p>
|
||||
|
||||
<p>This approach offers several benefits. First, it ensures you always have access to the best possible response by
|
||||
leveraging the strengths of different models. Second, it provides a more comprehensive understanding of a topic by
|
||||
considering various perspectives. Finally, using responses from one model as context for another can lead to more
|
||||
engaging and insightful conversations.</p>
|
||||
|
||||
|
||||
<a class="button is-primary mt-2 mb-2" href="/signin">
|
||||
Sign in
|
||||
</a>
|
||||
|
Loading…
x
Reference in New Issue
Block a user