Added together ai and DeepSeek
This commit is contained in:
parent
751e72dcd7
commit
6c3e4d4b8a
@ -1,3 +1,5 @@
|
||||
data
|
||||
wasm
|
||||
dbschema
|
||||
dbschema
|
||||
.git
|
||||
TODO.md
|
||||
|
21
Chat.go
21
Chat.go
@ -206,8 +206,6 @@ func GetMessageContentHandler(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func GenerateMessageContentHTML(authCookie string, messageId string, onlyContent string, withDiv bool) string {
|
||||
fmt.Println("Generating message for:", authCookie)
|
||||
|
||||
messageUUID, _ := edgedb.ParseUUID(messageId)
|
||||
|
||||
var selectedMessage Message
|
||||
@ -372,7 +370,7 @@ func generateEnterKeyChatHTML() string {
|
||||
<p>To get a key and learn more about the different LLM providers and their offerings, check out their websites:</p>
|
||||
<a
|
||||
class="button is-small is-primary is-outlined mt-1"
|
||||
href="https://openai.com/index/openai-api/"
|
||||
href="https://platform.openai.com/account/api-keys"
|
||||
target="_blank"
|
||||
>
|
||||
Get OpenAI API key
|
||||
@ -422,15 +420,22 @@ func generateEnterKeyChatHTML() string {
|
||||
>
|
||||
Get Fireworks API key
|
||||
</a>
|
||||
<a class="button is-small is-primary is-outlined mt-1"
|
||||
href="https://www.together.ai/"
|
||||
target="_blank">Get Together AI API key</a>
|
||||
<a class="button is-small is-primary is-outlined mt-1"
|
||||
href="https://platform.deepseek.com/"
|
||||
target="_blank">Get DeepSeek API key</a>
|
||||
<br/>
|
||||
<p>Note: Key are encrypted and saved on a secure database link only with the app.</p>`
|
||||
|
||||
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 := []TemplateMessage{}
|
||||
nextMsg := TemplateMessage{
|
||||
Icon: "icons/bouvai2.png", // Assuming Icon is a field you want to include from Message
|
||||
Icon: "icons/bouvai2.png",
|
||||
Content: welcomeMessage,
|
||||
Hidden: false, // Assuming Hidden is a field you want to include from Message
|
||||
Hidden: false,
|
||||
Id: "0",
|
||||
Name: "JADE",
|
||||
}
|
||||
@ -929,7 +934,7 @@ func LoadSettingsHandler(c *fiber.Ctx) error {
|
||||
|
||||
stripeSubLink := "https://billing.stripe.com/p/login/6oE6sc0PTfvq1Hi288?prefilled_email=" + user.Email
|
||||
|
||||
openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists, perplexityExists, fireworksExists, nimExists := getExistingKeys(c)
|
||||
openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists, perplexityExists, fireworksExists, nimExists, togetherExists, deepseekExists := getExistingKeysNew(c)
|
||||
isPremium, isBasic := IsCurrentUserSubscribed(c)
|
||||
|
||||
out, err := settingPopoverTmpl.Execute(pongo2.Context{
|
||||
@ -943,7 +948,9 @@ func LoadSettingsHandler(c *fiber.Ctx) error {
|
||||
"NimExists": nimExists,
|
||||
"PerplexityExists": perplexityExists,
|
||||
"FireworksExists": fireworksExists,
|
||||
"AnyExists": fireworksExists || openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists || perplexityExists || nimExists,
|
||||
"TogetherExists": togetherExists,
|
||||
"DeepseekExists": deepseekExists,
|
||||
"AnyExists": fireworksExists || openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists || perplexityExists || nimExists || togetherExists || deepseekExists,
|
||||
"isPremium": isPremium,
|
||||
"isBasic": isBasic,
|
||||
"StripeSubLink": stripeSubLink,
|
||||
|
164
MyUtils.go
164
MyUtils.go
@ -75,123 +75,73 @@ func addCodeHeader(htmlContent string, languages []string) string {
|
||||
return updatedHTML
|
||||
}
|
||||
|
||||
func getExistingKeys(c *fiber.Ctx) (bool, bool, bool, bool, bool, bool, bool, bool, bool) {
|
||||
func getExistingKeysNew(c *fiber.Ctx) (bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool) {
|
||||
// TODO: Optimize to use only one query
|
||||
if edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}) == nil {
|
||||
return false, false, false, false, false, false, false, false, false
|
||||
return false, false, false, false, false, false, false, false, false, false, false
|
||||
}
|
||||
|
||||
var (
|
||||
openaiExists bool
|
||||
anthropicExists bool
|
||||
mistralExists bool
|
||||
groqExists bool
|
||||
gooseaiExists bool
|
||||
nimExists bool
|
||||
googleExists bool
|
||||
perplexityExists bool
|
||||
fireworksExists bool
|
||||
)
|
||||
var userInfo User
|
||||
|
||||
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"
|
||||
);
|
||||
`, &openaiExists)
|
||||
SELECT global currentUser {
|
||||
setting: {
|
||||
keys: {
|
||||
name,
|
||||
key,
|
||||
company: {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LIMIT 1;
|
||||
`, &userInfo)
|
||||
if err != nil {
|
||||
fmt.Println("Error checking if OpenAI key exists")
|
||||
panic(err)
|
||||
fmt.Println("Error getting user keys", err)
|
||||
return false, false, false, false, false, false, false, false, false, false, false
|
||||
}
|
||||
|
||||
err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
|
||||
select exists (
|
||||
select global currentUser.setting.keys
|
||||
filter .company.name = "anthropic"
|
||||
);
|
||||
`, &anthropicExists)
|
||||
if err != nil {
|
||||
fmt.Println("Error checking if Anthropic key exists")
|
||||
panic(err)
|
||||
openaiExists := false
|
||||
anthropicExists := false
|
||||
mistralExists := false
|
||||
groqExists := false
|
||||
gooseaiExists := false
|
||||
nimExists := false
|
||||
googleExists := false
|
||||
perplexityExists := false
|
||||
fireworksExists := false
|
||||
togetherExists := false
|
||||
deepseekExists := false
|
||||
|
||||
for _, key := range userInfo.Setting.Keys {
|
||||
switch key.Company.Name {
|
||||
case "openai":
|
||||
openaiExists = true
|
||||
case "anthropic":
|
||||
anthropicExists = true
|
||||
case "mistral":
|
||||
mistralExists = true
|
||||
case "groq":
|
||||
groqExists = true
|
||||
case "gooseai":
|
||||
gooseaiExists = true
|
||||
case "nim":
|
||||
nimExists = true
|
||||
case "google":
|
||||
googleExists = true
|
||||
case "perplexity":
|
||||
perplexityExists = true
|
||||
case "fireworks":
|
||||
fireworksExists = true
|
||||
case "together":
|
||||
togetherExists = true
|
||||
case "deepseek":
|
||||
deepseekExists = true
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
);
|
||||
`, &mistralExists)
|
||||
if err != nil {
|
||||
fmt.Println("Error checking if Mistral key exists")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
|
||||
select exists (
|
||||
select global currentUser.setting.keys
|
||||
filter .company.name = "groq"
|
||||
);
|
||||
`, &groqExists)
|
||||
if err != nil {
|
||||
fmt.Println("Error checking if Groq key exists")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
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"
|
||||
);
|
||||
`, &gooseaiExists)
|
||||
if err != nil {
|
||||
fmt.Println("Error checking if GooseAI key exists")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
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"
|
||||
);
|
||||
`, &googleExists)
|
||||
if err != nil {
|
||||
fmt.Println("Error checking if Google key exists")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 = "fireworks"
|
||||
);
|
||||
`, &fireworksExists)
|
||||
if err != nil {
|
||||
fmt.Println("Error checking if Fireworks key exists")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
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 = "nim"
|
||||
);
|
||||
`, &nimExists)
|
||||
if err != nil {
|
||||
fmt.Println("Error checking if Fireworks key exists")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists, perplexityExists, fireworksExists, nimExists
|
||||
return openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists, perplexityExists, fireworksExists, nimExists, togetherExists, deepseekExists
|
||||
}
|
||||
|
||||
func Message2RequestMessage(messages []Message, context string) []RequestMessage {
|
||||
|
@ -158,6 +158,10 @@ func GenerateMultipleMessagesHandler(c *fiber.Ctx) error {
|
||||
addMessageFunc = addFireworkMessage
|
||||
case "nim":
|
||||
addMessageFunc = addNimMessage
|
||||
case "together":
|
||||
addMessageFunc = addTogetherMessage
|
||||
case "deepseek":
|
||||
addMessageFunc = addDeepseekMessage
|
||||
}
|
||||
|
||||
var messageID edgedb.UUID
|
||||
|
203
RequestDeepseek.go
Normal file
203
RequestDeepseek.go
Normal file
@ -0,0 +1,203 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/edgedb/edgedb-go"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type DeepseekChatCompletionRequest struct {
|
||||
Model string `json:"model"`
|
||||
Messages []RequestMessage `json:"messages"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
}
|
||||
|
||||
type DeepseekChatCompletionResponse struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Created int64 `json:"created"`
|
||||
Model string `json:"model"`
|
||||
Usage DeepseekUsage `json:"usage"`
|
||||
Choices []DeepseekChoice `json:"choices"`
|
||||
}
|
||||
|
||||
type DeepseekUsage struct {
|
||||
PromptTokens int32 `json:"prompt_tokens"`
|
||||
CompletionTokens int32 `json:"completion_tokens"`
|
||||
TotalTokens int32 `json:"total_tokens"`
|
||||
}
|
||||
|
||||
type DeepseekChoice struct {
|
||||
Message Message `json:"message"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
Index int `json:"index"`
|
||||
}
|
||||
|
||||
func addDeepseekMessage(c *fiber.Ctx, llm LLM, selected bool) edgedb.UUID {
|
||||
Messages := getAllSelectedMessages(c)
|
||||
|
||||
chatCompletion, err := RequestDeepseek(c, llm.Model.ModelID, Messages, float64(llm.Temperature), llm.Context, int(llm.MaxToken))
|
||||
if err != nil {
|
||||
fmt.Println("Error requesting Deepseek: ", err)
|
||||
id := insertBotMessage(c, "Error requesting DeepSeek, model may not be available anymore. Better error message in development.", selected, llm.ID)
|
||||
return id
|
||||
} else if len(chatCompletion.Choices) == 0 {
|
||||
fmt.Println("No response from DeepSeek")
|
||||
id := insertBotMessage(c, "No response from DeepSeek", selected, llm.ID)
|
||||
return id
|
||||
} else {
|
||||
Content := chatCompletion.Choices[0].Message.Content
|
||||
id := insertBotMessage(c, Content, selected, llm.ID)
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeepseekKey(apiKey string) bool {
|
||||
url := "https://api.deepseek.com/chat/completions"
|
||||
|
||||
// Convert messages to OpenAI format
|
||||
deepseekMessages := []RequestMessage{
|
||||
{
|
||||
Role: "user",
|
||||
Content: "Hello",
|
||||
},
|
||||
}
|
||||
|
||||
requestBody := DeepseekChatCompletionRequest{
|
||||
Model: "deepseek-chat",
|
||||
Messages: deepseekMessages,
|
||||
Temperature: 0,
|
||||
MaxTokens: 10,
|
||||
}
|
||||
|
||||
jsonBody, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to test Deepseek API key - json.Marshal :", err)
|
||||
return false
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody))
|
||||
if err != nil {
|
||||
fmt.Println("Failed to test Deepseek API key - http.NewRequest :", err)
|
||||
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 {
|
||||
fmt.Println("Failed to test Deepseek API key - client.Do :", err)
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
fmt.Println(resp.Status)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to test Deepseek API key - io.ReadAll :", err)
|
||||
return false
|
||||
}
|
||||
|
||||
var chatCompletionResponse DeepseekChatCompletionResponse
|
||||
err = json.Unmarshal(body, &chatCompletionResponse)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to test Deepseek API key - json.Marshal :", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if chatCompletionResponse.Usage.CompletionTokens == 0 {
|
||||
fmt.Println("Failed to test Deepseek API key - No completion tokens :", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func RequestDeepseek(c *fiber.Ctx, model string, messages []Message, temperature float64, context string, maxTokens int) (DeepseekChatCompletionResponse, 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, "deepseek")
|
||||
if err != nil {
|
||||
return DeepseekChatCompletionResponse{}, fmt.Errorf("error getting DeepSeek API key: %w", err)
|
||||
}
|
||||
|
||||
url := "https://api.deepseek.com/chat/completions"
|
||||
|
||||
requestBody := DeepseekChatCompletionRequest{
|
||||
Model: model,
|
||||
Messages: Message2RequestMessage(messages, context),
|
||||
MaxTokens: maxTokens,
|
||||
Temperature: temperature,
|
||||
}
|
||||
|
||||
jsonBody, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
return DeepseekChatCompletionResponse{}, fmt.Errorf("error marshaling JSON: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody))
|
||||
if err != nil {
|
||||
return DeepseekChatCompletionResponse{}, 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 DeepseekChatCompletionResponse{}, fmt.Errorf("error sending request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// TODO: Add a message to the user and do it for all 400 things
|
||||
if resp.Status == "402 Payment Required" {
|
||||
return DeepseekChatCompletionResponse{}, fmt.Errorf("error reading response body: %w", err)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return DeepseekChatCompletionResponse{}, fmt.Errorf("error reading response body: %w", err)
|
||||
}
|
||||
|
||||
var chatCompletionResponse DeepseekChatCompletionResponse
|
||||
err = json.Unmarshal(body, &chatCompletionResponse)
|
||||
if err != nil {
|
||||
return DeepseekChatCompletionResponse{}, 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 DeepseekChatCompletionResponse{}, 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
|
||||
}
|
188
RequestTogetherai.go
Normal file
188
RequestTogetherai.go
Normal file
@ -0,0 +1,188 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/edgedb/edgedb-go"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type TogetherChatCompletionRequest struct {
|
||||
Model string `json:"model"`
|
||||
Messages []RequestMessage `json:"messages"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
}
|
||||
|
||||
type TogetherChatCompletionResponse struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Created int64 `json:"created"`
|
||||
Model string `json:"model"`
|
||||
Usage TogetherUsage `json:"usage"`
|
||||
Choices []TogetherChoice `json:"choices"`
|
||||
}
|
||||
|
||||
type TogetherUsage struct {
|
||||
PromptTokens int32 `json:"prompt_tokens"`
|
||||
CompletionTokens int32 `json:"completion_tokens"`
|
||||
TotalTokens int32 `json:"total_tokens"`
|
||||
}
|
||||
|
||||
type TogetherChoice struct {
|
||||
Text string `json:"text"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
Index int `json:"index"`
|
||||
}
|
||||
|
||||
func addTogetherMessage(c *fiber.Ctx, llm LLM, selected bool) edgedb.UUID {
|
||||
Messages := getAllSelectedMessages(c)
|
||||
|
||||
chatCompletion, err := RequestTogether(c, llm.Model.ModelID, Messages, float64(llm.Temperature), llm.Context, int(llm.MaxToken))
|
||||
if err != nil {
|
||||
fmt.Println("Error requesting Together: ", err)
|
||||
id := insertBotMessage(c, "Error requesting Together, model may not be available anymore. Better error message in development.", selected, llm.ID)
|
||||
return id
|
||||
} else if len(chatCompletion.Choices) == 0 {
|
||||
fmt.Println("No response from Together")
|
||||
id := insertBotMessage(c, "No response from Together", selected, llm.ID)
|
||||
return id
|
||||
} else {
|
||||
Content := chatCompletion.Choices[0].Text
|
||||
id := insertBotMessage(c, Content, selected, llm.ID)
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
func TestTogetherKey(apiKey string) bool {
|
||||
url := "https://api.together.xyz/v1/completions"
|
||||
|
||||
// Convert messages to OpenAI format
|
||||
togetherMessages := []RequestMessage{
|
||||
{
|
||||
Role: "user",
|
||||
Content: "Hello",
|
||||
},
|
||||
}
|
||||
|
||||
requestBody := TogetherChatCompletionRequest{
|
||||
Model: "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
|
||||
Messages: togetherMessages,
|
||||
Temperature: 0,
|
||||
MaxTokens: 10,
|
||||
}
|
||||
|
||||
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 TogetherChatCompletionResponse
|
||||
err = json.Unmarshal(body, &chatCompletionResponse)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if chatCompletionResponse.Usage.CompletionTokens == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func RequestTogether(c *fiber.Ctx, model string, messages []Message, temperature float64, context string, maxTokens int) (TogetherChatCompletionResponse, 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, "together")
|
||||
if err != nil {
|
||||
return TogetherChatCompletionResponse{}, fmt.Errorf("error getting Together AI API key: %w", err)
|
||||
}
|
||||
|
||||
url := "https://api.together.xyz/v1/completions"
|
||||
|
||||
requestBody := TogetherChatCompletionRequest{
|
||||
Model: model,
|
||||
Messages: Message2RequestMessage(messages, context),
|
||||
MaxTokens: maxTokens,
|
||||
Temperature: temperature,
|
||||
}
|
||||
|
||||
jsonBody, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
return TogetherChatCompletionResponse{}, fmt.Errorf("error marshaling JSON: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody))
|
||||
if err != nil {
|
||||
return TogetherChatCompletionResponse{}, 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 TogetherChatCompletionResponse{}, fmt.Errorf("error sending request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return TogetherChatCompletionResponse{}, fmt.Errorf("error reading response body: %w", err)
|
||||
}
|
||||
|
||||
var chatCompletionResponse TogetherChatCompletionResponse
|
||||
err = json.Unmarshal(body, &chatCompletionResponse)
|
||||
if err != nil {
|
||||
return TogetherChatCompletionResponse{}, 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 TogetherChatCompletionResponse{}, 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
|
||||
}
|
18
TODO.md
18
TODO.md
@ -1,13 +1,27 @@
|
||||
# Bugs
|
||||
[ ] The SSE event that sometime fails after some times. Reconnect when sending a new message.
|
||||
[ ] 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
|
||||
|
||||
# Features
|
||||
[X] SSE to WebSocket
|
||||
[X] Add max tokens
|
||||
[ ] Errors messages to know what happend
|
||||
[ ] Add Deepseek API
|
||||
[ ] Check the response status and add a message accordingly (Like 402 Payment required)
|
||||
[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)
|
||||
|
||||
# Other
|
||||
[ ] Change the terms of service and enter keys page to an HTML
|
||||
[ ] Split Chat.go into smaller files
|
||||
[ ] Create a Request package
|
||||
|
@ -28,7 +28,7 @@ module default {
|
||||
|
||||
type Key {
|
||||
required name: str;
|
||||
company: Company;
|
||||
required company: Company;
|
||||
required key: str;
|
||||
required date: datetime {
|
||||
default := datetime_current();
|
||||
|
9
dbschema/migrations/00054-m1crb36.edgeql
Normal file
9
dbschema/migrations/00054-m1crb36.edgeql
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE MIGRATION m1crb36qoksqdmqtzncwo6b5gqb66jlgdfkziozqenrkgwowoj5vka
|
||||
ONTO m1eqooh5xjbysafocihnromqeyrleo57w6txsr36tu73rkju2eyfcq
|
||||
{
|
||||
ALTER TYPE default::Key {
|
||||
ALTER LINK company {
|
||||
SET REQUIRED USING (<default::Company>{});
|
||||
};
|
||||
};
|
||||
};
|
4
main.go
4
main.go
@ -138,6 +138,8 @@ func addKeys(c *fiber.Ctx) error {
|
||||
"nim": c.FormValue("nim_key"),
|
||||
"perplexity": c.FormValue("perplexity_key"),
|
||||
"fireworks": c.FormValue("fireworks_key"),
|
||||
"together": c.FormValue("together_key"),
|
||||
"deepseek": c.FormValue("deepseek_key"),
|
||||
}
|
||||
|
||||
testFunctions := map[string]func(string) bool{
|
||||
@ -150,6 +152,8 @@ func addKeys(c *fiber.Ctx) error {
|
||||
"nim": TestNimKey,
|
||||
"perplexity": TestPerplexityKey,
|
||||
"fireworks": TestFireworkKey,
|
||||
"together": TestTogetherKey,
|
||||
"deepseek": TestDeepseekKey,
|
||||
}
|
||||
|
||||
for company, key := range keys {
|
||||
|
BIN
static/icons/deepseek.png
Normal file
BIN
static/icons/deepseek.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
BIN
static/icons/together.png
Normal file
BIN
static/icons/together.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
@ -4,7 +4,7 @@
|
||||
the settings menu. Once enter you get access to all models from this provider.
|
||||
</p>
|
||||
<a class="button is-small is-primary is-outlined mt-1"
|
||||
href="https://openai.com/index/openai-api/"
|
||||
href="https://platform.openai.com/account/api-keys"
|
||||
target="_blank">Get OpenAI API key</a>
|
||||
<a class="button is-small is-primary is-outlined mt-1"
|
||||
href="https://console.anthropic.com/"
|
||||
@ -27,6 +27,12 @@
|
||||
<a class="button is-small is-primary is-outlined mt-1"
|
||||
href="https://fireworks.ai/login"
|
||||
target="_blank">Get Fireworks API key</a>
|
||||
<a class="button is-small is-primary is-outlined mt-1"
|
||||
href="https://www.together.ai/"
|
||||
target="_blank">Get Together AI API key</a>
|
||||
<a class="button is-small is-primary is-outlined mt-1"
|
||||
href="https://platform.deepseek.com/"
|
||||
target="_blank">Get DeepSeek API key</a>
|
||||
|
||||
<h2>Conversations</h2>
|
||||
<p>
|
||||
|
@ -115,6 +115,30 @@
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field has-addons is-hidden" id="together-field">
|
||||
<p class="control has-icons-left is-expanded">
|
||||
<input class="input is-small {% if TogetherExists %}is-success{% endif %}"
|
||||
type="text"
|
||||
placeholder="Together AI API key"
|
||||
name="together_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="deepseek-field">
|
||||
<p class="control has-icons-left is-expanded">
|
||||
<input class="input is-small {% if DeepseekExists %}is-success{% endif %}"
|
||||
type="text"
|
||||
placeholder="DeepSeek API key"
|
||||
name="deepseek_key"
|
||||
autocomplete="off">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-lock"></i>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field has-addons is-hidden"
|
||||
id="goose-field"
|
||||
title="GooseAI chat API will be available soon">
|
||||
|
@ -199,20 +199,25 @@
|
||||
<br />
|
||||
<br />
|
||||
<strong>Fireworks</strong> - Fireworks AI offer 1$ of free credits when
|
||||
creating an account. Firework AI have a lot of open source models. I may
|
||||
add fine tuned models in the future.
|
||||
creating an account. Firework AI have a lot of open source models.
|
||||
<br />
|
||||
<br />
|
||||
<strong>Nvidia NIM</strong> - Nvidia NIM offer 1000 free API credit when creating an account with a personal email and 5000 with a business email.
|
||||
<br />
|
||||
<br />
|
||||
<strong>Together AI</strong> - Together AI offer 5$ credits when creating an account and have a lot of open source models available.
|
||||
<br />
|
||||
<br />
|
||||
<strong>DeepSeek</strong> - DeepSeek do not offer free credits.
|
||||
<br />
|
||||
<br />
|
||||
<strong>Goose AI</strong> - Chat API will be available soon.
|
||||
<br />
|
||||
<br />
|
||||
<strong>Custom endpoint</strong> - You can also use custom endpoints as long as the key is valid and it use
|
||||
the openai api.
|
||||
<br />
|
||||
<br />
|
||||
<strong>Nvidia NIM</strong> - Available soon.
|
||||
<br />
|
||||
<br />
|
||||
<strong>Goose AI</strong> - Chat API will be available soon.
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
Loading…
x
Reference in New Issue
Block a user