fix
This commit is contained in:
parent
419eacc0bb
commit
85e3fc2ec0
@ -233,7 +233,7 @@ func handleCallbackSignup(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
stripCustID := CreateNewStripeCustomer(providerName, providerEmail)
|
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 {
|
INSERT User {
|
||||||
stripe_id := <str>$0,
|
stripe_id := <str>$0,
|
||||||
email := <str>$1,
|
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 {
|
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
|
var llms []LLM
|
||||||
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Query(edgeCtx, `
|
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)
|
isPremium, isBasic := IsCurrentUserSubscribed(c)
|
||||||
|
|
||||||
out, err := modelPopoverTmpl.Execute(pongo2.Context{
|
out, err := modelPopoverTmpl.Execute(pongo2.Context{
|
||||||
"IsLogin": checkIfLogin(c),
|
"IsLogin": checkIfLogin(c),
|
||||||
"OpenaiExists": openaiExists,
|
"OpenaiExists": openaiExists,
|
||||||
"AnthropicExists": anthropicExists,
|
"AnthropicExists": anthropicExists,
|
||||||
"MistralExists": mistralExists,
|
"MistralExists": mistralExists,
|
||||||
"GroqExists": groqExists,
|
"GroqExists": groqExists,
|
||||||
"GooseaiExists": gooseaiExists,
|
"GooseaiExists": gooseaiExists,
|
||||||
"GoogleExists": googleExists,
|
"GoogleExists": googleExists,
|
||||||
"AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists,
|
"PerplexityExists": perplexityExists,
|
||||||
"LLMs": llms,
|
"AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists || perplexityExists,
|
||||||
"ModelInfos": modelInfos,
|
"LLMs": llms,
|
||||||
"DeleteUpdate": refresh,
|
"ModelInfos": modelInfos,
|
||||||
"IsSub": isPremium || isBasic,
|
"DeleteUpdate": refresh,
|
||||||
|
"IsSub": isPremium || isBasic,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error generating model popover")
|
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
|
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)
|
isPremium, isBasic := IsCurrentUserSubscribed(c)
|
||||||
|
|
||||||
out, err := settingPopoverTmpl.Execute(pongo2.Context{
|
out, err := settingPopoverTmpl.Execute(pongo2.Context{
|
||||||
"IsLogin": checkIfLogin(c),
|
"IsLogin": checkIfLogin(c),
|
||||||
"OpenaiExists": openaiExists,
|
"OpenaiExists": openaiExists,
|
||||||
"AnthropicExists": anthropicExists,
|
"AnthropicExists": anthropicExists,
|
||||||
"MistralExists": mistralExists,
|
"MistralExists": mistralExists,
|
||||||
"GroqExists": groqExists,
|
"GroqExists": groqExists,
|
||||||
"GooseaiExists": gooseaiExists,
|
"GooseaiExists": gooseaiExists,
|
||||||
"GoogleExists": googleExists,
|
"GoogleExists": googleExists,
|
||||||
"AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists,
|
"PerplexityExists": perplexityExists,
|
||||||
"isPremium": isPremium,
|
"AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists || perplexityExists,
|
||||||
"isBasic": isBasic,
|
"isPremium": isPremium,
|
||||||
"StripeSubLink": stripeSubLink,
|
"isBasic": isBasic,
|
||||||
|
"StripeSubLink": stripeSubLink,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error loading settings")
|
fmt.Println("Error loading settings")
|
||||||
|
@ -12,11 +12,6 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"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 {
|
type Identity struct {
|
||||||
ID edgedb.UUID `edgedb:"id"`
|
ID edgedb.UUID `edgedb:"id"`
|
||||||
Issuer string `edgedb:"issuer"`
|
Issuer string `edgedb:"issuer"`
|
||||||
@ -54,10 +49,6 @@ type Conversation struct {
|
|||||||
User User `edgedb:"user"`
|
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 {
|
type Area struct {
|
||||||
ID edgedb.UUID `edgedb:"id"`
|
ID edgedb.UUID `edgedb:"id"`
|
||||||
Position int64 `edgedb:"position"`
|
Position int64 `edgedb:"position"`
|
||||||
@ -68,7 +59,7 @@ type Message struct {
|
|||||||
ID edgedb.UUID `edgedb:"id"`
|
ID edgedb.UUID `edgedb:"id"`
|
||||||
Content string `edgedb:"content"`
|
Content string `edgedb:"content"`
|
||||||
Role string `edgedb:"role"`
|
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"`
|
Date time.Time `edgedb:"date"`
|
||||||
Area Area `edgedb:"area"`
|
Area Area `edgedb:"area"`
|
||||||
Conv Conversation `edgedb:"conversation"`
|
Conv Conversation `edgedb:"conversation"`
|
||||||
@ -85,9 +76,6 @@ type Usage struct {
|
|||||||
OutputToken int32 `edgedb:"output_token"`
|
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 {
|
type LLM struct {
|
||||||
ID edgedb.UUID `edgedb:"id"`
|
ID edgedb.UUID `edgedb:"id"`
|
||||||
Name string `edgedb:"name"`
|
Name string `edgedb:"name"`
|
||||||
@ -120,6 +108,9 @@ type CompanyInfo struct {
|
|||||||
Icon string `edgedb:"icon"`
|
Icon string `edgedb:"icon"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var edgeCtx context.Context
|
||||||
|
var edgeGlobalClient *edgedb.Client
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var ctx = context.Background()
|
var ctx = context.Background()
|
||||||
client, err := edgedb.CreateClient(ctx, edgedb.Options{})
|
client, err := edgedb.CreateClient(ctx, edgedb.Options{})
|
||||||
@ -288,7 +279,8 @@ func getAllSelectedMessages(c *fiber.Ctx) []Message {
|
|||||||
|
|
||||||
func checkIfHaveKey(c *fiber.Ctx) bool {
|
func checkIfHaveKey(c *fiber.Ctx) bool {
|
||||||
var keys []Key
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
34
MyUtils.go
34
MyUtils.go
@ -78,18 +78,19 @@ func addCodeHeader(htmlContent string, languages []string) string {
|
|||||||
return updatedHTML
|
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 {
|
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 (
|
var (
|
||||||
openaiExists bool
|
openaiExists bool
|
||||||
anthropicExists bool
|
anthropicExists bool
|
||||||
mistralExists bool
|
mistralExists bool
|
||||||
groqExists bool
|
groqExists bool
|
||||||
gooseaiExists bool
|
gooseaiExists bool
|
||||||
googleExists bool
|
googleExists bool
|
||||||
|
perplexityExists bool
|
||||||
)
|
)
|
||||||
|
|
||||||
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
|
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)
|
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 {
|
func Message2RequestMessage(messages []Message, context string) []RequestMessage {
|
||||||
@ -218,7 +230,9 @@ func GetAvailableModels(c *fiber.Ctx) []ModelInfo {
|
|||||||
name,
|
name,
|
||||||
icon
|
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)
|
`, &models)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error getting models")
|
fmt.Println("Error getting models")
|
||||||
|
@ -137,6 +137,8 @@ func GenerateMultipleMessagesHandler(c *fiber.Ctx) error {
|
|||||||
addMessageFunc = addHuggingfaceMessage
|
addMessageFunc = addHuggingfaceMessage
|
||||||
case "google":
|
case "google":
|
||||||
addMessageFunc = addGoogleMessage
|
addMessageFunc = addGoogleMessage
|
||||||
|
case "perplexity":
|
||||||
|
addMessageFunc = addPerplexityMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
var messageID edgedb.UUID
|
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) + `">`
|
outIcon := `<img src="` + selectedLLMs[idx].Model.Company.Icon + `" alt="User Image" id="selectedIcon-` + fmt.Sprintf("%d", message.Area.Position) + `">`
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
// I do a ping because of sse size limit
|
||||||
sendEvent(
|
sendEvent(
|
||||||
"swapContent-"+fmt.Sprintf("%d", message.Area.Position),
|
"swapContent-"+fmt.Sprintf("%d", message.Area.Position),
|
||||||
`<hx hx-get="/messageContent?id=`+message.ID.String()+`" hx-trigger="load" hx-swap="outerHTML"></hx>`,
|
`<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.Post("/clearChat", ClearChatHandler)
|
||||||
app.Get("/userMessage", GetUserMessageHandler)
|
app.Get("/userMessage", GetUserMessageHandler)
|
||||||
app.Post("/editMessage", EditMessageHandler)
|
app.Post("/editMessage", EditMessageHandler)
|
||||||
app.Post("/archiveDefaultConversation", ArchiveDefaultConversationHandler)
|
|
||||||
app.Get("/help", generateHelpChatHandler)
|
app.Get("/help", generateHelpChatHandler)
|
||||||
|
|
||||||
// Settings routes
|
// Settings routes
|
||||||
@ -103,14 +102,16 @@ func main() {
|
|||||||
// Popovers
|
// Popovers
|
||||||
app.Get("/loadModelSelection", LoadModelSelectionHandler)
|
app.Get("/loadModelSelection", LoadModelSelectionHandler)
|
||||||
app.Get("/loadConversationSelection", LoadConversationSelectionHandler)
|
app.Get("/loadConversationSelection", LoadConversationSelectionHandler)
|
||||||
app.Get("/refreshConversationSelection", RefreshConversationSelectionHandler)
|
|
||||||
app.Get("/loadUsageKPI", LoadUsageKPIHandler)
|
app.Get("/loadUsageKPI", LoadUsageKPIHandler)
|
||||||
app.Get("/loadSettings", LoadSettingsHandler)
|
app.Get("/loadSettings", LoadSettingsHandler)
|
||||||
app.Post("/updateLLMPositionBatch", updateLLMPositionBatch)
|
app.Get("/refreshConversationSelection", RefreshConversationSelectionHandler)
|
||||||
|
|
||||||
|
// Conversation routes
|
||||||
app.Get("/createConversation", CreateConversationHandler)
|
app.Get("/createConversation", CreateConversationHandler)
|
||||||
app.Get("/deleteConversation", DeleteConversationHandler)
|
app.Get("/deleteConversation", DeleteConversationHandler)
|
||||||
app.Get("/selectConversation", SelectConversationHandler)
|
app.Get("/selectConversation", SelectConversationHandler)
|
||||||
app.Post("/updateConversationPositionBatch", updateConversationPositionBatch)
|
app.Post("/updateConversationPositionBatch", updateConversationPositionBatch)
|
||||||
|
app.Post("/archiveDefaultConversation", ArchiveDefaultConversationHandler)
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
app.Get("/signin", handleUiSignIn)
|
app.Get("/signin", handleUiSignIn)
|
||||||
@ -119,8 +120,9 @@ func main() {
|
|||||||
app.Get("/callbackSignup", handleCallbackSignup)
|
app.Get("/callbackSignup", handleCallbackSignup)
|
||||||
|
|
||||||
// LLM
|
// LLM
|
||||||
app.Get("deleteLLM", deleteLLM)
|
app.Get("/deleteLLM", deleteLLM)
|
||||||
app.Post("/createLLM", createLLM)
|
app.Post("/createLLM", createLLM)
|
||||||
|
app.Post("/updateLLMPositionBatch", updateLLMPositionBatch)
|
||||||
|
|
||||||
// Add static files
|
// Add static files
|
||||||
app.Static("/", "./static")
|
app.Static("/", "./static")
|
||||||
@ -170,310 +172,73 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func addKeys(c *fiber.Ctx) error {
|
func addKeys(c *fiber.Ctx) error {
|
||||||
openaiKey := c.FormValue("openai_key")
|
keys := map[string]string{
|
||||||
anthropicKey := c.FormValue("anthropic_key")
|
"openai": c.FormValue("openai_key"),
|
||||||
mistralKey := c.FormValue("mistral_key")
|
"anthropic": c.FormValue("anthropic_key"),
|
||||||
groqKey := c.FormValue("groq_key")
|
"mistral": c.FormValue("mistral_key"),
|
||||||
gooseaiKey := c.FormValue("goose_key")
|
"groq": c.FormValue("groq_key"),
|
||||||
googleKey := c.FormValue("google_key")
|
"gooseai": c.FormValue("goose_key"),
|
||||||
var Exists bool
|
"google": c.FormValue("google_key"),
|
||||||
|
"perplexity": c.FormValue("perplexity_key"),
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Anthropic key
|
testFunctions := map[string]func(string) bool{
|
||||||
if anthropicKey != "" {
|
"openai": TestOpenaiKey,
|
||||||
if !TestAnthropicKey(anthropicKey) {
|
"anthropic": TestAnthropicKey,
|
||||||
return c.SendString("Invalid Anthropic API Key\n")
|
"mistral": TestMistralKey,
|
||||||
}
|
"groq": TestGroqKey,
|
||||||
|
"gooseai": TestGooseaiKey,
|
||||||
// Check if the company key already exists
|
"google": TestGoogleKey,
|
||||||
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
|
"perplexity": TestPerplexityKey,
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Mistral key
|
for company, key := range keys {
|
||||||
if mistralKey != "" {
|
if key != "" {
|
||||||
if !TestMistralKey(mistralKey) {
|
if !testFunctions[company](key) {
|
||||||
return c.SendString("Invalid Mistral API Key\n")
|
return c.SendString(fmt.Sprintf("Invalid %s API Key\n", company))
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
}
|
}
|
||||||
`, 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 {
|
if err != nil {
|
||||||
fmt.Println("Error updating Mistral key")
|
fmt.Println("Error checking if key exists")
|
||||||
panic(err)
|
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 Exists {
|
||||||
if groqKey != "" {
|
err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, `
|
||||||
if !TestGroqKey(groqKey) {
|
UPDATE Key filter .company.name = <str>$0 AND .key = <str>$1
|
||||||
return c.SendString("Invalid Groq API Key\n")
|
SET {
|
||||||
}
|
key := <str>$1,
|
||||||
|
}
|
||||||
// Check if the company key already exists
|
`, company, key)
|
||||||
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
|
if err != nil {
|
||||||
select exists (
|
fmt.Println("Error updating key")
|
||||||
select global currentUser.setting.keys
|
panic(err)
|
||||||
filter .company.name = "groq"
|
}
|
||||||
);
|
} else {
|
||||||
`, &Exists)
|
err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, `
|
||||||
if err != nil {
|
WITH
|
||||||
fmt.Println("Error checking if Groq key exists")
|
c := (SELECT Company FILTER .name = <str>$0 LIMIT 1)
|
||||||
panic(err)
|
UPDATE global currentUser.setting
|
||||||
}
|
SET {
|
||||||
|
keys += (
|
||||||
if Exists {
|
INSERT Key {
|
||||||
err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).Execute(edgeCtx, `
|
company := c,
|
||||||
UPDATE Key filter .company.name = "groq" AND .key = <str>$0
|
key := <str>$1,
|
||||||
SET {
|
name := <str>$2 ++ " API Key",
|
||||||
key := <str>$0,
|
}
|
||||||
}
|
)
|
||||||
`, groqKey)
|
}`, company, key, company)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error updating Groq key")
|
fmt.Println("Error adding key")
|
||||||
panic(err)
|
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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);
|
textarea.addEventListener('keydown', handleTextareaKeydown);
|
||||||
|
|
||||||
function toggleSendButton() {
|
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() {
|
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
|
<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
|
unnecessary features like importing files or images. This focus on simplicity allows us to improve how we use AI
|
||||||
chatbots in other ways.</p>
|
chatbots in other ways.</p>
|
||||||
@ -78,6 +78,7 @@
|
|||||||
<li>llama3-8b-8192</li>
|
<li>llama3-8b-8192</li>
|
||||||
<li>llama3-70b-8192</li>
|
<li>llama3-70b-8192</li>
|
||||||
<li>gemma-7b-it</li>
|
<li>gemma-7b-it</li>
|
||||||
|
<li>mixtral-8x7b-32768</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>Google:
|
<li>Google:
|
||||||
@ -88,4 +89,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>Inference Endpoints (More custom to come)</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="is-flex is-align-items-start">
|
||||||
<div class="message-content" id="content-{{ ConversationAreaId }}"
|
<div class="message-content" id="content-{{ ConversationAreaId }}"
|
||||||
sse-swap="swapContent-{{ 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'>
|
<div class='message-header'>
|
||||||
<p>
|
<p>
|
||||||
Waiting...
|
Waiting...
|
||||||
|
@ -28,11 +28,11 @@
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<div>
|
<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">
|
<span class="icon">
|
||||||
<i class="fa-solid fa-pen"></i>
|
<i class="fa-solid fa-pen"></i>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button-->
|
||||||
<button class="button is-small is-success is-outlined" id="create-model-button">
|
<button class="button is-small is-success is-outlined" id="create-model-button">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="fa-solid fa-plus"></i>
|
<i class="fa-solid fa-plus"></i>
|
||||||
@ -48,7 +48,7 @@
|
|||||||
<div class="select is-fullwidth is-small mb-3" id="model-id-input">
|
<div class="select is-fullwidth is-small mb-3" id="model-id-input">
|
||||||
<select name="selectedLLMId">
|
<select name="selectedLLMId">
|
||||||
{% for modelInfo in ModelInfos %}
|
{% for modelInfo in ModelInfos %}
|
||||||
<option value="{{ modelInfo.ModelID }}">{{ modelInfo.ModelID }}</option>
|
<option value="{{ modelInfo.ModelID }}">{{ modelInfo.Name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<option value="{% if IsSub %}custom{% else %}none{% endif %}">Inference Endpoints</option>
|
<option value="{% if IsSub %}custom{% else %}none{% endif %}">Inference Endpoints</option>
|
||||||
</select>
|
</select>
|
||||||
|
@ -51,8 +51,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="field has-addons is-hidden" title="Gemini is unavailable because of Europe"
|
<div class="field has-addons is-hidden" id="gemini-field">
|
||||||
id="gemini-field">
|
|
||||||
<p class="control has-icons-left is-expanded">
|
<p class="control has-icons-left is-expanded">
|
||||||
<input class="input is-small {% if GoogleExists %}is-success{% endif %}" type="text"
|
<input class="input is-small {% if GoogleExists %}is-success{% endif %}" type="text"
|
||||||
placeholder="Google API key" name="google_key" autocomplete="off">
|
placeholder="Google API key" name="google_key" autocomplete="off">
|
||||||
@ -61,6 +60,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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"
|
<div class="field has-addons is-hidden" id="goose-field"
|
||||||
title="GooseAI chat API will be available soon">
|
title="GooseAI chat API will be available soon">
|
||||||
<p class="control has-icons-left is-expanded">
|
<p class="control has-icons-left is-expanded">
|
||||||
|
@ -1,20 +1,31 @@
|
|||||||
<h1>JADE: The First Multi-Model Chatbot</h1>
|
<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
|
<p>The world of Large Language Models (LLMs) is vast and exciting, with each model boasting unique strengths and
|
||||||
unnecessary features like importing files or images. This focus on simplicity allows us to improve how we use AI
|
weaknesses. However, this variety presents a challenge: using all available LLMs is practically impossible due to
|
||||||
chatbots in other ways.</p>
|
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
|
<p>This is precisely why JADE was built. With a focus on simplicity, JADE eliminates unnecessary features like file or
|
||||||
all of them can be impractical and very expensive.</p>
|
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>
|
<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>
|
<ol>
|
||||||
<li>When asking a question, you can query multiple models and compare their responses to choose the best one.</li>
|
<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
|
<li>The selected response can be used as the basis for the next message across all models.</li>
|
||||||
from GPT-4 can be used by Claude Haiku in the next interaction.</li>
|
|
||||||
</ol>
|
</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">
|
<a class="button is-primary mt-2 mb-2" href="/signin">
|
||||||
Sign in
|
Sign in
|
||||||
</a>
|
</a>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user