Compare commits

...

10 Commits

Author SHA1 Message Date
edd5c58a7b Lots of fix
Can now add new api key again, and some minor bugs
2024-08-14 17:52:30 +02:00
4dca94b1ee Deleted wasm stuff 2024-08-14 17:51:12 +02:00
1d535f0db9 Better temperature (Anthropic from 0 to 1) 2024-08-14 14:47:54 +02:00
9d76a49b18 Fix the bug that make redo and edit unusable even when model selected 2024-08-14 13:51:05 +02:00
5d8ea744c7 Added together auto db update 2024-08-14 13:50:29 +02:00
c543956495 Updated todo 2024-08-13 19:54:52 +02:00
d2106283d8 Removed wasm 2024-08-13 19:52:23 +02:00
1f80c2d9a8 Update TODO 2024-08-13 11:23:57 +02:00
4caffa2f4b Fix 2024-08-13 11:23:50 +02:00
5066067ff8 Changed database to rename it 2024-08-13 11:22:42 +02:00
28 changed files with 477 additions and 734 deletions

View File

@ -261,7 +261,8 @@ func handleCallbackSignup(c *fiber.Ctx) error {
INSERT Conversation {
name := 'Default',
user := global currentUser,
position := 1
position := 1,
selected := true,
}`)
if err != nil {
fmt.Println("Error creating default conversation")
@ -291,13 +292,13 @@ func handleCallback(c *fiber.Ctx) error {
defer resp.Body.Close()
if resp.StatusCode != fiber.StatusOK {
return c.Render("error", fiber.Map{"Text": "Hello"}, "layouts/main")
return c.Render("error", fiber.Map{"Text": "Response Status is not OK"}, "layouts/main")
}
var tokenResponse TokenResponse
err = json.NewDecoder(resp.Body).Decode(&tokenResponse)
if err != nil {
return c.Render("error", fiber.Map{"Text": "Hello"}, "layouts/main")
return c.Render("error", fiber.Map{"Text": "Can't decode response"}, "layouts/main")
}
c.Cookie(&fiber.Cookie{
@ -318,7 +319,7 @@ func handleSignOut(c *fiber.Ctx) error {
}
func handleEmailVerification(c *fiber.Ctx) error {
return c.Render("error", fiber.Map{"Text": "Hello"}, "layouts/main")
return c.Render("error", fiber.Map{"Text": "Email auth not yet implemented"}, "layouts/main")
}
func SendNoreplyEmail(to string, subject string, content string) {

View File

@ -269,11 +269,14 @@ func GenerateMessageContentHTML(authCookie string, messageId string, onlyContent
panic(err)
}
_ = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": authCookie}).Execute(edgeCtx, `
err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": authCookie}).Execute(edgeCtx, `
UPDATE Message
FILTER .id = <uuid>$0
SET {selected := true};
`, messageUUID)
if err != nil {
panic(err)
}
return out
}

View File

@ -105,8 +105,30 @@ type CompanyInfo struct {
Icon string `edgedb:"icon"`
}
var edgeCtx context.Context
var edgeGlobalClient *edgedb.Client
var (
edgeCtx context.Context
edgeGlobalClient *edgedb.Client
allModelInfo []ModelInfo
allCompany []CompanyInfo
)
func getModelInfoByID(id edgedb.UUID) (ModelInfo, bool) {
for _, model := range allModelInfo {
if model.ID == id {
return model, true
}
}
return ModelInfo{}, false
}
func getCompanyInfoByID(id edgedb.UUID) (CompanyInfo, bool) {
for _, company := range allCompany {
if company.ID == id {
return company, true
}
}
return CompanyInfo{}, false
}
func init() {
var ctx = context.Background()
@ -118,6 +140,16 @@ func init() {
edgeCtx = ctx
edgeGlobalClient = client
err = edgeGlobalClient.Query(edgeCtx, `SELECT ModelInfo { id, inputPrice, outputPrice, modelID, name, company}`, &allModelInfo)
if err != nil {
panic("Can't get all ModelInfo")
}
err = edgeGlobalClient.Query(edgeCtx, `SELECT Company { id, icon, name}`, &allCompany)
if err != nil {
panic("Can't get all Company")
}
}
func checkIfLogin(c *fiber.Ctx) bool {

View File

@ -53,6 +53,7 @@ func GeneratePlaceholderHTML(c *fiber.Ctx, message string, selectedLLMIds []stri
key
},
modelInfo : {
id,
modelID,
company : {
icon,
@ -138,7 +139,7 @@ func GenerateMultipleMessagesHandler(c *fiber.Ctx) error {
defer cancel() // Ensure the context is cancelled to free resources
// Determine which message function to call based on the model
var addMessageFunc func(c *fiber.Ctx, llm LLM, messages []Message) string
var addMessageFunc func(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string
switch selectedLLMs[idx].Model.Company.Name {
case "openai":
addMessageFunc = RequestOpenai
@ -149,7 +150,7 @@ func GenerateMultipleMessagesHandler(c *fiber.Ctx) error {
case "groq":
addMessageFunc = RequestGroq
case "huggingface":
addMessageFunc = RequestHuggingface
addMessageFunc = RequestCustomEndpoint
case "google":
addMessageFunc = RequestGoogle
case "perplexity":
@ -164,7 +165,7 @@ func GenerateMultipleMessagesHandler(c *fiber.Ctx) error {
addMessageFunc = RequestDeepseek
}
var content string = addMessageFunc(c, selectedLLMs[idx], messages)
var content string = addMessageFunc(c, selectedLLMs[idx], messages, "")
var messageUUID edgedb.UUID = insertBotMessage(c, content, selectedLLMs[idx].ID)
var message Message

View File

@ -50,7 +50,7 @@ func init() {
AnthropicErrorCodes["529"] = "Provider error: Anthropic’s server is temporarily overloaded."
}
func RequestAnthropic(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestAnthropic(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
model := llm.Model.ModelID
temperature := float64(llm.Temperature)
context := llm.Context
@ -63,15 +63,20 @@ func RequestAnthropic(c *fiber.Ctx, llm LLM, messages []Message) string {
var apiKey struct {
Key string `edgedb:"key"`
}
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
if testApiKey == "" {
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
SELECT Key {
key
}
FILTER .<keys[is Setting].<setting[is User] = global currentUser and .company.name = "anthropic"
LIMIT 1
`, &apiKey, "anthropic")
if err != nil {
return "JADE internal error: 01-00-0000. Please contact the support."
if err != nil {
return "JADE internal error: 01-00-0000. Please contact the support."
}
} else {
apiKey.Key = testApiKey
}
url := "https://api.anthropic.com/v1/messages"
@ -127,16 +132,13 @@ func RequestAnthropic(c *fiber.Ctx, llm LLM, messages []Message) string {
return "JADE internal error: 01-01-0005. Please contact the support."
}
if testApiKey != "" {
return chatCompletionResponse.Content[0].Text
}
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 {
usedModelInfo, found := getModelInfoByID(llm.Model.ID)
if !found {
logErrorCode.Println("01-00-0006")
return "JADE internal error: 01-00-0006. Please contact the support."
}

View File

@ -10,7 +10,7 @@ import (
"github.com/gofiber/fiber/v2"
)
func RequestHuggingface(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestCustomEndpoint(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
url := llm.Endpoint.Endpoint
temperature := float64(llm.Temperature)
context := llm.Context

View File

@ -23,7 +23,7 @@ func init() {
DeepseekErrorCodes["503"] = "Provider error: Deepseek’s server is temporarily overloaded."
}
func RequestDeepseek(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestDeepseek(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
model := llm.Model.ModelID
temperature := float64(llm.Temperature)
context := llm.Context
@ -32,7 +32,8 @@ func RequestDeepseek(c *fiber.Ctx, llm LLM, messages []Message) string {
url := "https://api.deepseek.com/chat/completions"
var apiKey string
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
if testApiKey == "" {
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
with
filtered_keys := (
select Key {
@ -41,8 +42,12 @@ func RequestDeepseek(c *fiber.Ctx, llm LLM, messages []Message) string {
)
select filtered_keys.key limit 1
`, &apiKey, "deepseek")
if err != nil {
return "JADE internal error: 08-00-0000. Please contact the support."
if err != nil {
logErrorCode.Println("08-00-0000")
return "JADE internal error: 08-00-0000. Please contact the support."
}
} else {
apiKey = testApiKey
}
requestBody := OpenaiChatCompletionRequest{
@ -94,16 +99,13 @@ func RequestDeepseek(c *fiber.Ctx, llm LLM, messages []Message) string {
return "JADE internal error: 08-01-0005. Please contact the support."
}
if testApiKey != "" {
return chatCompletionResponse.Choices[0].Message.Content
}
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 {
usedModelInfo, found := getModelInfoByID(llm.Model.ID)
if !found {
logErrorCode.Println("08-00-0006")
return "JADE internal error: 08-00-0006. Please contact the support."
}

View File

@ -10,14 +10,15 @@ import (
"github.com/gofiber/fiber/v2"
)
func RequestFirework(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestFirework(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
model := llm.Model.ModelID
temperature := float64(llm.Temperature)
context := llm.Context
maxTokens := int(llm.MaxToken)
var apiKey string
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
if testApiKey == "" {
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
with
filtered_keys := (
select Key {
@ -26,9 +27,12 @@ func RequestFirework(c *fiber.Ctx, llm LLM, messages []Message) string {
)
select filtered_keys.key limit 1
`, &apiKey, "fireworks")
if err != nil {
logErrorCode.Println("09-00-0000")
return "JADE internal error: 09-00-0000. Please contact the support."
if err != nil {
logErrorCode.Println("09-00-0000")
return "JADE internal error: 09-00-0000. Please contact the support."
}
} else {
apiKey = testApiKey
}
url := "https://api.fireworks.ai/inference/v1/chat/completions"
@ -82,16 +86,13 @@ func RequestFirework(c *fiber.Ctx, llm LLM, messages []Message) string {
return "JADE internal error: 09-01-0005. Please contact the support."
}
if testApiKey != "" {
return chatCompletionResponse.Choices[0].Message.Content
}
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 {
usedModelInfo, found := getModelInfoByID(llm.Model.ID)
if !found {
logErrorCode.Println("09-00-0006")
return "JADE internal error: 09-00-0006. Please contact the support."
}

View File

@ -62,7 +62,7 @@ func init() {
GoogleErrorCodes["503"] = "Provider error: Servers are experiencing high traffic - Please retry your requests after a brief wait."
}
func RequestGoogle(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestGoogle(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
model := llm.Model.ModelID
// TODO: Use those parameters
@ -71,7 +71,8 @@ func RequestGoogle(c *fiber.Ctx, llm LLM, messages []Message) string {
//maxTokens := int(llm.MaxToken)
var apiKey string
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
if testApiKey == "" {
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
with
filtered_keys := (
select Key {
@ -80,8 +81,12 @@ func RequestGoogle(c *fiber.Ctx, llm LLM, messages []Message) string {
)
select filtered_keys.key limit 1
`, &apiKey, "google")
if err != nil {
return "JADE internal error: 03-00-0000. Please contact the support."
if err != nil {
logErrorCode.Println("03-00-0000")
return "JADE internal error: 03-00-0000. Please contact the support."
}
} else {
apiKey = testApiKey
}
url := "https://generativelanguage.googleapis.com/v1beta/models/" + model + ":generateContent?key=" + apiKey
@ -147,16 +152,13 @@ func RequestGoogle(c *fiber.Ctx, llm LLM, messages []Message) string {
return "JADE internal error: 03-01-0005. Please contact the support."
}
if testApiKey != "" {
return chatCompletionResponse.Candidates[0].Content.Parts[0].Text
}
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 {
usedModelInfo, found := getModelInfoByID(llm.Model.ID)
if !found {
logErrorCode.Println("03-00-0006")
return "JADE internal error: 03-00-0006. Please contact the support."
}

View File

@ -23,14 +23,15 @@ func init() {
GroqErrorCodes["503"] = "Provider error: The server is not ready to handle the request, often due to maintenance or overload. Wait before retrying the request."
}
func RequestGroq(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestGroq(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
model := llm.Model.ModelID
temperature := float64(llm.Temperature)
context := llm.Context
maxTokens := int(llm.MaxToken)
var apiKey string
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
if testApiKey == "" {
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
with
filtered_keys := (
select Key {
@ -39,9 +40,12 @@ func RequestGroq(c *fiber.Ctx, llm LLM, messages []Message) string {
)
select filtered_keys.key limit 1
`, &apiKey, "groq")
if err != nil {
logErrorCode.Println("04-00-0000")
return "JADE internal error: 04-00-0000. Please contact the support."
if err != nil {
logErrorCode.Println("04-00-0000")
return "JADE internal error: 04-00-0000. Please contact the support."
}
} else {
apiKey = testApiKey
}
url := "https://api.groq.com/openai/v1/chat/completions"
@ -95,16 +99,13 @@ func RequestGroq(c *fiber.Ctx, llm LLM, messages []Message) string {
return "JADE internal error: 04-01-0005. Please contact the support."
}
if testApiKey != "" {
return chatCompletionResponse.Choices[0].Message.Content
}
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 {
usedModelInfo, found := getModelInfoByID(llm.Model.ID)
if !found {
logErrorCode.Println("04-00-0006")
return "JADE internal error: 04-00-0006. Please contact the support."
}

View File

@ -10,14 +10,15 @@ import (
"github.com/gofiber/fiber/v2"
)
func RequestMistral(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestMistral(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
model := llm.Model.ModelID
temperature := float64(llm.Temperature)
context := llm.Context
maxTokens := int(llm.MaxToken)
var apiKey string
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
if testApiKey == "" {
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
with
filtered_keys := (
select Key {
@ -26,9 +27,12 @@ func RequestMistral(c *fiber.Ctx, llm LLM, messages []Message) string {
)
select filtered_keys.key limit 1
`, &apiKey, "mistral")
if err != nil {
logErrorCode.Println("02-00-0000")
return "JADE internal error: 02-00-0000. Please contact the support."
if err != nil {
logErrorCode.Println("02-00-0000")
return "JADE internal error: 02-00-0000. Please contact the support."
}
} else {
apiKey = testApiKey
}
url := "https://api.mistral.ai/v1/chat/completions"
@ -82,16 +86,13 @@ func RequestMistral(c *fiber.Ctx, llm LLM, messages []Message) string {
return "JADE internal error: 02-01-0005. Please contact the support."
}
if testApiKey != "" {
return chatCompletionResponse.Choices[0].Message.Content
}
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 {
usedModelInfo, found := getModelInfoByID(llm.Model.ID)
if !found {
logErrorCode.Println("02-00-0006")
return "JADE internal error: 02-00-0006. Please contact the support."
}

View File

@ -10,14 +10,15 @@ import (
"github.com/gofiber/fiber/v2"
)
func RequestNim(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestNim(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
model := llm.Model.ModelID
temperature := float64(llm.Temperature)
context := llm.Context
maxTokens := int(llm.MaxToken)
var apiKey string
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
if testApiKey == "" {
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
with
filtered_keys := (
select Key {
@ -26,9 +27,12 @@ func RequestNim(c *fiber.Ctx, llm LLM, messages []Message) string {
)
select filtered_keys.key limit 1
`, &apiKey, "nim")
if err != nil {
logErrorCode.Println("05-0")
return "JADE internal error: 05-00-0000. Please contact the support."
if err != nil {
logErrorCode.Println("05-00-0000")
return "JADE internal error: 05-00-0000. Please contact the support."
}
} else {
apiKey = testApiKey
}
url := "https://integrate.api.nvidia.com/v1/chat/completions"
@ -82,16 +86,13 @@ func RequestNim(c *fiber.Ctx, llm LLM, messages []Message) string {
return "JADE internal error: 05-01-0005. Please contact the support."
}
if testApiKey != "" {
return chatCompletionResponse.Choices[0].Message.Content
}
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 {
usedModelInfo, found := getModelInfoByID(llm.Model.ID)
if !found {
logErrorCode.Println("05-00-0006")
return "JADE internal error: 05-00-0006. Please contact the support."
}

View File

@ -51,14 +51,15 @@ func init() {
OpenaiErrorCodes["503"] = "Provider error: Servers are experiencing high traffic - Please retry your requests after a brief wait."
}
func RequestOpenai(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestOpenai(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
model := llm.Model.ModelID
temperature := float64(llm.Temperature)
context := llm.Context
maxTokens := int(llm.MaxToken)
var apiKey string
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
if testApiKey == "" {
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
with
filtered_keys := (
select Key {
@ -67,9 +68,12 @@ func RequestOpenai(c *fiber.Ctx, llm LLM, messages []Message) string {
)
select filtered_keys.key limit 1
`, &apiKey, "openai")
if err != nil {
logErrorCode.Println("00-00-0000")
return "JADE internal error: 00-00-0000. Please contact the support."
if err != nil {
logErrorCode.Println("00-00-0000")
return "JADE internal error: 00-00-0000. Please contact the support."
}
} else {
apiKey = testApiKey
}
url := "https://api.openai.com/v1/chat/completions"
@ -123,16 +127,13 @@ func RequestOpenai(c *fiber.Ctx, llm LLM, messages []Message) string {
return "JADE internal error: 00-01-0005. Please contact the support."
}
if testApiKey != "" {
return chatCompletionResponse.Choices[0].Message.Content
}
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 {
usedModelInfo, found := getModelInfoByID(llm.Model.ID)
if !found {
logErrorCode.Println("00-00-0006")
return "JADE internal error: 00-00-0006. Please contact the support."
}

View File

@ -10,14 +10,15 @@ import (
"github.com/gofiber/fiber/v2"
)
func RequestPerplexity(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestPerplexity(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
model := llm.Model.ModelID
temperature := float64(llm.Temperature)
context := llm.Context
maxTokens := int(llm.MaxToken)
var apiKey string
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
if testApiKey == "" {
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
with
filtered_keys := (
select Key {
@ -26,9 +27,12 @@ func RequestPerplexity(c *fiber.Ctx, llm LLM, messages []Message) string {
)
select filtered_keys.key limit 1
`, &apiKey, "perplexity")
if err != nil {
logErrorCode.Println("06-00-0000")
return "JADE internal error: 06-00-0000. Please contact the support."
if err != nil {
logErrorCode.Println("06-00-0000")
return "JADE internal error: 06-00-0000. Please contact the support."
}
} else {
apiKey = testApiKey
}
url := "https://api.perplexity.ai/chat/completions"
@ -82,16 +86,13 @@ func RequestPerplexity(c *fiber.Ctx, llm LLM, messages []Message) string {
return "JADE internal error: 06-01-0005. Please contact the support."
}
if testApiKey != "" {
return chatCompletionResponse.Choices[0].Message.Content
}
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 {
usedModelInfo, found := getModelInfoByID(llm.Model.ID)
if !found {
logErrorCode.Println("06-00-0006")
return "JADE internal error: 06-00-0006. Please contact the support."
}

View File

@ -4,9 +4,12 @@ import (
"bytes"
"encoding/json"
"io"
"math"
"net/http"
"strings"
"fmt"
"github.com/gofiber/fiber/v2"
)
@ -42,14 +45,15 @@ func init() {
TogetherErrorCodes["529"] = "Provider error: n unexpected error has occurred internal to Together’s systems."
}
func RequestTogether(c *fiber.Ctx, llm LLM, messages []Message) string {
func RequestTogether(c *fiber.Ctx, llm LLM, messages []Message, testApiKey string) string {
model := llm.Model.ModelID
temperature := float64(llm.Temperature)
context := llm.Context
maxTokens := int(llm.MaxToken)
var apiKey string
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
if testApiKey == "" {
err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
with
filtered_keys := (
select Key {
@ -58,9 +62,12 @@ func RequestTogether(c *fiber.Ctx, llm LLM, messages []Message) string {
)
select filtered_keys.key limit 1
`, &apiKey, "together")
if err != nil {
logErrorCode.Println("07-00-0000")
return "JADE internal error: 07-00-0000. Please contact the support."
if err != nil {
logErrorCode.Println("07-00-0000")
return "JADE internal error: 07-00-0000. Please contact the support."
}
} else {
apiKey = testApiKey
}
url := "https://api.together.xyz/v1/completions"
@ -114,16 +121,13 @@ func RequestTogether(c *fiber.Ctx, llm LLM, messages []Message) string {
return "JADE internal error: 07-01-0005. Please contact the support."
}
if testApiKey != "" {
return chatCompletionResponse.Choices[0].Text
}
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 {
usedModelInfo, found := getModelInfoByID(llm.Model.ID)
if !found {
logErrorCode.Println("07-00-0006")
return "JADE internal error: 07-00-0006. Please contact the support."
}
@ -139,3 +143,162 @@ func RequestTogether(c *fiber.Ctx, llm LLM, messages []Message) string {
return chatCompletionResponse.Choices[0].Text
}
type TogetherModel struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Type string `json:"type"`
DisplayName string `json:"display_name"`
Organization string `json:"organization"`
Link string `json:"link"`
License string `json:"license"`
ContextLength int `json:"context_length"`
Pricing TogetherPricing `json:"pricing"`
}
type TogetherPricing struct {
Hourly float64 `json:"hourly"`
Input float64 `json:"input"`
Output float64 `json:"output"`
Base float64 `json:"base"`
Finetune float64 `json:"finetune"`
}
// TODO: Use my key everytime, no auth needed
func UpdateTogetherModels(c *fiber.Ctx, currentModelInfos []ModelInfo) {
url := "https://api.together.xyz/v1/models"
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, "together")
if err != nil {
logErrorCode.Println("08-00-0000")
return
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
logErrorCode.Println("08-01-0001")
return
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
logErrorCode.Println("08-02-0002")
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
logErrorCode.Println("08-01-0003")
return
}
if resp.StatusCode != http.StatusOK {
logErrorCode.Println("08-03-0004 -", resp.Status, "-", string(body))
return
}
var togetherModels []TogetherModel
err = json.Unmarshal(body, &togetherModels)
if err != nil {
logErrorCode.Println("08-01-0005")
return
}
var exist bool
var savedModel ModelInfo
var newModelFound bool = false
const epsilon = 1e-9
fmt.Println("Updating Together ModelInfo:")
for _, model := range togetherModels {
if model.Type == "chat" {
exist = false
for _, currentModel := range currentModelInfos {
if currentModel.ModelID == model.ID {
exist = true
savedModel = currentModel
break
}
}
if exist {
if math.Abs(model.Pricing.Input/1000000-float64(savedModel.InputPrice)) > epsilon ||
math.Abs(model.Pricing.Output/1000000-float64(savedModel.OutputPrice)) > epsilon {
fmt.Println("Found one existing model with changed price:", model.ID, ". Updating price from ", float64(savedModel.InputPrice), " to ", model.Pricing.Input/1000000)
err = edgeGlobalClient.Execute(edgeCtx, `
UPDATE ModelInfo FILTER .modelID = <str>$0 SET { inputPrice := <float32>$1, outputPrice := <float32>$2}
`, model.ID, float32(model.Pricing.Input)/1000000, float32(model.Pricing.Output)/1000000)
if err != nil {
fmt.Println("Error updating price:", err.Error())
}
}
} else {
fmt.Println("Found new model:", model.ID)
newModelFound = true
err = edgeGlobalClient.Execute(edgeCtx, `
INSERT ModelInfo { name := "New model, name coming soon...", modelID := <str>$0, inputPrice := <float32>$1, outputPrice := <float32>$2, company := assert_single(( SELECT Company FILTER .name = <str>$3))}
`, model.ID, float32(model.Pricing.Input)/1000000, float32(model.Pricing.Output)/1000000, "together")
if err != nil {
fmt.Println("Error creating new modelInfo:", err.Error())
}
}
}
}
if newModelFound {
SendNoreplyEmail("adrien.bouvais.pro@gmail.com", "New Together models found", "Look like new model have been found, you should name it.")
}
// Delete all unfound models
for _, currentModel := range currentModelInfos {
if currentModel.Company.Name != "together" {
continue
}
exist = false
for _, model := range togetherModels {
if model.ID == currentModel.ModelID {
exist = true
}
}
if !exist {
fmt.Println("A ModelInfo using this modelID:", currentModel.ModelID, ", was found using a modelID that doesn't exist anymore. Deleting it.")
err = edgeGlobalClient.Execute(edgeCtx, `
DELETE ModelInfo FILTER .modelID = <str>$0 AND .company.name = <str>$1
`, currentModel.ModelID, "together")
if err != nil {
fmt.Println("Error deleting a ModelInfo with an unfound modelID.")
}
}
}
}
func UpdateTogetherModelsHandler(c *fiber.Ctx) error {
if !IsUserAdmin(c) {
return c.SendString("That's for admin, how did you manage to come here ?")
}
var currentModelInfos []ModelInfo
err := edgeGlobalClient.Query(edgeCtx, `SELECT ModelInfo { id, name, modelID, inputPrice, outputPrice, company}`, &currentModelInfos)
if err != nil {
fmt.Println("Error getting all ModelInfo for updating them")
}
UpdateTogetherModels(c, currentModelInfos)
return c.SendString("Models from together ai updated")
}

21
TODO.md
View File

@ -1,12 +1,12 @@
# Bugs
[ ] Sometime I can redo or edit but the button is not available
[X] Sometime I can redo or edit but the button is not available
[X] The SSE event that sometime fails after some times. Reconnect when sending a new message.
[X] 2 selected messages
[X] On first response, code block width are too long
[X] Change Terms of service to say that I use one cookie
[X] Change the lastSelectedLLMs, 2 users can't use the same time...
[X] CTRL + Enter not working
[ ] Add all Together AI models
[X] Add all Together AI models
# Features
[X] SSE to WebSocket
@ -16,21 +16,18 @@
[X] Add Deepseek API
[X] Add TogetherAI API
[X] Add Nvidia NIM
[ ] Host the database on Fly.io
[ ] Add login with email and password
[ ] Add login with other provider
[ ] Better temperature settings. (Anthropic from 0-1, OpenAI from 0-2, DeepSeek from -2-2, ect)
[ ] Auto update the modelsInfos list
[ ] Add login with AppleID
[X] Better temperature settings. (Anthropic from 0-1, OpenAI from 0-2, DeepSeek from -2-2, ect)
[X] Auto update the modelsInfos db (At least remove unavailable models and send me an email when new one arrive)
[ ] Encrypt the API keys
[ ] Send me an email every xh with the new code error logs
# Opti
[ ] Import all company and modelInfos at the start to not request the db
[ ] Reduce db query
[ ] Look to reduce memory
# Other
[ ] Remove all panic
[ ] Change the terms of service and enter keys page to an HTML
[ ] Split Chat.go into smaller files
[ ] Create a Request package
[ ] Use the normal RequestProvider function instead of TestProvider to remove TestProvider
[ ] Add a dashboard to see the use of the app (Like how many this route or this model, ect) accross all users
[X] Use the normal RequestProvider function instead of TestProvider to remove TestProvider
[ ] Implement the JADE_IN_DEV env variable to dev locally on my laptop

BIN
dbschema/db-dump-migration Normal file

Binary file not shown.

BIN
dbschema/db-migration Normal file

Binary file not shown.

View File

@ -1 +0,0 @@
nbwt1_eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJlZGIuZC5hbGwiOnRydWUsImVkYi5pIjpbIk1yQm91bnR5L0pBREUiXSwiZWRiLnIuYWxsIjp0cnVlLCJpYXQiOjE3MTQ2NzcwMjQsImlzcyI6ImF3cy5lZGdlZGIuY2xvdWQiLCJqdGkiOiJvVUJNaWdpM0VlLXZ5MXZMejdLQUNBIiwic3ViIjoic3BWSkxBTV9FZS0yY1dfZzRSY1V0ZyJ9.awIKoPzGKsBzAfTgI2HwmmFf8fbHKTsVV57M3lFSn2jbPEMEZhtwJdvsRoPGRiD922qSFFVmcPQ_IKkVNVZT6g

View File

@ -1,2 +1,2 @@
[edgedb]
server-version = "5.2"
server-version = "5.6"

View File

@ -123,6 +123,9 @@ func main() {
app.Get("/ws", websocket.New(handleWebSocket))
app.Get("/errorLogs", ErrorCodeLogsHandler)
// Update models routes
app.Get("/models/together", UpdateTogetherModelsHandler)
// Start server
if err := app.Listen(":8080"); err != nil {
log.Fatal(err)
@ -144,7 +147,7 @@ func addKeys(c *fiber.Ctx) error {
"deepseek": c.FormValue("deepseek_key"),
}
requestFunctions := map[string]func(*fiber.Ctx, LLM, []Message) string{
requestFunctions := map[string]func(*fiber.Ctx, LLM, []Message, string) string{
"openai": RequestOpenai,
"anthropic": RequestAnthropic,
"mistral": RequestMistral,
@ -205,7 +208,7 @@ func addKeys(c *fiber.Ctx) error {
Context: "",
}
var responseText string = requestFunctions[company](c, llm, messages)
var responseText string = requestFunctions[company](c, llm, messages, key)
if responseText == "" || strings.Contains(responseText, "JADE internal error") || strings.Contains(responseText, "Provider error") {
return c.SendString(fmt.Sprintf("Invalid %s API Key\n", company))

Binary file not shown.

View File

@ -1,561 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
"use strict";
(() => {
const enosys = () => {
const err = new Error("not implemented");
err.code = "ENOSYS";
return err;
};
if (!globalThis.fs) {
let outputBuf = "";
globalThis.fs = {
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
writeSync(fd, buf) {
outputBuf += decoder.decode(buf);
const nl = outputBuf.lastIndexOf("\n");
if (nl != -1) {
console.log(outputBuf.substring(0, nl));
outputBuf = outputBuf.substring(nl + 1);
}
return buf.length;
},
write(fd, buf, offset, length, position, callback) {
if (offset !== 0 || length !== buf.length || position !== null) {
callback(enosys());
return;
}
const n = this.writeSync(fd, buf);
callback(null, n);
},
chmod(path, mode, callback) { callback(enosys()); },
chown(path, uid, gid, callback) { callback(enosys()); },
close(fd, callback) { callback(enosys()); },
fchmod(fd, mode, callback) { callback(enosys()); },
fchown(fd, uid, gid, callback) { callback(enosys()); },
fstat(fd, callback) { callback(enosys()); },
fsync(fd, callback) { callback(null); },
ftruncate(fd, length, callback) { callback(enosys()); },
lchown(path, uid, gid, callback) { callback(enosys()); },
link(path, link, callback) { callback(enosys()); },
lstat(path, callback) { callback(enosys()); },
mkdir(path, perm, callback) { callback(enosys()); },
open(path, flags, mode, callback) { callback(enosys()); },
read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
readdir(path, callback) { callback(enosys()); },
readlink(path, callback) { callback(enosys()); },
rename(from, to, callback) { callback(enosys()); },
rmdir(path, callback) { callback(enosys()); },
stat(path, callback) { callback(enosys()); },
symlink(path, link, callback) { callback(enosys()); },
truncate(path, length, callback) { callback(enosys()); },
unlink(path, callback) { callback(enosys()); },
utimes(path, atime, mtime, callback) { callback(enosys()); },
};
}
if (!globalThis.process) {
globalThis.process = {
getuid() { return -1; },
getgid() { return -1; },
geteuid() { return -1; },
getegid() { return -1; },
getgroups() { throw enosys(); },
pid: -1,
ppid: -1,
umask() { throw enosys(); },
cwd() { throw enosys(); },
chdir() { throw enosys(); },
}
}
if (!globalThis.crypto) {
throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
}
if (!globalThis.performance) {
throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");
}
if (!globalThis.TextEncoder) {
throw new Error("globalThis.TextEncoder is not available, polyfill required");
}
if (!globalThis.TextDecoder) {
throw new Error("globalThis.TextDecoder is not available, polyfill required");
}
const encoder = new TextEncoder("utf-8");
const decoder = new TextDecoder("utf-8");
globalThis.Go = class {
constructor() {
this.argv = ["js"];
this.env = {};
this.exit = (code) => {
if (code !== 0) {
console.warn("exit code:", code);
}
};
this._exitPromise = new Promise((resolve) => {
this._resolveExitPromise = resolve;
});
this._pendingEvent = null;
this._scheduledTimeouts = new Map();
this._nextCallbackTimeoutID = 1;
const setInt64 = (addr, v) => {
this.mem.setUint32(addr + 0, v, true);
this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
}
const setInt32 = (addr, v) => {
this.mem.setUint32(addr + 0, v, true);
}
const getInt64 = (addr) => {
const low = this.mem.getUint32(addr + 0, true);
const high = this.mem.getInt32(addr + 4, true);
return low + high * 4294967296;
}
const loadValue = (addr) => {
const f = this.mem.getFloat64(addr, true);
if (f === 0) {
return undefined;
}
if (!isNaN(f)) {
return f;
}
const id = this.mem.getUint32(addr, true);
return this._values[id];
}
const storeValue = (addr, v) => {
const nanHead = 0x7FF80000;
if (typeof v === "number" && v !== 0) {
if (isNaN(v)) {
this.mem.setUint32(addr + 4, nanHead, true);
this.mem.setUint32(addr, 0, true);
return;
}
this.mem.setFloat64(addr, v, true);
return;
}
if (v === undefined) {
this.mem.setFloat64(addr, 0, true);
return;
}
let id = this._ids.get(v);
if (id === undefined) {
id = this._idPool.pop();
if (id === undefined) {
id = this._values.length;
}
this._values[id] = v;
this._goRefCounts[id] = 0;
this._ids.set(v, id);
}
this._goRefCounts[id]++;
let typeFlag = 0;
switch (typeof v) {
case "object":
if (v !== null) {
typeFlag = 1;
}
break;
case "string":
typeFlag = 2;
break;
case "symbol":
typeFlag = 3;
break;
case "function":
typeFlag = 4;
break;
}
this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
this.mem.setUint32(addr, id, true);
}
const loadSlice = (addr) => {
const array = getInt64(addr + 0);
const len = getInt64(addr + 8);
return new Uint8Array(this._inst.exports.mem.buffer, array, len);
}
const loadSliceOfValues = (addr) => {
const array = getInt64(addr + 0);
const len = getInt64(addr + 8);
const a = new Array(len);
for (let i = 0; i < len; i++) {
a[i] = loadValue(array + i * 8);
}
return a;
}
const loadString = (addr) => {
const saddr = getInt64(addr + 0);
const len = getInt64(addr + 8);
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
}
const timeOrigin = Date.now() - performance.now();
this.importObject = {
_gotest: {
add: (a, b) => a + b,
},
gojs: {
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
// This changes the SP, thus we have to update the SP used by the imported function.
// func wasmExit(code int32)
"runtime.wasmExit": (sp) => {
sp >>>= 0;
const code = this.mem.getInt32(sp + 8, true);
this.exited = true;
delete this._inst;
delete this._values;
delete this._goRefCounts;
delete this._ids;
delete this._idPool;
this.exit(code);
},
// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
"runtime.wasmWrite": (sp) => {
sp >>>= 0;
const fd = getInt64(sp + 8);
const p = getInt64(sp + 16);
const n = this.mem.getInt32(sp + 24, true);
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
},
// func resetMemoryDataView()
"runtime.resetMemoryDataView": (sp) => {
sp >>>= 0;
this.mem = new DataView(this._inst.exports.mem.buffer);
},
// func nanotime1() int64
"runtime.nanotime1": (sp) => {
sp >>>= 0;
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
},
// func walltime() (sec int64, nsec int32)
"runtime.walltime": (sp) => {
sp >>>= 0;
const msec = (new Date).getTime();
setInt64(sp + 8, msec / 1000);
this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
},
// func scheduleTimeoutEvent(delay int64) int32
"runtime.scheduleTimeoutEvent": (sp) => {
sp >>>= 0;
const id = this._nextCallbackTimeoutID;
this._nextCallbackTimeoutID++;
this._scheduledTimeouts.set(id, setTimeout(
() => {
this._resume();
while (this._scheduledTimeouts.has(id)) {
// for some reason Go failed to register the timeout event, log and try again
// (temporary workaround for https://github.com/golang/go/issues/28975)
console.warn("scheduleTimeoutEvent: missed timeout event");
this._resume();
}
},
getInt64(sp + 8),
));
this.mem.setInt32(sp + 16, id, true);
},
// func clearTimeoutEvent(id int32)
"runtime.clearTimeoutEvent": (sp) => {
sp >>>= 0;
const id = this.mem.getInt32(sp + 8, true);
clearTimeout(this._scheduledTimeouts.get(id));
this._scheduledTimeouts.delete(id);
},
// func getRandomData(r []byte)
"runtime.getRandomData": (sp) => {
sp >>>= 0;
crypto.getRandomValues(loadSlice(sp + 8));
},
// func finalizeRef(v ref)
"syscall/js.finalizeRef": (sp) => {
sp >>>= 0;
const id = this.mem.getUint32(sp + 8, true);
this._goRefCounts[id]--;
if (this._goRefCounts[id] === 0) {
const v = this._values[id];
this._values[id] = null;
this._ids.delete(v);
this._idPool.push(id);
}
},
// func stringVal(value string) ref
"syscall/js.stringVal": (sp) => {
sp >>>= 0;
storeValue(sp + 24, loadString(sp + 8));
},
// func valueGet(v ref, p string) ref
"syscall/js.valueGet": (sp) => {
sp >>>= 0;
const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 32, result);
},
// func valueSet(v ref, p string, x ref)
"syscall/js.valueSet": (sp) => {
sp >>>= 0;
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
},
// func valueDelete(v ref, p string)
"syscall/js.valueDelete": (sp) => {
sp >>>= 0;
Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
},
// func valueIndex(v ref, i int) ref
"syscall/js.valueIndex": (sp) => {
sp >>>= 0;
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
},
// valueSetIndex(v ref, i int, x ref)
"syscall/js.valueSetIndex": (sp) => {
sp >>>= 0;
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
},
// func valueCall(v ref, m string, args []ref) (ref, bool)
"syscall/js.valueCall": (sp) => {
sp >>>= 0;
try {
const v = loadValue(sp + 8);
const m = Reflect.get(v, loadString(sp + 16));
const args = loadSliceOfValues(sp + 32);
const result = Reflect.apply(m, v, args);
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 56, result);
this.mem.setUint8(sp + 64, 1);
} catch (err) {
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 56, err);
this.mem.setUint8(sp + 64, 0);
}
},
// func valueInvoke(v ref, args []ref) (ref, bool)
"syscall/js.valueInvoke": (sp) => {
sp >>>= 0;
try {
const v = loadValue(sp + 8);
const args = loadSliceOfValues(sp + 16);
const result = Reflect.apply(v, undefined, args);
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, result);
this.mem.setUint8(sp + 48, 1);
} catch (err) {
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, err);
this.mem.setUint8(sp + 48, 0);
}
},
// func valueNew(v ref, args []ref) (ref, bool)
"syscall/js.valueNew": (sp) => {
sp >>>= 0;
try {
const v = loadValue(sp + 8);
const args = loadSliceOfValues(sp + 16);
const result = Reflect.construct(v, args);
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, result);
this.mem.setUint8(sp + 48, 1);
} catch (err) {
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, err);
this.mem.setUint8(sp + 48, 0);
}
},
// func valueLength(v ref) int
"syscall/js.valueLength": (sp) => {
sp >>>= 0;
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
},
// valuePrepareString(v ref) (ref, int)
"syscall/js.valuePrepareString": (sp) => {
sp >>>= 0;
const str = encoder.encode(String(loadValue(sp + 8)));
storeValue(sp + 16, str);
setInt64(sp + 24, str.length);
},
// valueLoadString(v ref, b []byte)
"syscall/js.valueLoadString": (sp) => {
sp >>>= 0;
const str = loadValue(sp + 8);
loadSlice(sp + 16).set(str);
},
// func valueInstanceOf(v ref, t ref) bool
"syscall/js.valueInstanceOf": (sp) => {
sp >>>= 0;
this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
},
// func copyBytesToGo(dst []byte, src ref) (int, bool)
"syscall/js.copyBytesToGo": (sp) => {
sp >>>= 0;
const dst = loadSlice(sp + 8);
const src = loadValue(sp + 32);
if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
this.mem.setUint8(sp + 48, 0);
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
setInt64(sp + 40, toCopy.length);
this.mem.setUint8(sp + 48, 1);
},
// func copyBytesToJS(dst ref, src []byte) (int, bool)
"syscall/js.copyBytesToJS": (sp) => {
sp >>>= 0;
const dst = loadValue(sp + 8);
const src = loadSlice(sp + 16);
if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
this.mem.setUint8(sp + 48, 0);
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
setInt64(sp + 40, toCopy.length);
this.mem.setUint8(sp + 48, 1);
},
"debug": (value) => {
console.log(value);
},
}
};
}
async run(instance) {
if (!(instance instanceof WebAssembly.Instance)) {
throw new Error("Go.run: WebAssembly.Instance expected");
}
this._inst = instance;
this.mem = new DataView(this._inst.exports.mem.buffer);
this._values = [ // JS values that Go currently has references to, indexed by reference id
NaN,
0,
null,
true,
false,
globalThis,
this,
];
this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
this._ids = new Map([ // mapping from JS values to reference ids
[0, 1],
[null, 2],
[true, 3],
[false, 4],
[globalThis, 5],
[this, 6],
]);
this._idPool = []; // unused ids that have been garbage collected
this.exited = false; // whether the Go program has exited
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
let offset = 4096;
const strPtr = (str) => {
const ptr = offset;
const bytes = encoder.encode(str + "\0");
new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
offset += bytes.length;
if (offset % 8 !== 0) {
offset += 8 - (offset % 8);
}
return ptr;
};
const argc = this.argv.length;
const argvPtrs = [];
this.argv.forEach((arg) => {
argvPtrs.push(strPtr(arg));
});
argvPtrs.push(0);
const keys = Object.keys(this.env).sort();
keys.forEach((key) => {
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
});
argvPtrs.push(0);
const argv = offset;
argvPtrs.forEach((ptr) => {
this.mem.setUint32(offset, ptr, true);
this.mem.setUint32(offset + 4, 0, true);
offset += 8;
});
// The linker guarantees global data starts from at least wasmMinDataAddr.
// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
const wasmMinDataAddr = 4096 + 8192;
if (offset >= wasmMinDataAddr) {
throw new Error("total length of command line and environment variables exceeds limit");
}
this._inst.exports.run(argc, argv);
if (this.exited) {
this._resolveExitPromise();
}
await this._exitPromise;
}
_resume() {
if (this.exited) {
throw new Error("Go program has already exited");
}
this._inst.exports.resume();
if (this.exited) {
this._resolveExitPromise();
}
}
_makeFuncWrapper(id) {
const go = this;
return function () {
const event = { id: id, this: this, args: arguments };
go._pendingEvent = event;
go._resume();
return event.result;
};
}
}
})();

View File

@ -22,8 +22,8 @@
<script defer src="dependencies/highlight.js"></script>
<script defer src="dependencies/marked.js"></script>
<script defer src="wasm_exec.js"></script>
<script>
<!--script defer src="wasm_exec.js"></script>
<script>
window.addEventListener('load', function() {
hljs.highlightAll();
@ -32,7 +32,7 @@
go.run(result.instance);
});
});
</script>
</script -->
</head>
<body>
@ -40,6 +40,19 @@
{{ embed }}
<script>
const companyTempMinMaxValues = {
'openai': { min: 0, max: 2 },
'anthropic': { min: 0, max: 1 },
'deepseek': { min: 0, max: 2 },
'fireworks': { min: 0, max: 2 },
'google': { min: 0, max: 2 },
'groq': { min: 0, max: 2 },
'mistral': { min: 0, max: 2 },
'nim': { min: 0, max: 2 },
'perplexity': { min: 0, max: 2 },
'together': { min: 0, max: 2 },
};
function copyToClipboardCode(button) {
// Get the code element next to the button
var codeElement = button.parentElement.nextElementSibling;

View File

@ -38,6 +38,7 @@
<script>
var textareaControl = document.getElementById('textarea-control');
const textarea = document.getElementById('chat-input-textarea');
// Every 0.01s check if the text area have htmx-request class, if yes, add the class is-loading
setInterval(function () {
@ -49,7 +50,10 @@
} else {
textareaControl.classList.remove('is-loading');
}
}, 10);
if (document.getElementById('chat-input-textarea') != null ) {
toggleSendButton();
}
}, 100);
function updateIcons() {
if (window.innerWidth < 450) {
@ -89,7 +93,6 @@
}
});
const textarea = document.querySelector('#chat-input-textarea');
textarea.addEventListener('keydown', handleTextareaKeydown);
document.addEventListener('htmx:afterSwap', toggleSendButton)
@ -107,6 +110,64 @@
}
}
function generatePlaceholder() {
return `
<div class="message-user mt-3 message-placeholder wasm-placeholder">
<div class="columns is-mobile">
<div class="column is-narrow" id="icon-column">
<figure class="image is-48x48 message-icon" style="flex-shrink: 0;">
<img src="icons/bouvai2.png" alt="User Image">
</figure>
</div>
<div class="column" id="content-column" style="width: 100px;">
<div class="is-flex is-align-items-start">
<div class="message-content" style="width: 100%%; overflow-y: hidden;">
<div class="message-header">
<p>You</p>
</div>
<div class="message-body">
<div class="content" style="overflow-x: auto; width: 100%%;">
<p>Rendering...</p>
</div>
</div>
</div>
</div>
<div class="is-flex is-justify-content mt-2">
<div style="height: 30px;"></div>
</div>
</div>
</div>
</div>
<div class="message-bot mt-3 message-placeholder">
<div class="columns is-mobile">
<div class="column is-narrow" id="icon-column">
<figure class="image is-48x48 message-icon" style="flex-shrink: 0;">
<img src="icons/bouvai2.png" alt="User Image">
</figure>
</div>
<div class="column" id="content-column" style="width: 100px;">
<div class="is-flex is-align-items-start">
<div class="message-content">
<div class='message-header'>
<p>Waiting...</p>
</div>
<div class="message-body">
<div class="content">
<br>
<img src="/puff.svg" />
</div>
</div>
</div>
</div>
<div class="is-flex is-justify-content mt-2">
<div style="height: 30px;"></div>
</div>
</div>
</div>
</div>
`
}
function onClickSendButton() {
// TODO: Add the message placeholder using WASM
messagesPlaceholderHTML = generatePlaceholder(textarea.value);

View File

@ -44,7 +44,7 @@
</div>
{% endif %}
<div class="message-body">
<div class="content" style="overflow-x: auto; width: 100%;">{{ message.Content | safe }}</div>
<div class="content" style="overflow: hidden; width: 100%;">{{ message.Content | safe }}</div>
</div>
{% endif %}
{% endfor %}

View File

@ -14,7 +14,7 @@
</div>
<div class="message-body">
<div class="content"
style="overflow-x: auto;
style="overflow: hidden;
width: 100%"
id="content-{{ ID }}">{{ Content | safe }}</div>
</div>

View File

@ -64,7 +64,7 @@
placeholder="Model name"
autocomplete="off">
<div class="select is-fullwidth is-small mb-3" id="model-id-input">
<select name="selectedLLMId">
<select name="selectedLLMId" id="modelID-select">
{% for modelInfo in ModelInfos %}
<option value="{{ modelInfo.ModelID }}">
{{ modelInfo.Company.Name }} - {{ modelInfo.Name }}
@ -100,7 +100,7 @@
<input class="slider is-small mb-3"
step="0.05"
min="0"
max="2"
max="1"
value="0"
type="range"
id="temperature-slider"
@ -161,6 +161,25 @@
}
});
document.getElementById('modelID-select').addEventListener('change', handleSelectChange)
function handleSelectChange() {
const selectElement = document.getElementById('modelID-select');
const selectedOption = selectElement.options[selectElement.selectedIndex];
const selectedText = selectedOption.innerHTML;
if (selectedText != "Custom Endpoints") {
const companyName = selectedText.split(' - ')[0].trim();
var companyMinMax = companyTempMinMaxValues[companyName];
} else {
var companyMinMax = companyTempMinMaxValues['openai'];
}
var tempSlider = document.getElementById('temperature-slider');
tempSlider.setAttribute("min", companyMinMax.min.toString());
tempSlider.setAttribute("max", companyMinMax.max.toString());
}
document.getElementById('model-name-input').addEventListener('input', function () {
document.getElementById('confirm-create-model-button').disabled = this.value === '' || !document.getElementById('endpoint-error').classList.contains('is-hidden');
})