Working custom endpoint

This commit is contained in:
Adrien Bouvais 2024-05-23 00:26:13 +02:00
parent 1603532150
commit 103d6371c8
9 changed files with 102 additions and 36 deletions

11
Chat.go
View File

@ -156,7 +156,7 @@ func GetMessageContentHandler(c *fiber.Ctx) error {
out := "<div class='message-header'>"
out += "<p>"
out += "<strong>" + selectedMessage.LLM.Name + "</strong> <small>" + selectedMessage.LLM.Model.Name + "</small>"
out += "<strong>" + selectedMessage.LLM.Name + "</strong> <small>" + selectedMessage.LLM.Model.ModelID + "</small>"
out += " </p>"
out += "</div>"
out += "<div class='message-body'>"
@ -449,19 +449,12 @@ func GenerateModelPopoverHTML(refresh bool) string {
}
}
}
FILTER .user = global currentUser AND .name != 'none' AND .to_delete = false
FILTER .user = global currentUser AND .name != 'none' AND .to_delete = false
`, &llms)
if err != nil {
panic(err)
}
//for i := 0; i < len(llms); i++ {
// // If the modelID len is higher than 15, truncate it
// if len(llms[i].Model.ModelID) > 12 {
// llms[i].Model.ModelID = llms[i].Model.ModelID[0:12] + "..."
// }
//}
modelInfos := GetAvailableModels()
out, err := pongo2.Must(pongo2.FromFile("views/partials/popover-models.html")).Execute(pongo2.Context{

43
LLM.go
View File

@ -2,6 +2,7 @@ package main
import (
"encoding/json"
"fmt"
"strconv"
"github.com/edgedb/edgedb-go"
@ -52,12 +53,43 @@ func createLLM(c *fiber.Ctx) error {
name := c.FormValue("model-name-input")
modelID := c.FormValue("selectedLLMId")
temperature := c.FormValue("temperature-slider")
systemPrompt := c.FormValue("model-prompt-input")
f, _ := strconv.ParseFloat(temperature, 32)
temperatureFloat := float32(f)
err := edgeClient.Execute(edgeCtx, `
systemPrompt := c.FormValue("model-prompt-input")
url := c.FormValue("model-url-input")
token := c.FormValue("model-key-input")
customID := c.FormValue("model-cid-input")
fmt.Println(name, modelID, temperatureFloat, systemPrompt, url, token)
if modelID == "custom" {
err := edgeClient.Execute(edgeCtx, `
INSERT LLM {
name := <str>$0,
context := <str>$1,
temperature := <float32>$2,
modelInfo := (INSERT ModelInfo {
name := <str>$0,
modelID := <str>$5,
inputPrice := 0.0,
outputPrice := 0.0,
maxToken := 500,
company := (SELECT Company FILTER .name = "huggingface" LIMIT 1),
}),
custom_endpoint := (INSERT CustomEndpoint {
endpoint := <str>$3,
key := <str>$4,
}),
user := global currentUser
};
`, name, systemPrompt, temperatureFloat, url, token, customID) // TODO Add real max token
if err != nil {
fmt.Println("Error in createLLM: ", err)
panic(err)
}
} else {
err := edgeClient.Execute(edgeCtx, `
INSERT LLM {
name := <str>$0,
context := <str>$1,
@ -66,8 +98,9 @@ func createLLM(c *fiber.Ctx) error {
user := global currentUser
}
`, name, systemPrompt, temperatureFloat, modelID)
if err != nil {
panic(err)
if err != nil {
panic(err)
}
}
return c.SendString(GenerateModelPopoverHTML(true))

View File

@ -14,6 +14,7 @@ type GooseaiCompletionRequest struct {
Model string `json:"model"`
Prompt []string `json:"prompt"`
Temperature float64 `json:"temperature"`
MaxToken int32 `json:"max_tokens"`
}
type GooseaiCompletionResponse struct {
@ -53,6 +54,7 @@ func TestGooseaiKey(apiKey string) bool {
Model: "gpt-j-6b",
Prompt: []string{"Hello, how are you?"},
Temperature: 0,
MaxToken: 10,
}
jsonBody, err := json.Marshal(requestBody)
@ -111,10 +113,16 @@ func RequestGooseai(model string, messages []Message, temperature float64) (Goos
url := "https://api.goose.ai/v1/engines/" + model + "/completions"
var prompt string
for _, message := range messages {
prompt += message.Content
}
requestBody := GooseaiCompletionRequest{
Model: model,
Prompt: []string{messages[len(messages)-1].Content},
Prompt: []string{prompt},
Temperature: temperature,
MaxToken: 300,
}
jsonBody, err := json.Marshal(requestBody)

View File

@ -77,6 +77,8 @@ func RequestHuggingface(llm LLM, messages []Message, temperature float64) (Huggi
req.Header.Set("Authorization", "Bearer "+llm.Endpoint.Key)
req.Header.Set("Content-Type", "application/json")
fmt.Println(url, llm.Endpoint.Key)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {

View File

@ -177,6 +177,9 @@ func RequestMistral(model string, messages []Message, temperature float64) (Mist
FILTER .modelID = <str>$0
LIMIT 1
`, &usedModelInfo, model)
if err != nil {
return MistralChatCompletionResponse{}, fmt.Errorf("error getting model info: %w", err)
}
if usedModelInfo.InputPrice == 0 || usedModelInfo.OutputPrice == 0 {
return MistralChatCompletionResponse{}, fmt.Errorf("model %s not found in Mistral", model)

View File

@ -145,7 +145,7 @@ func GetAvailableModels() []ModelInfo {
name,
icon
}
} FILTER .modelID != 'none'
} FILTER .modelID != 'none' AND .company.name != 'huggingface'
`, &models)
if err != nil {
panic(err)

View File

@ -29,7 +29,7 @@
{% if not message.Hidden %}
<div class="message-header">
<p>
<strong>{{ message.Name }}</strong> <small>{{ message.Model }}</small>
<strong>{{ message.Name }}</strong> <small>{{ message.ModelID }}</small>
</p>
</div>
<div class="message-body">

View File

@ -1,14 +1,14 @@
<div class="dropdown is-hoverable is-up is-right" id="models-dropdown">
<div class="dropdown is-up is-right" id="models-dropdown">
<div class="dropdown-trigger">
<button class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu4">
<button class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu3"
onclick="this.parentElement.parentElement.classList.toggle('is-active')">
<span class="icon"><i class="fa-solid fa-robot"></i></span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu4" role="menu">
<div class="dropdown-menu" id="dropdown-menu3" role="menu">
<div class="dropdown-content is-small">
<div class="dropdown-item" id="models-list">
{% for LLM in LLMs %}
<!-- Added "has-text" as a default class -->
<div class="icon-text has-text unselected" onclick="toggleSelection(this)" style="cursor: pointer;"
value="{{ LLM.ID.String() }}">
<span class="icon">
@ -17,7 +17,7 @@
<span>{{ LLM.Name }}</span>
</div>
{% endfor %}
<div class="is-flex is-justify-content-space-between mt-3">
<div class="is-flex is-justify-content-space-between mt-4">
<button class="button is-small is-danger is-outlined" hx-get="/deleteLLM" hx-swap="outerHTML"
hx-target="#models-dropdown" hx-confirm="Are you sure?" hx-trigger="click"
hx-vals="js:{selectedLLMIds: getSelectedModelsIDs()}">
@ -38,7 +38,6 @@
</button>
</div>
</div>
</div>
<div class="dropdown-item is-hidden" id="models-creation">
<form id="create-model-form" hx-post="/createLLM" hx-target="#models-dropdown" hx-swap="outerHTML">
@ -46,12 +45,18 @@
placeholder="Model name">
<div class="select is-fullwidth is-small mb-3" id="model-id-input">
<select name="selectedLLMId">
<option value="custom">Custom endpoint</option>
{% for modelInfo in ModelInfos %}
<option value="{{ modelInfo.ModelID }}">{{ modelInfo.ModelID }}</option>
{% endfor %}
<option value="custom">Custom endpoint</option>
</select>
</div>
<input class="input is-small mb-3 is-hidden" type="text" id="model-cid-input" name="model-cid-input"
placeholder="Model id">
<input class="input is-small mb-3 is-hidden" type="text" id="model-url-input" name="model-url-input"
placeholder="URL (with /v1/chat/completions)">
<input class="input is-small mb-3 is-hidden" type="text" id="model-key-input" name="model-key-input"
placeholder="Token">
<p><small>Temperature:</small></p>
<input class="slider is-small mb-3" step="0.05" min="0" max="1" value="0" type="range"
id="temperature-slider" name="temperature-slider">
@ -79,6 +84,27 @@
</div>
<script>
function toggleDropdown(event) {
event.stopPropagation();
document.getElementById('models-dropdown').classList.toggle('is-active');
}
function closeDropdown(event) {
if (!document.getElementById('models-dropdown').contains(event.target)) {
document.getElementById('models-dropdown').classList.remove('is-active');
}
}
document.addEventListener('click', closeDropdown);
document.getElementById('model-id-input').querySelector('select').addEventListener('change', function () {
const customEndpoint = this.value === 'custom';
document.getElementById('model-url-input').classList.toggle('is-hidden', !customEndpoint);
document.getElementById('model-key-input').classList.toggle('is-hidden', !customEndpoint);
document.getElementById('model-cid-input').classList.toggle('is-hidden', !customEndpoint);
});
document.getElementById('temperature-slider').addEventListener('input', function () {
document.getElementById('temperature-slider-output').innerHTML = this.value;
})

View File

@ -40,6 +40,16 @@
</span>
</p>
</div>
<div class="field has-addons is-hidden" id="groq-field">
<p class="control has-icons-left is-expanded">
<input class="input is-small {% if GroqExists %}is-success{% endif %}" type="text"
placeholder="Groq API key" {%if not IsLogin %}disabled{% endif %} name="groq_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" title="Gemini is unavailable because of Europe"
id="gemini-field">
<p class="control has-icons-left is-expanded">
@ -50,21 +60,12 @@
</span>
</p>
</div>
<div class="field has-addons is-hidden" id="goose-field">
<div class="field has-addons is-hidden" id="goose-field"
title="GooseAI chat API soon to be available">
<p class="control has-icons-left is-expanded">
<input class="input is-small {% if GooseaiExists %}is-success{% endif %}" type="text"
{%if not IsLogin %}disabled{% endif %} placeholder="Gooseai API key"
name="goose_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="groq-field">
<p class="control has-icons-left is-expanded">
<input class="input is-small {% if GroqExists %}is-success{% endif %}" type="text"
placeholder="Groq API key" {%if not IsLogin %}disabled{% endif %} name="groq_key"
autocomplete="off">
name="goose_key" autocomplete="off" disabled>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>