mirror of
https://github.com/ParisNeo/lollms-webui.git
synced 2024-12-22 22:02:21 +00:00
upgraded ui
This commit is contained in:
parent
3ca79f9909
commit
de4a6ecef6
@ -761,7 +761,7 @@ class LoLLMsAPPI(LollmsApplication):
|
||||
sys.stdout.flush()
|
||||
antiprompt = self.personality.detect_antiprompt(self.connections[client_id]["generated_text"])
|
||||
if antiprompt:
|
||||
ASCIIColors.warning(f"Detected hallucination with antiprompt: {antiprompt}")
|
||||
ASCIIColors.warning(f"\nDetected hallucination with antiprompt: {antiprompt}")
|
||||
self.connections[client_id]["generated_text"] = self.remove_text_from_string(self.connections[client_id]["generated_text"],antiprompt)
|
||||
self.socketio.emit('message', {
|
||||
'data': self.connections[client_id]["generated_text"],
|
||||
|
38
app.py
38
app.py
@ -54,8 +54,9 @@ import psutil
|
||||
from lollms.main_config import LOLLMSConfig
|
||||
from typing import Optional
|
||||
import gc
|
||||
import pkg_resources
|
||||
|
||||
|
||||
import gc
|
||||
|
||||
log = logging.getLogger('werkzeug')
|
||||
log.setLevel(logging.ERROR)
|
||||
@ -132,18 +133,8 @@ def run_restart_script(args):
|
||||
arg_string = " ".join([f"--{key} {value}" for key, value in valid_args.items()])
|
||||
file.write(arg_string)
|
||||
|
||||
os.system(f"python {restart_script} {temp_file}")
|
||||
|
||||
# Reload the main script with the original arguments
|
||||
if os.path.exists(temp_file):
|
||||
with open(temp_file, "r") as file:
|
||||
args = file.read().split()
|
||||
main_script = "app.py" # Replace with the actual name of your main script
|
||||
os.system(f"python {main_script} {' '.join(args)}")
|
||||
os.remove(temp_file)
|
||||
else:
|
||||
print("Error: Temporary arguments file not found.")
|
||||
sys.exit(1)
|
||||
os.system(f"python {restart_script}")
|
||||
sys.exit(0)
|
||||
|
||||
def run_update_script(args):
|
||||
update_script = "update_script.py"
|
||||
@ -161,18 +152,8 @@ def run_update_script(args):
|
||||
arg_string = " ".join([f"--{key} {value}" for key, value in valid_args.items()])
|
||||
file.write(arg_string)
|
||||
|
||||
os.system(f"python {update_script} {temp_file}")
|
||||
|
||||
# Reload the main script with the original arguments
|
||||
if os.path.exists(temp_file):
|
||||
with open(temp_file, "r") as file:
|
||||
args = file.read().split()
|
||||
main_script = "app.py" # Replace with the actual name of your main script
|
||||
os.system(f"python {main_script} {' '.join(args)}")
|
||||
os.remove(temp_file)
|
||||
else:
|
||||
print("Error: Temporary arguments file not found.")
|
||||
sys.exit(1)
|
||||
os.system(f"python {update_script}")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class LoLLMsWebUI(LoLLMsAPPI):
|
||||
@ -205,6 +186,9 @@ class LoLLMsWebUI(LoLLMsAPPI):
|
||||
# Endpoints
|
||||
# =========================================================================================
|
||||
|
||||
self.add_endpoint("/get_lollms_version", "get_lollms_version", self.get_lollms_version, methods=["POST"])
|
||||
|
||||
|
||||
self.add_endpoint("/reload_binding", "reload_binding", self.reload_binding, methods=["POST"])
|
||||
self.add_endpoint("/update_software", "update_software", self.update_software, methods=["GET"])
|
||||
self.add_endpoint("/clear_uploads", "clear_uploads", self.clear_uploads, methods=["GET"])
|
||||
@ -1162,6 +1146,10 @@ class LoLLMsWebUI(LoLLMsAPPI):
|
||||
ASCIIColors.info("")
|
||||
run_update_script(self.args)
|
||||
|
||||
def get_lollms_version(self):
|
||||
version = pkg_resources.get_distribution('lollms').version
|
||||
ASCIIColors.yellow("Lollms version : "+ version)
|
||||
return jsonify({"version":version})
|
||||
|
||||
def reload_binding(self):
|
||||
try:
|
||||
|
@ -5,9 +5,9 @@ import git
|
||||
import subprocess
|
||||
import argparse
|
||||
|
||||
def run_git_pull(repo_path):
|
||||
def run_git_pull():
|
||||
try:
|
||||
repo = git.Repo(repo_path)
|
||||
repo = git.Repo(".")
|
||||
origin = repo.remotes.origin
|
||||
origin.pull()
|
||||
except git.GitCommandError as e:
|
||||
@ -27,7 +27,7 @@ def main():
|
||||
repo_path = args.repo
|
||||
|
||||
# Perform git pull to update the repository
|
||||
run_git_pull(repo_path)
|
||||
run_git_pull()
|
||||
|
||||
# Install the new requirements
|
||||
install_requirements()
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
web/dist/index.html
vendored
4
web/dist/index.html
vendored
@ -6,8 +6,8 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LoLLMS WebUI - Welcome</title>
|
||||
<script type="module" crossorigin src="/assets/index-0fe86d3d.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-218d4a39.css">
|
||||
<script type="module" crossorigin src="/assets/index-2afc397e.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-506d87c6.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
@ -1,32 +1,174 @@
|
||||
<template>
|
||||
<div>
|
||||
<input v-model="inputValue" :placeholder="placeholderText" />
|
||||
<button @click="pasteFromClipboard"><i data-feather="clipboard"></i></button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import feather from 'feather-icons'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
inputValue: "",
|
||||
placeholderText: "Enter text here",
|
||||
};
|
||||
<div class="flex items-center space-x-2">
|
||||
<input
|
||||
:value="value"
|
||||
:type="inputType"
|
||||
:placeholder="placeholderText"
|
||||
@input="handleInput"
|
||||
@paste="handlePaste"
|
||||
|
||||
class="flex-1 px-4 py-2 text-lg border border-gray-300 rounded-md focus:outline-none focus:ring focus:border-blue-500"
|
||||
/>
|
||||
<button
|
||||
@click="pasteFromClipboard"
|
||||
class="p-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring focus:border-blue-300"
|
||||
>
|
||||
<i data-feather="clipboard"></i>
|
||||
</button>
|
||||
<!-- File type button -->
|
||||
<button
|
||||
v-if="inputType === 'file'"
|
||||
@click="openFileInput"
|
||||
class="p-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring focus:border-blue-300"
|
||||
>
|
||||
<i data-feather="upload"></i>
|
||||
</button>
|
||||
|
||||
<!-- Hidden file input -->
|
||||
<input
|
||||
v-if="inputType === 'file'"
|
||||
ref="fileInput"
|
||||
type="file"
|
||||
style="display: none"
|
||||
:accept="fileAccept"
|
||||
@change="handleFileInputChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import feather from "feather-icons";
|
||||
import { nextTick } from "vue";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: String, // Custom v-model prop to receive the input value
|
||||
inputType: {
|
||||
type: String,
|
||||
default: "text",
|
||||
validator: (value) =>
|
||||
["text", "email", "password", "file", "path", "integer", "float"].includes(
|
||||
value
|
||||
),
|
||||
},
|
||||
mounted() {
|
||||
console.log('mnted all chat', this.allDiscussionPersonalities)
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
})
|
||||
fileAccept: String, // Prop to specify the accepted file types
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
placeholderText: this.getPlaceholderText(),
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
// Watch for changes from parent component to keep inputValue in sync
|
||||
console.log("Changing value to ", newVal)
|
||||
this.inputValue = newVal;
|
||||
},
|
||||
methods: {
|
||||
pasteFromClipboard() {
|
||||
navigator.clipboard.readText().then((text) => {
|
||||
},
|
||||
mounted() {
|
||||
nextTick(() => {
|
||||
feather.replace();
|
||||
});
|
||||
console.log("Changing value to ", this.value)
|
||||
this.inputValue = this.value;
|
||||
},
|
||||
methods: {
|
||||
getPlaceholderText() {
|
||||
switch (this.inputType) {
|
||||
case "text":
|
||||
return "Enter text here";
|
||||
case "email":
|
||||
return "Enter your email";
|
||||
case "password":
|
||||
return "Enter your password";
|
||||
case "file":
|
||||
case "path":
|
||||
return "Choose a file";
|
||||
case "integer":
|
||||
return "Enter an integer";
|
||||
case "float":
|
||||
return "Enter a float";
|
||||
default:
|
||||
return "Enter value here";
|
||||
}
|
||||
},
|
||||
handleInput(event) {
|
||||
if (this.inputType === "integer") {
|
||||
const sanitizedValue = event.target.value.replace(/[^0-9]/g, "");
|
||||
this.inputValue = sanitizedValue;
|
||||
}
|
||||
console.log("handling input : ", event.target.value)
|
||||
this.$emit('input', event.target.value)
|
||||
},
|
||||
async pasteFromClipboard() {
|
||||
try {
|
||||
const text = await navigator.clipboard.readText();
|
||||
this.handleClipboardData(text);
|
||||
} catch (error) {
|
||||
console.error("Failed to read from clipboard:", error);
|
||||
// Handle the error gracefully, e.g., show a message to the user
|
||||
}
|
||||
},
|
||||
handlePaste(event) {
|
||||
const text = event.clipboardData.getData("text");
|
||||
this.handleClipboardData(text);
|
||||
},
|
||||
handleClipboardData(text) {
|
||||
switch (this.inputType) {
|
||||
case "email":
|
||||
this.inputValue = this.isValidEmail(text) ? text : "";
|
||||
break;
|
||||
case "password":
|
||||
// Here, you can add validation for password strength if needed
|
||||
this.inputValue = text;
|
||||
});
|
||||
},
|
||||
break;
|
||||
case "file":
|
||||
case "path":
|
||||
// For file and path types, you might not want to allow pasting directly
|
||||
// into the input field. You can handle this as per your requirements.
|
||||
this.inputValue = "";
|
||||
break;
|
||||
case "integer":
|
||||
this.inputValue = this.parseInteger(text);
|
||||
break;
|
||||
case "float":
|
||||
this.inputValue = this.parseFloat(text);
|
||||
break;
|
||||
default:
|
||||
this.inputValue = text;
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
isValidEmail(value) {
|
||||
// Simple email validation using regex
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(value);
|
||||
},
|
||||
parseInteger(value) {
|
||||
// Parse integer value or return empty if invalid
|
||||
const parsedValue = parseInt(value);
|
||||
return isNaN(parsedValue) ? "" : parsedValue;
|
||||
},
|
||||
parseFloat(value) {
|
||||
// Parse float value or return empty if invalid
|
||||
const parsedValue = parseFloat(value);
|
||||
return isNaN(parsedValue) ? "" : parsedValue;
|
||||
},
|
||||
openFileInput() {
|
||||
this.$refs.fileInput.click(); // Trigger the file input when the button is clicked
|
||||
},
|
||||
handleFileInputChange(event) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
// Display the file path in the input field
|
||||
this.inputValue = file.name;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Optional: You can add more custom styling here if needed */
|
||||
</style>
|
||||
|
@ -159,8 +159,6 @@ export default {
|
||||
const script = document.createElement('script');
|
||||
script.textContent = `
|
||||
// Your inline script code here
|
||||
console.log('Inline script executed!');
|
||||
|
||||
function copyContentToClipboard(id) {
|
||||
console.log("copied");
|
||||
const codeElement = document.getElementById('code_' + id);
|
||||
|
@ -236,15 +236,19 @@ export default {
|
||||
this.voices = this.speechSynthesis.getVoices();
|
||||
},
|
||||
speak() {
|
||||
if(this.msg)
|
||||
{
|
||||
this.isVoiceActive = false;
|
||||
if (this.msg) {
|
||||
this.speechSynthesis.cancel();
|
||||
this.msg = null;
|
||||
return
|
||||
this.isVoiceActive = false;
|
||||
return;
|
||||
}
|
||||
const textToSpeak = this.message.content;
|
||||
let startIndex =0;
|
||||
// Set isVoiceActive to true before starting synthesis
|
||||
console.log("voice on")
|
||||
this.isVoiceActive = true;
|
||||
|
||||
const chunkSize = 200; // You can adjust the chunk size as needed
|
||||
this.message.content;
|
||||
|
||||
// Create a new SpeechSynthesisUtterance instance
|
||||
this.msg = new SpeechSynthesisUtterance();
|
||||
@ -254,33 +258,49 @@ export default {
|
||||
this.msg.voice = this.voices.filter(voice => voice.name === this.$store.state.config.audio_out_voice)[0];
|
||||
}
|
||||
|
||||
// Set isVoiceActive to true before starting synthesis
|
||||
this.isVoiceActive = true;
|
||||
|
||||
// Function to find the index of the last sentence that fits within the chunk size
|
||||
const findLastSentenceIndex = (startIndex) => {
|
||||
let txt = this.message.content.substring(startIndex, startIndex+chunkSize)
|
||||
// Define an array of characters that represent end of sentence markers.
|
||||
const endOfSentenceMarkers = ['.', '!', '?'];
|
||||
|
||||
// Initialize a variable to store the index of the last end of sentence marker.
|
||||
let lastIndex = -1;
|
||||
|
||||
// Iterate through the end of sentence markers and find the last occurrence in the txt string.
|
||||
endOfSentenceMarkers.forEach(marker => {
|
||||
const markerIndex = txt.lastIndexOf(marker);
|
||||
if (markerIndex > lastIndex) {
|
||||
lastIndex = markerIndex;
|
||||
}
|
||||
});
|
||||
return lastIndex+startIndex;
|
||||
};
|
||||
|
||||
// Function to speak a chunk of text
|
||||
const speakChunk = (startIdx) => {
|
||||
const chunk = textToSpeak.substr(startIdx, chunkSize);
|
||||
const speakChunk = () => {
|
||||
const endIndex = findLastSentenceIndex(startIndex);
|
||||
const chunk = this.message.content.substring(startIndex, endIndex);
|
||||
this.msg.text = chunk;
|
||||
startIndex = endIndex + 1;
|
||||
this.msg.onend = (event) => {
|
||||
if (startIndex < this.message.content.length-2) {
|
||||
// Use setTimeout to add a brief delay before speaking the next chunk
|
||||
setTimeout(() => {
|
||||
speakChunk();
|
||||
}, 1); // Adjust the delay as needed
|
||||
} else {
|
||||
this.isVoiceActive = false;
|
||||
console.log("voice off :",this.message.content.length," ",endIndex)
|
||||
}
|
||||
};
|
||||
this.speechSynthesis.speak(this.msg);
|
||||
};
|
||||
|
||||
// Listen for the end event to set isVoiceActive to false after synthesis completes
|
||||
this.msg.addEventListener('end', () => {
|
||||
const startIdx = msg.text.length;
|
||||
if (startIdx < textToSpeak.length) {
|
||||
// Use setTimeout to add a brief delay before speaking the next chunk
|
||||
setTimeout(() => {
|
||||
speakChunk(startIdx);
|
||||
}, 200); // Adjust the delay as needed
|
||||
} else {
|
||||
this.isVoiceActive = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Speak the first chunk
|
||||
speakChunk(0);
|
||||
speakChunk();
|
||||
},
|
||||
|
||||
|
||||
toggleModel() {
|
||||
this.expanded = !this.expanded;
|
||||
@ -378,10 +398,24 @@ export default {
|
||||
day_diff == 1 && "Yesterday" ||
|
||||
day_diff < 7 && day_diff + " days ago" ||
|
||||
day_diff < 31 && Math.ceil(day_diff / 7) + " weeks ago";
|
||||
}
|
||||
|
||||
},
|
||||
checkForFullSentence() {
|
||||
if(this.message.content.trim().split(" ").length>3){
|
||||
// If the sentence contains at least 3 words, call the speak() method
|
||||
this.speak();
|
||||
return; // Exit the loop after the first full sentence is found
|
||||
}
|
||||
},
|
||||
|
||||
}, watch: {
|
||||
'message.content': function (newContent) {
|
||||
if(this.$store.state.config.auto_speak){
|
||||
if(!this.isVoiceActive){
|
||||
// Watch for changes to this.message.content and call the checkForFullSentence method
|
||||
this.checkForFullSentence();
|
||||
}
|
||||
}
|
||||
},
|
||||
showConfirmation() {
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
@ -403,6 +437,7 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
computed: {
|
||||
isTalking :{
|
||||
|
@ -23,6 +23,19 @@
|
||||
<i data-feather="github"></i>
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://www.youtube.com/channel/UCJzrg0cyQV2Z30SQ1v2FdSQ" target="_blank">
|
||||
|
||||
<div class="text-2xl hover:text-primary duration-150" title="Visit repository page">
|
||||
<i data-feather="youtube"></i>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="https://twitter.com/SpaceNerduino" target="_blank">
|
||||
|
||||
<div class="text-2xl hover:text-primary duration-150" title="Follow me on my twitter acount">
|
||||
<i data-feather="twitter"></i>
|
||||
</div>
|
||||
</a>
|
||||
<div class="sun text-2xl w-6 hover:text-primary duration-150" title="Swith to Light theme"
|
||||
@click="themeSwitch()">
|
||||
<i data-feather="sun"></i>
|
||||
|
@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<div class="container mx-auto p-4 bg-bg-light-tone dark:bg-bg-dark-tone shadow-lg">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold mb-2">About Lord of large Language Models</h2>
|
||||
<p class="mb-4"> Lollms version {{ lollmsVersion }}</p>
|
||||
<p>Discord link: <a class="text-blue-500 hover:text-blue-400 duration-150" href="https://discord.gg/C73K7hjy">https://discord.gg/C73K7hjy</a></p>
|
||||
</div>
|
||||
|
||||
<div class="mb-8 overflow-y-auto max-h-96 scrollbar">
|
||||
<h2 class="text-2xl font-bold mb-2">Frequently Asked Questions</h2>
|
||||
@ -12,7 +17,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold mb-2">Contact Us</h2>
|
||||
<p class="mb-4">If you have any further questions or need assistance, feel free to reach out to us.</p>
|
||||
<p class="mb-4">If you have any further questions or need assistance, feel free to reach out to me.</p>
|
||||
<p>Discord link: <a class="text-blue-500 hover:text-blue-400 duration-150" href="https://discord.gg/C73K7hjy">https://discord.gg/C73K7hjy</a></p>
|
||||
</div>
|
||||
<div class="mt-8">
|
||||
@ -22,23 +27,50 @@
|
||||
<p>Check out the project on <a class="text-blue-500 hover:text-blue-400 duration-150" :href="githubLink" target="_blank" rel="noopener noreferrer">GitHub</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import Papa from 'papaparse'; // Import the Papa Parse library for CSV parsing
|
||||
|
||||
export default {
|
||||
name: 'HelpPage',
|
||||
data() {
|
||||
return {
|
||||
lollmsVersion: "unknown",
|
||||
faqs: [], // Array to store the loaded FAQs
|
||||
githubLink: 'https://github.com/ParisNeo/lollms-webui', // Replace with your project's GitHub link
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadFAQs(); // Call the method to load FAQs when the component is mounted
|
||||
this.fetchLollmsVersion().then((val)=>{this.lollmsVersion=val});
|
||||
},
|
||||
computed: {
|
||||
// This will be triggered whenever lollmsVersion is updated
|
||||
// but it will not be directly used in the template.
|
||||
async fetchLollmsVersion() {
|
||||
return await axios.get("/get_lollms_version");
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
// Fetch the data when the component is created
|
||||
},
|
||||
methods: {
|
||||
async api_get_req(endpoint) {
|
||||
try {
|
||||
const res = await axios.get("/" + endpoint);
|
||||
|
||||
if (res) {
|
||||
|
||||
return res.data
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error.message, 'api_get_req')
|
||||
return
|
||||
}
|
||||
},
|
||||
loadFAQs() {
|
||||
// Fetch and parse the CSV file
|
||||
fetch('/help/faqs.csv')
|
||||
|
@ -530,199 +530,234 @@
|
||||
</button>
|
||||
</div>
|
||||
<div :class="{ 'hidden': minconf_collapsed }" class="flex flex-col mb-2 px-3 pb-0">
|
||||
<div class="flex flex-col mb-2 px-3 pb-2">
|
||||
<div class="pb-2">
|
||||
<table class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||
<th>Generic</th>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="db_path" class="text-sm font-bold" style="margin-right: 1rem;">Database path:</label>
|
||||
</td>
|
||||
<td style="width: 100%;">
|
||||
<input
|
||||
type="text"
|
||||
id="db_path"
|
||||
required
|
||||
v-model="db_path"
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('db_path', db_path)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="enable_gpu" class="text-sm font-bold" style="margin-right: 1rem;">Enable GPU:</label>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="enable_gpu"
|
||||
required
|
||||
v-model="enable_gpu"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('enable_gpu', enable_gpu)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="auto_update" class="text-sm font-bold" style="margin-right: 1rem;">Auto update:</label>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="auto_update"
|
||||
required
|
||||
v-model="auto_update"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('auto_update', auto_update)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pb-2">
|
||||
<table class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||
<th>User</th>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="user_name" class="text-sm font-bold" style="margin-right: 1rem;">User name:</label>
|
||||
</td>
|
||||
<td style="width: 100%;">
|
||||
<input
|
||||
type="text"
|
||||
id="user_name"
|
||||
required
|
||||
v-model="userName"
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('user_name', userName)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Row 3 -->
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="user_name" class="text-sm font-bold" style="margin-right: 1rem;">User avatar:</label>
|
||||
</td>
|
||||
<td style="width: 100%;">
|
||||
<label for="avatar-upload">
|
||||
<img :src="user_avatar" class="w-50 h-50 rounded-full" style="max-width: 50px; max-height: 50px; cursor: pointer;">
|
||||
</label>
|
||||
<input type="file" id="avatar-upload" style="display: none" @change="uploadAvatar">
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('user_name', userName)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Row 4 -->
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="use_user_name_in_discussions" class="text-sm font-bold" style="margin-right: 1rem;">Use User Name in discussions:</label>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="use_user_name_in_discussions"
|
||||
required
|
||||
v-model="use_user_name_in_discussions"
|
||||
class=" mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('use_user_name_in_discussions', use_user_name_in_discussions)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pb-2">
|
||||
<table class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||
<th>Audio</th>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="auto_speak" class="text-sm font-bold" style="margin-right: 1rem;">Enable auto speak:</label>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="auto_speak"
|
||||
required
|
||||
v-model="auto_speak"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('auto_speak', auto_speak)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="audio_in_language" class="text-sm font-bold" style="margin-right: 1rem;">Input Audio Language:</label>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Select element for choosing the input audio language -->
|
||||
<select
|
||||
id="audio_in_language"
|
||||
v-model="audio_in_language"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<!-- Options with language codes and corresponding language names -->
|
||||
<option v-for="language in audioLanguages" :key="language.code" :value="language.code">
|
||||
{{ language.name }}
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('audio_in_language', audio_in_language)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="audio_out_voice" class="text-sm font-bold" style="margin-right: 1rem;">Output Audio Voice:</label>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Select element for choosing the output audio voice -->
|
||||
<select
|
||||
id="audio_out_voice"
|
||||
v-model="audio_out_voice"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<!-- Options with available voices in the browser -->
|
||||
<option v-for="voice in audioVoices" :key="voice.name" :value="voice.name">
|
||||
{{ voice.name }}
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('audio_out_voice', audio_out_voice)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="enable_gpu" class="text-sm font-bold" style="margin-right: 1rem;">Enable GPU:</label>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="enable_gpu"
|
||||
required
|
||||
v-model="enable_gpu"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('enable_gpu', enable_gpu)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="auto_update" class="text-sm font-bold" style="margin-right: 1rem;">Auto update:</label>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="auto_update"
|
||||
required
|
||||
v-model="auto_update"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('auto_update', auto_update)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 2 -->
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="db_path" class="text-sm font-bold" style="margin-right: 1rem;">Database path:</label>
|
||||
</td>
|
||||
<td style="width: 100%;">
|
||||
<input
|
||||
type="text"
|
||||
id="db_path"
|
||||
required
|
||||
v-model="db_path"
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('db_path', db_path)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="user_name" class="text-sm font-bold" style="margin-right: 1rem;">User name:</label>
|
||||
</td>
|
||||
<td style="width: 100%;">
|
||||
<input
|
||||
type="text"
|
||||
id="user_name"
|
||||
required
|
||||
v-model="userName"
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('user_name', userName)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Row 3 -->
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="user_name" class="text-sm font-bold" style="margin-right: 1rem;">User avatar:</label>
|
||||
</td>
|
||||
<td style="width: 100%;">
|
||||
<label for="avatar-upload">
|
||||
<img :src="user_avatar" class="w-50 h-50 rounded-full" style="max-width: 50px; max-height: 50px; cursor: pointer;">
|
||||
</label>
|
||||
<input type="file" id="avatar-upload" style="display: none" @change="uploadAvatar">
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('user_name', userName)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Row 4 -->
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="use_user_name_in_discussions" class="text-sm font-bold" style="margin-right: 1rem;">Use User Name in discussions:</label>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="use_user_name_in_discussions"
|
||||
required
|
||||
v-model="use_user_name_in_discussions"
|
||||
class=" mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('use_user_name_in_discussions', use_user_name_in_discussions)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="audio_in_language" class="text-sm font-bold" style="margin-right: 1rem;">Input Audio Language:</label>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Select element for choosing the input audio language -->
|
||||
<select
|
||||
id="audio_in_language"
|
||||
v-model="audio_in_language"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<!-- Options with language codes and corresponding language names -->
|
||||
<option v-for="language in audioLanguages" :key="language.code" :value="language.code">
|
||||
{{ language.name }}
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('audio_in_language', audio_in_language)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="audio_out_voice" class="text-sm font-bold" style="margin-right: 1rem;">Output Audio Voice:</label>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Select element for choosing the output audio voice -->
|
||||
<select
|
||||
id="audio_out_voice"
|
||||
v-model="audio_out_voice"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<!-- Options with available voices in the browser -->
|
||||
<option v-for="voice in audioVoices" :key="voice.name" :value="voice.name">
|
||||
{{ voice.name }}
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('audio_out_voice', audio_out_voice)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</table>
|
||||
|
||||
<!-- Row 0 -->
|
||||
<div class="w-full">
|
||||
@ -742,13 +777,15 @@
|
||||
</button>
|
||||
</div>
|
||||
<!-- Row 0 -->
|
||||
<div v-if="has_updates" class="w-full">
|
||||
<div class="w-full">
|
||||
<button
|
||||
class="hover:text-secondary w-full bg-red-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="api_get_req('update_software').then((res)=>{if(res.status){this.$refs.toast.showToast('Success!', 4, true)}else{this.$refs.toast.showToast('Success!', 4, true)}})"
|
||||
>
|
||||
Upgrade program
|
||||
<i data-feather="alert-circle"></i>
|
||||
<div v-if="has_updates" >
|
||||
<i data-feather="alert-circle"></i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -3064,6 +3101,15 @@ export default {
|
||||
},
|
||||
|
||||
},
|
||||
auto_speak:{
|
||||
get() {
|
||||
return this.$store.state.config.auto_speak;
|
||||
},
|
||||
set(value) {
|
||||
// You should not set the value directly here; use the updateSetting method instead
|
||||
this.$store.state.config.auto_speak = value
|
||||
},
|
||||
},
|
||||
audio_in_language:{
|
||||
get() {
|
||||
return this.$store.state.config.audio_in_language;
|
||||
|
@ -4,101 +4,44 @@
|
||||
<!-- Model/Tokenizer -->
|
||||
<div class="mb-4">
|
||||
<label for="model_name" class="text-sm">Model Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="model_name"
|
||||
v-model="model_name"
|
||||
required
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<ClipBoardTextInput id="model_path" inputType="text" :value="model_name" />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="tokenizer_name" class="text-sm">Tokenizer Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="tokenizer_name"
|
||||
v-model="tokenizer_name"
|
||||
required
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<ClipBoardTextInput id="model_path" inputType="text" :value="tokenizer_name" />
|
||||
</div>
|
||||
|
||||
<!-- Dataset -->
|
||||
<div class="mb-4">
|
||||
<label for="dataset_path" class="text-sm">Dataset:</label>
|
||||
<input
|
||||
type="file"
|
||||
id="dataset_path"
|
||||
ref="dataset_path"
|
||||
accept=".parquet"
|
||||
v-on:change="selectDatasetPath"
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<p class="mt-2 text-xs">Selected File: {{ selectedDatasetPath }}</p>
|
||||
<ClipBoardTextInput id="model_path" inputType="file" :value="dataset_path"/>
|
||||
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="max_length" class="text-sm">Max Length:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="max_length"
|
||||
v-model.number="max_length"
|
||||
required
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<ClipBoardTextInput id="model_path" inputType="integer" :value="max_length"/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="batch_size" class="text-sm">Batch Size:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="batch_size"
|
||||
v-model.number="batch_size"
|
||||
required
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<ClipBoardTextInput id="model_path" inputType="integer" :value="batch_size"/>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Train Dynamics -->
|
||||
<div class="mb-4">
|
||||
<label for="lr" class="text-sm">Learning Rate:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="lr"
|
||||
v-model.number="lr"
|
||||
required
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<ClipBoardTextInput id="model_path" inputType="integer" :value="lr"/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="num_epochs" class="text-sm">Number of Epochs:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="num_epochs"
|
||||
v-model.number="num_epochs"
|
||||
required
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<ClipBoardTextInput id="model_path" inputType="integer" :value="num_epochs"/>
|
||||
</div>
|
||||
|
||||
<!-- Logging -->
|
||||
<div class="mb-4">
|
||||
<label for="output_dir" class="text-sm">Output Directory:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="output_dir"
|
||||
v-model="selectedFolder"
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
placeholder="Enter or select the output folder"
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
id="folder_selector"
|
||||
ref="folder_selector"
|
||||
style="display: none"
|
||||
webkitdirectory
|
||||
v-on:change="selectOutputDirectory"
|
||||
>
|
||||
<button type="button" @click="openFolderSelector" class="bg-blue-500 text-white px-4 py-2 rounded">Select Folder</button>
|
||||
</div>
|
||||
<ClipBoardTextInput id="model_path" inputType="text" :value="output_dir" />
|
||||
</div>
|
||||
|
||||
<button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Train LLM</button>
|
||||
</form>
|
||||
@ -106,7 +49,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClipBoardTextInput from "@/components/ClipBoardTextInput.vue";
|
||||
export default {
|
||||
components: {
|
||||
ClipBoardTextInput,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
model_name: 'jondurbin/airoboros-7b-gpt4',
|
||||
@ -154,6 +101,13 @@
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
model_name(newVal) {
|
||||
// Watch for changes to model_name and propagate them to the child component
|
||||
console.log("watching model_name", newVal)
|
||||
this.$refs.clipboardInput.inputValue = newVal;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user