Cqn enter key
This commit is contained in:
parent
ed64fc01d8
commit
d2556a4a6d
59
Chat.go
59
Chat.go
@ -18,14 +18,14 @@ func ChatPageHandler(c *fiber.Ctx) error {
|
|||||||
edgeClient = edgeClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": authCookie})
|
edgeClient = edgeClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": authCookie})
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Current User: ", getCurrentUser())
|
fmt.Println("Current User: ", getCurrentUser(), " - ", checkIfLogin())
|
||||||
|
|
||||||
return c.Render("chat", fiber.Map{"IsLogin": checkIfLogin()}, "layouts/main")
|
return c.Render("chat", fiber.Map{"IsLogin": checkIfLogin(), "HaveKey": checkIfHaveKey()}, "layouts/main")
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadModelSelectionHandler(c *fiber.Ctx) error {
|
func LoadModelSelectionHandler(c *fiber.Ctx) error {
|
||||||
CheckedModels := []string{"gpt-3.5-turbo"} // Default model
|
CheckedModels := []string{"gpt-3.5-turbo"} // Default model
|
||||||
out, err := pongo2.Must(pongo2.FromFile("views/partials/modelsPopover.html")).Execute(pongo2.Context{
|
out, err := pongo2.Must(pongo2.FromFile("views/partials/popover-models.html")).Execute(pongo2.Context{
|
||||||
"CompanyInfos": CompanyInfos,
|
"CompanyInfos": CompanyInfos,
|
||||||
"CheckedModels": CheckedModels,
|
"CheckedModels": CheckedModels,
|
||||||
})
|
})
|
||||||
@ -53,7 +53,7 @@ func LoadUsageKPIHandler(c *fiber.Ctx) error {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := pongo2.Must(pongo2.FromFile("views/partials/usagePopover.html")).Execute(pongo2.Context{
|
out, err := pongo2.Must(pongo2.FromFile("views/partials/popover-usage.html")).Execute(pongo2.Context{
|
||||||
"TotalUsage": TotalUsage,
|
"TotalUsage": TotalUsage,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -64,6 +64,16 @@ func LoadUsageKPIHandler(c *fiber.Ctx) error {
|
|||||||
return c.SendString(out)
|
return c.SendString(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LoadSettingsHandler(c *fiber.Ctx) error {
|
||||||
|
out, err := pongo2.Must(pongo2.FromFile("views/partials/popover-settings.html")).Execute(pongo2.Context{"IsLogin": checkIfLogin()})
|
||||||
|
if err != nil {
|
||||||
|
c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"error": "Error rendering template",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return c.SendString(out)
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteMessageHandler(c *fiber.Ctx) error {
|
func DeleteMessageHandler(c *fiber.Ctx) error {
|
||||||
messageId := c.FormValue("id")
|
messageId := c.FormValue("id")
|
||||||
|
|
||||||
@ -92,6 +102,9 @@ func DeleteMessageHandler(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
func LoadChatHandler(c *fiber.Ctx) error {
|
func LoadChatHandler(c *fiber.Ctx) error {
|
||||||
if checkIfLogin() {
|
if checkIfLogin() {
|
||||||
|
if getCurrentUserKeys() == nil {
|
||||||
|
return c.SendString(generateEnterKeyChatHTML())
|
||||||
|
}
|
||||||
return c.SendString(generateChatHTML())
|
return c.SendString(generateChatHTML())
|
||||||
} else {
|
} else {
|
||||||
return c.SendString(generateWelcomeChatHTML())
|
return c.SendString(generateWelcomeChatHTML())
|
||||||
@ -210,19 +223,53 @@ func GetMessageContentHandler(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateWelcomeChatHTML() string {
|
func generateWelcomeChatHTML() string {
|
||||||
|
welcomeMessage := `To start using JADE, please login.`
|
||||||
|
|
||||||
|
loginButton := `
|
||||||
|
<a class="button is-primary is-small" href="/signin">
|
||||||
|
Log in
|
||||||
|
</a>`
|
||||||
|
|
||||||
htmlString := "<div class='columns is-centered' id='chat-container'><div class='column is-12-mobile is-8-tablet is-6-desktop' id='chat-messages'>"
|
htmlString := "<div class='columns is-centered' id='chat-container'><div class='column is-12-mobile is-8-tablet is-6-desktop' id='chat-messages'>"
|
||||||
|
|
||||||
NextMessages := []NextMessage{}
|
NextMessages := []NextMessage{}
|
||||||
nextMsg := NextMessage{
|
nextMsg := NextMessage{
|
||||||
Icon: "bouvai2", // Assuming Icon is a field you want to include from Message
|
Icon: "bouvai2", // Assuming Icon is a field you want to include from Message
|
||||||
Content: markdownToHTML("Hi, I'm Bouvai. How can I help you today?"),
|
Content: "<br>" + markdownToHTML(welcomeMessage) + loginButton,
|
||||||
Hidden: false, // Assuming Hidden is a field you want to include from Message
|
Hidden: false, // Assuming Hidden is a field you want to include from Message
|
||||||
Id: "0",
|
Id: "0",
|
||||||
Name: "JADE",
|
Name: "JADE",
|
||||||
}
|
}
|
||||||
NextMessages = append(NextMessages, nextMsg)
|
NextMessages = append(NextMessages, nextMsg)
|
||||||
|
|
||||||
botOut, err := botTmpl.Execute(pongo2.Context{"Messages": NextMessages, "ConversationAreaId": 0})
|
botOut, err := botTmpl.Execute(pongo2.Context{"Messages": NextMessages, "ConversationAreaId": 0, "NotClickable": !true})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
htmlString += botOut
|
||||||
|
htmlString += "<div style='height: 10px;'></div>"
|
||||||
|
htmlString += "</div></div>"
|
||||||
|
|
||||||
|
// Render the HTML template with the messages
|
||||||
|
return htmlString
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEnterKeyChatHTML() string {
|
||||||
|
welcomeMessage := `To start using JADE, please enter at least one key in the settings.`
|
||||||
|
|
||||||
|
htmlString := "<div class='columns is-centered' id='chat-container'><div class='column is-12-mobile is-8-tablet is-6-desktop' id='chat-messages'>"
|
||||||
|
|
||||||
|
NextMessages := []NextMessage{}
|
||||||
|
nextMsg := NextMessage{
|
||||||
|
Icon: "bouvai2", // Assuming Icon is a field you want to include from Message
|
||||||
|
Content: "<br>" + markdownToHTML(welcomeMessage),
|
||||||
|
Hidden: false, // Assuming Hidden is a field you want to include from Message
|
||||||
|
Id: "0",
|
||||||
|
Name: "JADE",
|
||||||
|
}
|
||||||
|
NextMessages = append(NextMessages, nextMsg)
|
||||||
|
|
||||||
|
botOut, err := botTmpl.Execute(pongo2.Context{"Messages": NextMessages, "ConversationAreaId": 0, "NotClickable": !true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/edgedb/edgedb-go"
|
"github.com/edgedb/edgedb-go"
|
||||||
|
"github.com/flosch/pongo2"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AnthropicChatCompletionRequest struct {
|
type AnthropicChatCompletionRequest struct {
|
||||||
@ -124,8 +126,69 @@ func EdgeMessages2AnthropicMessages(messages []Message) []AnthropicMessage {
|
|||||||
return AnthropicMessages
|
return AnthropicMessages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAnthropicKey(apiKey string) bool {
|
||||||
|
url := "https://api.anthropic.com/v1/messages"
|
||||||
|
|
||||||
|
AnthropicMessages := []AnthropicMessage{
|
||||||
|
{
|
||||||
|
Role: "user",
|
||||||
|
Content: "Hello",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
requestBody := AnthropicChatCompletionRequest{
|
||||||
|
Model: "claude-3-haiku-20240307",
|
||||||
|
Messages: AnthropicMessages,
|
||||||
|
MaxTokens: 10,
|
||||||
|
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("anthropic-version", "2023-06-01")
|
||||||
|
req.Header.Set("x-api-key", 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 AnthropicChatCompletionResponse
|
||||||
|
err = json.Unmarshal(body, &chatCompletionResponse)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
func RequestAnthropic(model string, messages []Message, maxTokens int, temperature float64) (AnthropicChatCompletionResponse, error) {
|
func RequestAnthropic(model string, messages []Message, maxTokens int, temperature float64) (AnthropicChatCompletionResponse, error) {
|
||||||
apiKey := "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" // TODO Use env variable
|
var apiKey string
|
||||||
|
err := edgeClient.QuerySingle(edgeCtx, `
|
||||||
|
with
|
||||||
|
filtered_keys := (
|
||||||
|
select Key {
|
||||||
|
key
|
||||||
|
} filter .company = <str>$0
|
||||||
|
)
|
||||||
|
select filtered_keys.key limit 1
|
||||||
|
`, &apiKey, "anthropic")
|
||||||
|
if err != nil {
|
||||||
|
return AnthropicChatCompletionResponse{}, fmt.Errorf("error getting OpenAI API key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
url := "https://api.anthropic.com/v1/messages"
|
url := "https://api.anthropic.com/v1/messages"
|
||||||
|
|
||||||
AnthropicMessages := EdgeMessages2AnthropicMessages(messages)
|
AnthropicMessages := EdgeMessages2AnthropicMessages(messages)
|
||||||
@ -181,3 +244,74 @@ func RequestAnthropic(model string, messages []Message, maxTokens int, temperatu
|
|||||||
|
|
||||||
return chatCompletionResponse, nil
|
return chatCompletionResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addAnthropicKey(c *fiber.Ctx) error {
|
||||||
|
key := c.FormValue("key")
|
||||||
|
|
||||||
|
// Check if the key already exists
|
||||||
|
err := edgeClient.QuerySingle(edgeCtx, `
|
||||||
|
with
|
||||||
|
filtered_keys := (
|
||||||
|
select Key {
|
||||||
|
key
|
||||||
|
} filter .key = <str>$0 and .company = "anthropic"
|
||||||
|
)
|
||||||
|
select filtered_keys.key limit 1
|
||||||
|
`, &key, key)
|
||||||
|
if err == nil {
|
||||||
|
return c.SendString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !TestAnthropicKey(key) {
|
||||||
|
fmt.Println("Invalid Anthropic API Key")
|
||||||
|
|
||||||
|
NextMessages := []NextMessage{}
|
||||||
|
nextMsg := NextMessage{
|
||||||
|
Icon: "bouvai2", // Assuming Icon is a field you want to include from Message
|
||||||
|
Content: "<br>" + markdownToHTML("Invalid Anthropic API Key"),
|
||||||
|
Hidden: false, // Assuming Hidden is a field you want to include from Message
|
||||||
|
Id: "0",
|
||||||
|
Name: "JADE",
|
||||||
|
}
|
||||||
|
NextMessages = append(NextMessages, nextMsg)
|
||||||
|
|
||||||
|
botOut, err := botTmpl.Execute(pongo2.Context{"Messages": NextMessages, "ConversationAreaId": 0})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c.SendString(botOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = edgeClient.Execute(edgeCtx, `
|
||||||
|
UPDATE global currentUser.setting
|
||||||
|
SET {
|
||||||
|
keys += (
|
||||||
|
INSERT Key {
|
||||||
|
company := <str>$0,
|
||||||
|
key := <str>$1,
|
||||||
|
name := <str>$2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}`, "anthropic", key, "Anthropic API Key")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error in edgedb.QuerySingle: in addOpenaiKey")
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
NextMessages := []NextMessage{}
|
||||||
|
nextMsg := NextMessage{
|
||||||
|
Icon: "bouvai2", // Assuming Icon is a field you want to include from Message
|
||||||
|
Content: "<br>" + markdownToHTML("Key added successfully!"),
|
||||||
|
Hidden: false, // Assuming Hidden is a field you want to include from Message
|
||||||
|
Id: "0",
|
||||||
|
Name: "JADE",
|
||||||
|
}
|
||||||
|
NextMessages = append(NextMessages, nextMsg)
|
||||||
|
|
||||||
|
botOut, err := botTmpl.Execute(pongo2.Context{"Messages": NextMessages, "ConversationAreaId": 0})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendString(botOut)
|
||||||
|
}
|
||||||
|
172
RequestOpenai.go
172
RequestOpenai.go
@ -8,6 +8,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/edgedb/edgedb-go"
|
"github.com/edgedb/edgedb-go"
|
||||||
|
"github.com/flosch/pongo2"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OpenaiChatCompletionRequest struct {
|
type OpenaiChatCompletionRequest struct {
|
||||||
@ -115,8 +117,76 @@ func EdgeMessages2OpenaiMessages(messages []Message) []OpenaiMessage {
|
|||||||
return openaiMessages
|
return openaiMessages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenaiKey(apiKey string) bool {
|
||||||
|
url := "https://api.openai.com/v1/chat/completions"
|
||||||
|
|
||||||
|
// Convert messages to OpenAI format
|
||||||
|
openaiMessages := []OpenaiMessage{
|
||||||
|
{
|
||||||
|
Role: "user",
|
||||||
|
Content: "Hello",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
requestBody := OpenaiChatCompletionRequest{
|
||||||
|
Model: "gpt-3.5-turbo",
|
||||||
|
Messages: openaiMessages,
|
||||||
|
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 OpenaiChatCompletionResponse
|
||||||
|
err = json.Unmarshal(body, &chatCompletionResponse)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if chatCompletionResponse.Choices == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func RequestOpenai(model string, messages []Message, temperature float64) (OpenaiChatCompletionResponse, error) {
|
func RequestOpenai(model string, messages []Message, temperature float64) (OpenaiChatCompletionResponse, error) {
|
||||||
apiKey := "sk-proj-f7StCvXCtcmiOOayiVmgT3BlbkFJlVtAcOo3JcrnGq1cPa5o" // TODO Use env variable
|
var apiKey string
|
||||||
|
err := edgeClient.QuerySingle(edgeCtx, `
|
||||||
|
with
|
||||||
|
filtered_keys := (
|
||||||
|
select Key {
|
||||||
|
key
|
||||||
|
} filter .company = <str>$0
|
||||||
|
)
|
||||||
|
select filtered_keys.key limit 1
|
||||||
|
`, &apiKey, "openai")
|
||||||
|
if err != nil {
|
||||||
|
return OpenaiChatCompletionResponse{}, fmt.Errorf("error getting OpenAI API key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("OpenAI API key: ", apiKey)
|
||||||
|
|
||||||
url := "https://api.openai.com/v1/chat/completions"
|
url := "https://api.openai.com/v1/chat/completions"
|
||||||
|
|
||||||
// Convert messages to OpenAI format
|
// Convert messages to OpenAI format
|
||||||
@ -171,3 +241,103 @@ func RequestOpenai(model string, messages []Message, temperature float64) (Opena
|
|||||||
|
|
||||||
return chatCompletionResponse, nil
|
return chatCompletionResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addOpenaiKey(c *fiber.Ctx) error {
|
||||||
|
key := c.FormValue("key")
|
||||||
|
|
||||||
|
// Check if the key already exists
|
||||||
|
var keyUUID edgedb.UUID
|
||||||
|
err := edgeClient.QuerySingle(edgeCtx, `
|
||||||
|
with
|
||||||
|
filtered_keys := (
|
||||||
|
select Key {
|
||||||
|
id
|
||||||
|
} filter .key = <str>$0 and .company = "openai"
|
||||||
|
)
|
||||||
|
select filtered_keys.key limit 1
|
||||||
|
`, &keyUUID, key)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Println("Error in edgedb.Query: in addOpenaiKey")
|
||||||
|
return c.SendString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !TestOpenaiKey(key) {
|
||||||
|
fmt.Println("Invalid OpenAI API Key")
|
||||||
|
NextMessages := []NextMessage{}
|
||||||
|
nextMsg := NextMessage{
|
||||||
|
Icon: "bouvai2", // Assuming Icon is a field you want to include from Message
|
||||||
|
Content: "<br>" + markdownToHTML("Invalid OpenAI API Key"),
|
||||||
|
Hidden: false, // Assuming Hidden is a field you want to include from Message
|
||||||
|
Id: "0",
|
||||||
|
Name: "JADE",
|
||||||
|
}
|
||||||
|
NextMessages = append(NextMessages, nextMsg)
|
||||||
|
|
||||||
|
botOut, err := botTmpl.Execute(pongo2.Context{"Messages": NextMessages, "ConversationAreaId": 0})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c.SendString(botOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the company key already exists
|
||||||
|
err = edgeClient.QuerySingle(edgeCtx, `
|
||||||
|
with
|
||||||
|
filtered_keys := (
|
||||||
|
select Key {
|
||||||
|
id
|
||||||
|
} filter .company = "openai"
|
||||||
|
)
|
||||||
|
select filtered_keys.key limit 1
|
||||||
|
`, &keyUUID, key)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Company key already exists")
|
||||||
|
err = edgeClient.Execute(edgeCtx, `
|
||||||
|
UPDATE Key filter .company = <str>$0 AND .key = <str>$1
|
||||||
|
SET {
|
||||||
|
key := <str>$1,
|
||||||
|
}
|
||||||
|
`, "openai", key)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error in edgedb.QuerySingle: in addOpenaiKey")
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("OpenAI API key: ", key)
|
||||||
|
|
||||||
|
err = edgeClient.Execute(edgeCtx, `
|
||||||
|
UPDATE global currentUser.setting
|
||||||
|
SET {
|
||||||
|
keys += (
|
||||||
|
INSERT Key {
|
||||||
|
company := <str>$0,
|
||||||
|
key := <str>$1,
|
||||||
|
name := <str>$2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}`, "openai", key, "OpenAI API Key")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error in edgedb.QuerySingle: in addOpenaiKey")
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
NextMessages := []NextMessage{}
|
||||||
|
nextMsg := NextMessage{
|
||||||
|
Icon: "bouvai2", // Assuming Icon is a field you want to include from Message
|
||||||
|
Content: "<br> Key added successfully!",
|
||||||
|
Hidden: false, // Assuming Hidden is a field you want to include from Message
|
||||||
|
Id: "0",
|
||||||
|
Name: "JADE",
|
||||||
|
}
|
||||||
|
NextMessages = append(NextMessages, nextMsg)
|
||||||
|
|
||||||
|
botOut, err := botTmpl.Execute(pongo2.Context{"Messages": NextMessages, "ConversationAreaId": 0})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendString(botOut)
|
||||||
|
}
|
||||||
|
85
database.go
85
database.go
@ -13,19 +13,16 @@ var edgeCtx context.Context
|
|||||||
var edgeClient *edgedb.Client
|
var edgeClient *edgedb.Client
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID edgedb.UUID `edgedb:"id"`
|
ID edgedb.UUID `edgedb:"id"`
|
||||||
Email string `edgedb:"email"`
|
Setting Setting `edgedb:"setting"`
|
||||||
Name string `edgedb:"name"`
|
|
||||||
Setting Setting `edgedb:"setting"`
|
|
||||||
Conversations []Conversation `edgedb:"conversations"`
|
|
||||||
Usages []Usage `edgedb:"usages"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Key struct {
|
type Key struct {
|
||||||
ID edgedb.UUID `edgedb:"id"`
|
ID edgedb.UUID `edgedb:"id"`
|
||||||
Name string `edgedb:"name"`
|
Name string `edgedb:"name"`
|
||||||
Key string `edgedb:"key"`
|
Company string `edgedb:"company"`
|
||||||
Date time.Time `edgedb:"date"`
|
Key string `edgedb:"key"`
|
||||||
|
Date time.Time `edgedb:"date"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Setting struct {
|
type Setting struct {
|
||||||
@ -35,24 +32,27 @@ type Setting struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Conversation struct {
|
type Conversation struct {
|
||||||
ID edgedb.UUID `edgedb:"id"`
|
ID edgedb.UUID `edgedb:"id"`
|
||||||
Name string `edgedb:"name"`
|
Name string `edgedb:"name"`
|
||||||
Areas []Area `edgedb:"areas"`
|
Date time.Time `edgedb:"date"`
|
||||||
|
User User `edgedb:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Area struct {
|
type Area struct {
|
||||||
ID edgedb.UUID `edgedb:"id"`
|
ID edgedb.UUID `edgedb:"id"`
|
||||||
Position int `edgedb:"position"`
|
Position int `edgedb:"position"`
|
||||||
Messages []Message `edgedb:"messages"`
|
Conv Conversation `edgedb:"conversation"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
ID edgedb.UUID `edgedb:"id"`
|
ID edgedb.UUID `edgedb:"id"`
|
||||||
|
Content string `edgedb:"content"`
|
||||||
|
Role string `edgedb:"role"`
|
||||||
ModelID edgedb.OptionalStr `edgedb:"model_id"`
|
ModelID edgedb.OptionalStr `edgedb:"model_id"`
|
||||||
Selected edgedb.OptionalBool `edgedb:"selected"`
|
Selected edgedb.OptionalBool `edgedb:"selected"`
|
||||||
Role string `edgedb:"role"`
|
|
||||||
Content string `edgedb:"content"`
|
|
||||||
Date time.Time `edgedb:"date"`
|
Date time.Time `edgedb:"date"`
|
||||||
|
Area Area `edgedb:"area"`
|
||||||
|
Conv Conversation `edgedb:"conversation"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Usage struct {
|
type Usage struct {
|
||||||
@ -109,10 +109,34 @@ func checkIfLogin() bool {
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertArea() edgedb.UUID {
|
func insertNewConversation() edgedb.UUID {
|
||||||
// Insert a new area.
|
|
||||||
var inserted struct{ id edgedb.UUID }
|
var inserted struct{ id edgedb.UUID }
|
||||||
err := edgeClient.QuerySingle(edgeCtx, `
|
err := edgeClient.QuerySingle(edgeCtx, `
|
||||||
|
INSERT Conversation {
|
||||||
|
name := 'Default',
|
||||||
|
user := global currentUser
|
||||||
|
}
|
||||||
|
`, &inserted)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error in edgedb.QuerySingle: in insertNewConversation")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return inserted.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertArea() edgedb.UUID {
|
||||||
|
// If the Default conversation doesn't exist, create it.
|
||||||
|
err := edgeClient.QuerySingle(edgeCtx, `
|
||||||
|
SELECT Conversation
|
||||||
|
FILTER .name = 'Default' AND .user = global currentUser
|
||||||
|
LIMIT 1
|
||||||
|
`, nil)
|
||||||
|
if err != nil {
|
||||||
|
insertNewConversation()
|
||||||
|
}
|
||||||
|
// Insert a new area.
|
||||||
|
var inserted struct{ id edgedb.UUID }
|
||||||
|
err = edgeClient.QuerySingle(edgeCtx, `
|
||||||
WITH
|
WITH
|
||||||
positionVar := count((SELECT Area FILTER .conversation.name = 'Default' AND .conversation.user = global currentUser)) + 1
|
positionVar := count((SELECT Area FILTER .conversation.name = 'Default' AND .conversation.user = global currentUser)) + 1
|
||||||
INSERT Area {
|
INSERT Area {
|
||||||
@ -168,10 +192,10 @@ func insertBotMessage(content string, selected bool, model string) edgedb.UUID {
|
|||||||
content := <str>$2,
|
content := <str>$2,
|
||||||
selected := <bool>$3,
|
selected := <bool>$3,
|
||||||
conversation := (
|
conversation := (
|
||||||
SELECT Conversation
|
SELECT Area
|
||||||
FILTER .name = 'Default' AND .user = global currentUser
|
FILTER .id = <uuid>$4
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
),
|
).conversation,
|
||||||
area := (
|
area := (
|
||||||
SELECT Area
|
SELECT Area
|
||||||
FILTER .id = <uuid>$4
|
FILTER .id = <uuid>$4
|
||||||
@ -212,3 +236,18 @@ func getAllMessages() []Message {
|
|||||||
|
|
||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCurrentUserKeys() []Key {
|
||||||
|
var result []Key
|
||||||
|
err := edgeClient.Query(edgeCtx, "SELECT global currentUser.setting.keys", &result)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error in edgedb.Query: in getCurrentUserKeys")
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIfHaveKey() bool {
|
||||||
|
keys := getCurrentUserKeys()
|
||||||
|
return keys != nil && len(keys) > 0
|
||||||
|
}
|
||||||
|
5
main.go
5
main.go
@ -40,9 +40,14 @@ func main() {
|
|||||||
app.Get("/generateMultipleMessages", GenerateMultipleMessages)
|
app.Get("/generateMultipleMessages", GenerateMultipleMessages)
|
||||||
app.Get("/messageContent", GetMessageContentHandler)
|
app.Get("/messageContent", GetMessageContentHandler)
|
||||||
|
|
||||||
|
// Settings routes
|
||||||
|
app.Post("/addOpenaiKey", addOpenaiKey)
|
||||||
|
app.Post("/addAnthropicKey", addAnthropicKey)
|
||||||
|
|
||||||
// Popovers
|
// Popovers
|
||||||
app.Get("/loadModelSelection", LoadModelSelectionHandler)
|
app.Get("/loadModelSelection", LoadModelSelectionHandler)
|
||||||
app.Get("/loadUsageKPI", LoadUsageKPIHandler)
|
app.Get("/loadUsageKPI", LoadUsageKPIHandler)
|
||||||
|
app.Get("/loadSettings", LoadSettingsHandler)
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
app.Get("/signin", handleUiSignIn)
|
app.Get("/signin", handleUiSignIn)
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
<div class="chat-container mt-5">
|
<div class="chat-container mt-5">
|
||||||
|
|
||||||
<hx hx-get="/loadChat" hx-trigger="load" hx-swap="outerHTML"></hx>
|
<hx hx-get="/loadChat" hx-trigger="load once" hx-swap="outerHTML"></hx>
|
||||||
|
|
||||||
|
|
||||||
<div class="chat-input-container mb-5">
|
<div class="chat-input-container mb-5">
|
||||||
<div class="textarea-wrapper">
|
<div class="textarea-wrapper">
|
||||||
<textarea class="textarea" placeholder="Type your message here..." name="message"></textarea>
|
<textarea {% if not IsLogin or not HaveKey %}disabled{% endif %} class="textarea"
|
||||||
|
placeholder="Type your message here..." name="message"></textarea>
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<hx hx-get="/loadModelSelection" hx-trigger="load" hx-swap="outerHTML" hx-target="this"></hx>
|
<hx hx-get="/loadSettings" hx-trigger="load" hx-swap="outerHTML" hx-target="this"></hx>
|
||||||
<hx hx-get="/loadUsageKPI" hx-trigger="load" hx-swap="outerHTML" hx-target="this"></hx>
|
<hx hx-get="/loadUsageKPI" hx-trigger="load" hx-swap="outerHTML" hx-target="this"></hx>
|
||||||
<button type="submit" class="send-button button is-primary is-small" hx-post="/requestMultipleMessages"
|
<hx hx-get="/loadModelSelection" hx-trigger="load" hx-swap="outerHTML" hx-target="this"></hx>
|
||||||
|
<button {% if not IsLogin or not HaveKey %}disabled{% endif %} type="submit"
|
||||||
|
class="send-button button is-primary is-small" hx-post="/requestMultipleMessages"
|
||||||
hx-swap="beforeend settle:200ms" hx-target="#chat-messages" id="chat-input-send-btn"
|
hx-swap="beforeend settle:200ms" hx-target="#chat-messages" id="chat-input-send-btn"
|
||||||
class="chat-input" hx-include="[name='message'], [name^='model-check-']">Send</button>
|
class="chat-input" hx-include="[name='message'], [name^='model-check-']">Send</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
<div class='rows'>
|
<div class='rows'>
|
||||||
{% for message in Messages %}
|
{% for message in Messages %}
|
||||||
<div class='row is-full mt-1'>
|
<div class='row is-full mt-1'>
|
||||||
<a href="#" hx-get="/messageContent?id={{ message.Id }}" class="is-clickable"
|
<a {% if NotClickable %} href="#" hx-get="/messageContent?id={{ message.Id }}" class="is-clickable"
|
||||||
hx-target="#content-{{ ConversationAreaId }}" onclick="toggleGrayscale(this)">
|
onclick="toggleGrayscale(this)" hx-target="#content-{{ ConversationAreaId }}" {% endif %}>
|
||||||
<figure class="image is-48x48" style="flex-shrink: 0;">
|
<figure class="image is-48x48" style="flex-shrink: 0;">
|
||||||
<img src="icons/{{ message.Icon }}.png" alt="User Image" {% if message.Hidden %}
|
<img src="icons/{{ message.Icon }}.png" alt="User Image" {% if message.Hidden %}
|
||||||
style="filter: grayscale(100%);" {% endif %} title="{{ message.Name }}">
|
style="filter: grayscale(100%);" {% endif %} title="{{ message.Name }}">
|
||||||
|
@ -13,13 +13,9 @@
|
|||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
{% if IsLogin %}
|
{% if IsLogin %}
|
||||||
<a class="button is-light is-small" href="/signout">
|
<a class="button is-small is-info is-outlined" href="/signout">
|
||||||
Log out
|
Log out
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
|
||||||
<a class="button is-light is-small" href="/signin">
|
|
||||||
Log in
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
42
views/partials/popover-settings.html
Normal file
42
views/partials/popover-settings.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<div class="dropdown is-hoverable is-up">
|
||||||
|
<div class="dropdown-trigger">
|
||||||
|
<button class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu4">
|
||||||
|
<span>Settings</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu" id="dropdown-menu4" role="menu">
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<div class="dropdown-item">
|
||||||
|
<div class="field">
|
||||||
|
<form id="api-keys-form" method="post" action="/addKeys" hx-trigger="submit"
|
||||||
|
hx-target="#chat-messages" hx-swap="beforeend">
|
||||||
|
<div class="field has-addons">
|
||||||
|
<p class="control has-icons-left is-expanded">
|
||||||
|
<input class="input is-small" type="text" placeholder="OpenAI API key" name="openai_key"
|
||||||
|
autocomplete="off" {% if not IsLogin %}disabled{% endif %}>
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fas fa-lock"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<p class="control has-icons-left is-expanded">
|
||||||
|
<input class="input is-small" type="text" placeholder="Anthropic API key"
|
||||||
|
name="anthropic_key" autocomplete="off" {% if not IsLogin %}disabled{% endif %}>
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fas fa-lock"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<p class="control">
|
||||||
|
<button {% if not IsLogin %}disabled{% endif %} type="submit"
|
||||||
|
class="button is-small">Submit</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
x
Reference in New Issue
Block a user