Merge branch 'staging' into feature/exorcism
This commit is contained in:
commit
194278d171
|
@ -0,0 +1,25 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
We take the security of this project seriously. If you discover any security vulnerabilities or have concerns regarding the security of this repository, please reach out to us immediately. We appreciate your efforts in responsibly disclosing the issue and will make every effort to address it promptly.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
To report a security vulnerability, please follow these steps:
|
||||||
|
|
||||||
|
1. Go to the **Security** tab of this repository on GitHub.
|
||||||
|
2. Click on **"Report a vulnerability"**.
|
||||||
|
3. Provide a clear description of the vulnerability and its potential impact. Be as detailed as possible.
|
||||||
|
4. If applicable, include steps or a PoC (Proof of Concept) to reproduce the vulnerability.
|
||||||
|
5. Submit the report.
|
||||||
|
|
||||||
|
Once we receive the private report notification, we will promptly investigate and assess the reported vulnerability.
|
||||||
|
|
||||||
|
Please do not disclose any potential vulnerabilities in public repositories, issue trackers, or forums until we have had a chance to review and address the issue.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This security policy applies to all the code and files within this repository and its dependencies actively maintained by us. If you encounter a security issue in a dependency that is not directly maintained by us, please follow responsible disclosure practices and report it to the respective project.
|
||||||
|
|
||||||
|
While we strive to ensure the security of this project, please note that there may be limitations on resources, response times, and mitigations.
|
||||||
|
|
||||||
|
Thank you for your help in making this project more secure.
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "ESNext",
|
||||||
|
"target": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"allowUmdGlobalAccess": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"**/node_modules/*"
|
||||||
|
]
|
||||||
|
}
|
|
@ -404,6 +404,7 @@
|
||||||
|
|
||||||
.widthFitContent {
|
.widthFitContent {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
min-width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flexGap5 {
|
.flexGap5 {
|
||||||
|
@ -416,4 +417,4 @@
|
||||||
|
|
||||||
.opacity1 {
|
.opacity1 {
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
139
public/i18n.json
139
public/i18n.json
|
@ -4,8 +4,9 @@
|
||||||
"ja-jp",
|
"ja-jp",
|
||||||
"ko-kr",
|
"ko-kr",
|
||||||
"ru-ru",
|
"ru-ru",
|
||||||
"it-it",
|
"it-it",
|
||||||
"nl-nl"
|
"nl-nl",
|
||||||
|
"es-spa"
|
||||||
],
|
],
|
||||||
"zh-cn": {
|
"zh-cn": {
|
||||||
"clickslidertips": "点击滑块右侧数字可手动输入",
|
"clickslidertips": "点击滑块右侧数字可手动输入",
|
||||||
|
@ -3483,5 +3484,135 @@
|
||||||
"Select this as default persona for the new chats.": "Selecteer dit als standaard persona voor de nieuwe chats.",
|
"Select this as default persona for the new chats.": "Selecteer dit als standaard persona voor de nieuwe chats.",
|
||||||
"Change persona image": "persona afbeelding wijzigen",
|
"Change persona image": "persona afbeelding wijzigen",
|
||||||
"Delete persona": "persona verwijderen"
|
"Delete persona": "persona verwijderen"
|
||||||
}
|
},
|
||||||
}
|
"es-spa": {
|
||||||
|
"clickslidertips": "Haz click en el número al lado de la barra \npara seleccionar un número manualmente.",
|
||||||
|
"kobldpresets": "Configuraciones de KoboldAI",
|
||||||
|
"guikoboldaisettings": "Configuración actual de la interfaz de KoboldAI",
|
||||||
|
"novelaipreserts": "Configuraciones de NovelAI",
|
||||||
|
"default": "Predeterminado",
|
||||||
|
"openaipresets": "Configuraciones de OpenAI",
|
||||||
|
"text gen webio(ooba) presets": "Configuraciones de WebUI(ooba)",
|
||||||
|
"response legth(tokens)": "Largo de la respuesta de la IA (en Tokens)",
|
||||||
|
"select": "Seleccionar",
|
||||||
|
"context size(tokens)": "Tamaño del contexto (en Tokens)",
|
||||||
|
"unlocked": "Desbloqueado",
|
||||||
|
"only select modls support context sizes greater than 2048 tokens. proceed only is you know you're doing": "Solo algunos modelos tienen soporte para tamaños de más de 2048 tokens. Procede solo si sabes lo que estás haciendo.",
|
||||||
|
"rep.pen": "Rep. Pen.",
|
||||||
|
"rep.pen range": "Rango de Rep. Pen.",
|
||||||
|
"temperature": "Temperature",
|
||||||
|
"Encoder Rep. Pen.": "Encoder Rep. Pen.",
|
||||||
|
"No Repeat Ngram Size": "No Repeat Ngram Size",
|
||||||
|
"Min Length": "Largo mínimo",
|
||||||
|
"OpenAI Reverse Proxy": "Reverse Proxy de OpenAI",
|
||||||
|
"Alternative server URL (leave empty to use the default value).": "URL del server alternativo (deja vacío para usar el predeterminado)",
|
||||||
|
"Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Borra tu clave(API) real de OpenAI ANTES de escribir nada en este campo.",
|
||||||
|
"We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "SillyTaven no puede dar soporte por problemas encontrados durante el uso de un proxy no-oficial de OpenAI",
|
||||||
|
"Legacy Streaming Processing": "Processo Streaming Legacy",
|
||||||
|
"Enable this if the streaming doesn't work with your proxy": "Habilita esta opción si el \"streaming\" no está funcionando.",
|
||||||
|
"Context Size (tokens)": "Tamaño del contexto (en Tokens)",
|
||||||
|
"Max Response Length (tokens)": "Tamaño máximo (en Tokens)",
|
||||||
|
"Temperature": "Temperatura",
|
||||||
|
"Frequency Penalty": "Frequency Penalty",
|
||||||
|
"Presence Penalty": "Presence Penalty",
|
||||||
|
"Top-p": "Top-p",
|
||||||
|
"Display bot response text chunks as they are generated": "Muestra el texto poco a poco al mismo tiempo que es generado.",
|
||||||
|
"Top A": "Top-a",
|
||||||
|
"Typical Sampling": "Typical Sampling",
|
||||||
|
"Tail Free Sampling": "Tail Free Sampling",
|
||||||
|
"Rep. Pen. Slope": "Rep. Pen. Slope",
|
||||||
|
"Single-line mode": "Modo \"Solo una línea\"",
|
||||||
|
"Top K": "Top-k",
|
||||||
|
"Top P": "Top-p",
|
||||||
|
"Do Sample": "Do Sample",
|
||||||
|
"Add BOS Token": "Añadir BOS Token",
|
||||||
|
"Add the bos_token to the beginning of prompts. Disabling this can make the replies more creative.": "Añade el \"bos_token\" al inicio del prompt. Desabilitar esto puede hacer las respuestas de la IA más creativas",
|
||||||
|
"Ban EOS Token": "Prohibir EOS Token",
|
||||||
|
"Ban the eos_token. This forces the model to never end the generation prematurely": "Prohibe el \"eos_token\". Esto obliga a la IA a no terminar su generación de forma prematura",
|
||||||
|
"Skip Special Tokens": "Saltarse Tokens Especiales",
|
||||||
|
"Beam search": "Beam Search",
|
||||||
|
"Number of Beams": "Number of Beams",
|
||||||
|
"Length Penalty": "Length Penalty",
|
||||||
|
"Early Stopping": "Early Stopping",
|
||||||
|
"Contrastive search": "Contrastive search",
|
||||||
|
"Penalty Alpha": "Penalty Alpha",
|
||||||
|
"Seed": "Seed",
|
||||||
|
"Inserts jailbreak as a last system message.": "Inserta el \"jailbreak\" como el último mensaje del Sistema",
|
||||||
|
"This tells the AI to ignore its usual content restrictions.": "Esto ayuda a la IA para ignorar sus restricciones de contenido",
|
||||||
|
"NSFW Encouraged": "Alentar \"NSFW\"",
|
||||||
|
"Tell the AI that NSFW is allowed.": "Le dice a la IA que el contenido NSFW (+18) está permitido",
|
||||||
|
"NSFW Prioritized": "Priorizar NSFW",
|
||||||
|
"NSFW prompt text goes first in the prompt to emphasize its effect.": "El \"prompt NSFW\" va antes para enfatizar su efecto",
|
||||||
|
"Streaming": "Streaming",
|
||||||
|
"Display the response bit by bit as it is generated.": "Enseña el texto poco a poco mientras es generado",
|
||||||
|
"When this is off, responses will be displayed all at once when they are complete.": "Cuando esto está deshabilitado, las respuestas se mostrarán de una vez cuando la generación se haya completado",
|
||||||
|
"Enhance Definitions": "Definiciones Mejoradas",
|
||||||
|
"Use OAI knowledge base to enhance definitions for public figures and known fictional characters": "Usa el conocimiento de OpenAI (GPT 3.5, GPT 4, ChatGPT) para mejorar las definiciones de figuras públicas y personajes ficticios",
|
||||||
|
"Wrap in Quotes": "Envolver En Comillas",
|
||||||
|
"Wrap entire user message in quotes before sending.": "Envuelve todo el mensaje en comillas antes de enviar",
|
||||||
|
"Leave off if you use quotes manually for speech.": "Déjalo deshabilitado si usas comillas manualmente para denotar diálogo",
|
||||||
|
"Main prompt": "Prompt Principal",
|
||||||
|
"The main prompt used to set the model behavior": "El prompt principal usado para definir el comportamiento de la IA",
|
||||||
|
"NSFW prompt": "Prompt NSFW",
|
||||||
|
"Prompt that is used when the NSFW toggle is on": "Prompt que es utilizado cuando \"Alentar NSFW\" está activado",
|
||||||
|
"Jailbreak prompt": "Jailbreak prompt",
|
||||||
|
"Prompt that is used when the Jailbreak toggle is on": "Prompt que es utilizado cuando Jailbreak Prompt está activado",
|
||||||
|
"Impersonation prompt": "Prompt \"Impersonar\"",
|
||||||
|
"Prompt that is used for Impersonation function": "Prompt que es utilizado para la función \"Impersonar\"",
|
||||||
|
"Logit Bias": "Logit Bias",
|
||||||
|
"Helps to ban or reenforce the usage of certain words": "Ayuda a prohibir o alentar el uso de algunas palabras",
|
||||||
|
"View / Edit bias preset": "Ver/Editar configuración de \"Logit Bias\"",
|
||||||
|
"Add bias entry": "Añadir bias",
|
||||||
|
"Jailbreak activation message": "Mensaje de activación de Jailbrak",
|
||||||
|
"Message to send when auto-jailbreak is on.": "Mensaje enviado cuando auto-jailbreak está activado",
|
||||||
|
"Jailbreak confirmation reply": "Mensaje de confirmación de Jailbreak",
|
||||||
|
"Bot must send this back to confirm jailbreak": "La IA debe enviar un mensaje para confirmar el jailbreak",
|
||||||
|
"Character Note": "Nota del personaje",
|
||||||
|
"Influences bot behavior in its responses": "Influencia el comportamiento de la IA y sus respuestas",
|
||||||
|
"API": "API",
|
||||||
|
"KoboldAI": "KoboldAI",
|
||||||
|
"Use Horde": "Usar AI Horde de KoboldAI",
|
||||||
|
"API url": "URL de la API",
|
||||||
|
"Register a Horde account for faster queue times": "Regístrate en KoboldAI para conseguir respuestas más rápido",
|
||||||
|
"Learn how to contribute your idle GPU cycles to the Hord": "Aprende cómo contribuir a AI Horde con tu GPU",
|
||||||
|
"Adjust context size to worker capabilities": "Ajustar tamaño del contexto a las capacidades del trabajador",
|
||||||
|
"Adjust response length to worker capabilities": "Ajustar tamaño de la respuesta a las capacidades del trabajador",
|
||||||
|
"API key": "API key",
|
||||||
|
"Register": "Registrarse",
|
||||||
|
"For privacy reasons": "Por motivos de privacidad, tu API será ocultada cuando se vuelva a cargar la página",
|
||||||
|
"Model": "Modelo IA",
|
||||||
|
"Hold Control / Command key to select multiple models.": "Presiona Ctrl/Command Key para seleccionar multiples modelos",
|
||||||
|
"Horde models not loaded": "Modelos del Horde no cargados",
|
||||||
|
"Not connected": "Desconectado",
|
||||||
|
"Novel API key": "API key de NovelAI",
|
||||||
|
"Follow": "Sigue",
|
||||||
|
"these directions": "estas instrucciones",
|
||||||
|
"to get your NovelAI API key.": "para conseguir tu NovelAI API key",
|
||||||
|
"Enter it in the box below": "Introduce tu NovelAI API key en el siguiente campo",
|
||||||
|
"Novel AI Model": "Modelo IA de NovelAI",
|
||||||
|
"Euterpe": "Euterpe",
|
||||||
|
"Krake": "Krake",
|
||||||
|
"No connection": "Desconectado",
|
||||||
|
"oobabooga/text-generation-webui": "oobabooga/text-generation-webui",
|
||||||
|
"Make sure you run it with": "Asegúrate de usar el argumento --api cuando se ejecute",
|
||||||
|
"Blocking API url": "API URL",
|
||||||
|
"Streaming API url": "Streaming API URL",
|
||||||
|
"to get your OpenAI API key.": "para conseguir tu clave API de OpenAI",
|
||||||
|
"OpenAI Model": "Modelo AI de OpenAI",
|
||||||
|
"View API Usage Metrics": "Ver métricas de uso de la API",
|
||||||
|
"Bot": "Bot",
|
||||||
|
"Auto-connect to Last Server": "Auto-conectarse con el último servidor",
|
||||||
|
"View hidden API keys": "Ver claves API ocultas",
|
||||||
|
"Advanced Formatting": "Formateo avanzado",
|
||||||
|
"AutoFormat Overrides": "Autoformateo de overrides",
|
||||||
|
"Samplers Order": "Orden de Samplers",
|
||||||
|
"Samplers will be applied in a top-down order. Use with caution.": "Los Samplers serán aplicados de orden superior a inferior. \nUsa con precaución",
|
||||||
|
"Load koboldcpp order": "Cargar el orden de koboldcpp",
|
||||||
|
"Repetition Penalty": "Repetition Penalty",
|
||||||
|
"Epsilon Cutoff": "Epsilon Cutoff",
|
||||||
|
"Eta Cutoff": "Eta Cutoff",
|
||||||
|
"Rep. Pen. Range.": "Rep. Pen. Range.",
|
||||||
|
"Rep. Pen. Freq.": "Rep. Pen. Freq.",
|
||||||
|
"Rep. Pen. Presence": "Rep. Pen. Presence."
|
||||||
|
}
|
||||||
|
}
|
|
@ -2099,8 +2099,8 @@
|
||||||
<h4 data-i18n="Context Template">
|
<h4 data-i18n="Context Template">
|
||||||
Context Template
|
Context Template
|
||||||
</h4>
|
</h4>
|
||||||
<div class="preset_buttons">
|
<div class="preset_buttons flex-container">
|
||||||
<select id="context_presets" data-preset-manager-for="context" class="flex1"></select>
|
<select id="context_presets" data-preset-manager-for="context" class="flex1 widthFitContent"></select>
|
||||||
<input type="file" hidden data-preset-manager-file="context" accept=".json, .settings">
|
<input type="file" hidden data-preset-manager-file="context" accept=".json, .settings">
|
||||||
<i id="context_set_default" class="menu_button fa-solid fa-heart" title="Auto-select this preset for Instruct Mode."></i>
|
<i id="context_set_default" class="menu_button fa-solid fa-heart" title="Auto-select this preset for Instruct Mode."></i>
|
||||||
<i data-newbie-hidden data-preset-manager-update="context" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i>
|
<i data-newbie-hidden data-preset-manager-update="context" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i>
|
||||||
|
@ -2926,6 +2926,10 @@
|
||||||
<input id="encode_tags" type="checkbox" />
|
<input id="encode_tags" type="checkbox" />
|
||||||
<span data-i18n="Show tags in responses">Show <tags> in responses</span>
|
<span data-i18n="Show tags in responses">Show <tags> in responses</span>
|
||||||
</label>
|
</label>
|
||||||
|
<label data-newbie-hidden class="checkbox_label" for="disable_group_trimming" title="Allow AI messages in groups to contain lines spoken by other group members.">
|
||||||
|
<input id="disable_group_trimming" type="checkbox" />
|
||||||
|
<span data-i18n="Show impersonated replies in groups">Show impersonated replies in groups</span>
|
||||||
|
</label>
|
||||||
<label data-newbie-hidden class="checkbox_label" for="console_log_prompts">
|
<label data-newbie-hidden class="checkbox_label" for="console_log_prompts">
|
||||||
<input id="console_log_prompts" type="checkbox" />
|
<input id="console_log_prompts" type="checkbox" />
|
||||||
<span data-i18n="Log prompts to console">Log prompts to console</span>
|
<span data-i18n="Log prompts to console">Log prompts to console</span>
|
||||||
|
@ -4322,6 +4326,9 @@
|
||||||
<i class="fa-lg fa-solid fa-note-sticky"></i>
|
<i class="fa-lg fa-solid fa-note-sticky"></i>
|
||||||
<span data-i18n="Author's Note">Author's Note</span>
|
<span data-i18n="Author's Note">Author's Note</span>
|
||||||
</a>
|
</a>
|
||||||
|
<div data-newbie-hidden id="options_advanced">
|
||||||
|
|
||||||
|
</div>
|
||||||
<a id="option_back_to_main">
|
<a id="option_back_to_main">
|
||||||
<i class="fa-lg fa-solid fa-left-long"></i>
|
<i class="fa-lg fa-solid fa-left-long"></i>
|
||||||
<span data-i18n="Back to parent chat">Back to parent chat</span>
|
<span data-i18n="Back to parent chat">Back to parent chat</span>
|
||||||
|
@ -4435,4 +4442,4 @@
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1895,7 +1895,17 @@ export function extractMessageBias(message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes impersonated group member lines from the group member messages.
|
||||||
|
* Doesn't do anything if group reply trimming is disabled.
|
||||||
|
* @param {string} getMessage Group message
|
||||||
|
* @returns Cleaned-up group message
|
||||||
|
*/
|
||||||
function cleanGroupMessage(getMessage) {
|
function cleanGroupMessage(getMessage) {
|
||||||
|
if (power_user.disable_group_trimming) {
|
||||||
|
return getMessage;
|
||||||
|
}
|
||||||
|
|
||||||
const group = groups.find((x) => x.id == selected_group);
|
const group = groups.find((x) => x.id == selected_group);
|
||||||
|
|
||||||
if (group && Array.isArray(group.members) && group.members) {
|
if (group && Array.isArray(group.members) && group.members) {
|
||||||
|
@ -2535,6 +2545,8 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||||
|
|
||||||
let examplesString = '';
|
let examplesString = '';
|
||||||
let chatString = '';
|
let chatString = '';
|
||||||
|
let cyclePrompt = '';
|
||||||
|
|
||||||
function getMessagesTokenCount() {
|
function getMessagesTokenCount() {
|
||||||
const encodeString = [
|
const encodeString = [
|
||||||
storyString,
|
storyString,
|
||||||
|
@ -2542,6 +2554,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||||
chatString,
|
chatString,
|
||||||
allAnchors,
|
allAnchors,
|
||||||
quiet_prompt,
|
quiet_prompt,
|
||||||
|
cyclePrompt,
|
||||||
].join('').replace(/\r/gm, '');
|
].join('').replace(/\r/gm, '');
|
||||||
return getTokenCount(encodeString, power_user.token_padding);
|
return getTokenCount(encodeString, power_user.token_padding);
|
||||||
}
|
}
|
||||||
|
@ -2552,7 +2565,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||||
pinExmString = examplesString = mesExamplesArray.join('');
|
pinExmString = examplesString = mesExamplesArray.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
let cyclePrompt = '';
|
|
||||||
if (isContinue) {
|
if (isContinue) {
|
||||||
cyclePrompt = chat2.shift();
|
cyclePrompt = chat2.shift();
|
||||||
}
|
}
|
||||||
|
@ -2801,11 +2813,11 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const anchorDepth = Math.abs(index - finalMesSend.length + 1);
|
const anchorDepth = Math.abs(index - finalMesSend.length);
|
||||||
// NOTE: Depth injected here!
|
// NOTE: Depth injected here!
|
||||||
const extensionAnchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, anchorDepth);
|
const extensionAnchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, anchorDepth);
|
||||||
|
|
||||||
if (anchorDepth > 0 && extensionAnchor && extensionAnchor.length) {
|
if (anchorDepth >= 0 && extensionAnchor && extensionAnchor.length) {
|
||||||
mesItem.extensionPrompts.push(extensionAnchor);
|
mesItem.extensionPrompts.push(extensionAnchor);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { callPopup, event_types, eventSource, is_send_press, main_api, substitut
|
||||||
import { is_group_generating } from "./group-chats.js";
|
import { is_group_generating } from "./group-chats.js";
|
||||||
import { TokenHandler } from "./openai.js";
|
import { TokenHandler } from "./openai.js";
|
||||||
import { power_user } from "./power-user.js";
|
import { power_user } from "./power-user.js";
|
||||||
import { debounce, waitUntilCondition } from "./utils.js";
|
import { debounce, waitUntilCondition, escapeHtml } from "./utils.js";
|
||||||
|
|
||||||
function debouncePromise(func, delay) {
|
function debouncePromise(func, delay) {
|
||||||
let timeoutId;
|
let timeoutId;
|
||||||
|
@ -1291,7 +1291,7 @@ PromptManagerModule.prototype.renderPromptManager = function () {
|
||||||
const prompts = [...this.serviceSettings.prompts]
|
const prompts = [...this.serviceSettings.prompts]
|
||||||
.filter(prompt => prompt && !prompt?.system_prompt)
|
.filter(prompt => prompt && !prompt?.system_prompt)
|
||||||
.sort((promptA, promptB) => promptA.name.localeCompare(promptB.name))
|
.sort((promptA, promptB) => promptA.name.localeCompare(promptB.name))
|
||||||
.reduce((acc, prompt) => acc + `<option value="${prompt.identifier}">${prompt.name}</option>`, '');
|
.reduce((acc, prompt) => acc + `<option value="${prompt.identifier}">${escapeHtml(prompt.name)}</option>`, '');
|
||||||
|
|
||||||
const footerHtml = `
|
const footerHtml = `
|
||||||
<div class="${this.configuration.prefix}prompt_manager_footer">
|
<div class="${this.configuration.prefix}prompt_manager_footer">
|
||||||
|
@ -1440,13 +1440,14 @@ PromptManagerModule.prototype.renderPromptManagerListItems = function () {
|
||||||
toggleSpanHtml = `<span class="fa-solid"></span>`;
|
toggleSpanHtml = `<span class="fa-solid"></span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const encodedName = escapeHtml(prompt.name);
|
||||||
listItemHtml += `
|
listItemHtml += `
|
||||||
<li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass}" data-pm-identifier="${prompt.identifier}">
|
<li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass}" data-pm-identifier="${prompt.identifier}">
|
||||||
<span class="${prefix}prompt_manager_prompt_name" data-pm-name="${prompt.name}">
|
<span class="${prefix}prompt_manager_prompt_name" data-pm-name="${encodedName}">
|
||||||
${prompt.marker ? '<span class="fa-solid fa-thumb-tack" title="Marker"></span>' : ''}
|
${prompt.marker ? '<span class="fa-solid fa-thumb-tack" title="Marker"></span>' : ''}
|
||||||
${!prompt.marker && prompt.system_prompt ? '<span class="fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
|
${!prompt.marker && prompt.system_prompt ? '<span class="fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
|
||||||
${!prompt.marker && !prompt.system_prompt ? '<span class="fa-solid fa-user" title="User Prompt"></span>' : ''}
|
${!prompt.marker && !prompt.system_prompt ? '<span class="fa-solid fa-user" title="User Prompt"></span>' : ''}
|
||||||
${this.isPromptInspectionAllowed(prompt) ? `<a class="prompt-manager-inspect-action">${prompt.name}</a>` : prompt.name}
|
${this.isPromptInspectionAllowed(prompt) ? `<a class="prompt-manager-inspect-action">${encodedName}</a>` : encodedName}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<span class="prompt_manager_prompt_controls">
|
<span class="prompt_manager_prompt_controls">
|
||||||
|
|
|
@ -1021,9 +1021,9 @@ export function initRossMods() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key == "Escape") { //closes various panels
|
if (event.key == "Escape") { //closes various panels
|
||||||
|
|
||||||
//dont override Escape hotkey functions from script.js
|
//dont override Escape hotkey functions from script.js
|
||||||
//"close edit box" and "cancel stream generation".
|
//"close edit box" and "cancel stream generation".
|
||||||
|
|
||||||
if ($("#curEditTextarea").is(":visible") || $("#mes_stop").is(":visible")) {
|
if ($("#curEditTextarea").is(":visible") || $("#mes_stop").is(":visible")) {
|
||||||
console.debug('escape key, but deferring to script.js routines')
|
console.debug('escape key, but deferring to script.js routines')
|
||||||
return
|
return
|
||||||
|
@ -1060,13 +1060,11 @@ export function initRossMods() {
|
||||||
.not('#left-nav-panel')
|
.not('#left-nav-panel')
|
||||||
.not('#right-nav-panel')
|
.not('#right-nav-panel')
|
||||||
.not('#floatingPrompt')
|
.not('#floatingPrompt')
|
||||||
console.log(visibleDrawerContent)
|
|
||||||
$(visibleDrawerContent).parent().find('.drawer-icon').trigger('click');
|
$(visibleDrawerContent).parent().find('.drawer-icon').trigger('click');
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($("#floatingPrompt").is(":visible")) {
|
if ($("#floatingPrompt").is(":visible")) {
|
||||||
console.log('saw AN visible, trying to close')
|
|
||||||
$("#ANClose").trigger('click');
|
$("#ANClose").trigger('click');
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1097,7 +1095,7 @@ export function initRossMods() {
|
||||||
|
|
||||||
|
|
||||||
if (event.ctrlKey && /^[1-9]$/.test(event.key)) {
|
if (event.ctrlKey && /^[1-9]$/.test(event.key)) {
|
||||||
// Your code here
|
// This will eventually be to trigger quick replies
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
console.log("Ctrl +" + event.key + " pressed!");
|
console.log("Ctrl +" + event.key + " pressed!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -385,7 +385,7 @@ jQuery(async () => {
|
||||||
|
|
||||||
const buttonHtml = $(await $.get(`${extensionFolderPath}/menuButton.html`));
|
const buttonHtml = $(await $.get(`${extensionFolderPath}/menuButton.html`));
|
||||||
buttonHtml.on('click', onCfgMenuItemClick)
|
buttonHtml.on('click', onCfgMenuItemClick)
|
||||||
buttonHtml.insertAfter("#option_toggle_AN");
|
buttonHtml.appendTo("#options_advanced");
|
||||||
|
|
||||||
// Hook events
|
// Hook events
|
||||||
eventSource.on(event_types.CHAT_CHANGED, async () => {
|
eventSource.on(event_types.CHAT_CHANGED, async () => {
|
||||||
|
|
|
@ -165,6 +165,7 @@ let power_user = {
|
||||||
continue_on_send: false,
|
continue_on_send: false,
|
||||||
trim_spaces: true,
|
trim_spaces: true,
|
||||||
relaxed_api_urls: false,
|
relaxed_api_urls: false,
|
||||||
|
disable_group_trimming: false,
|
||||||
|
|
||||||
default_instruct: '',
|
default_instruct: '',
|
||||||
instruct: {
|
instruct: {
|
||||||
|
@ -789,6 +790,7 @@ function loadPowerUserSettings(settings, data) {
|
||||||
$("#trim_sentences_checkbox").prop("checked", power_user.trim_sentences);
|
$("#trim_sentences_checkbox").prop("checked", power_user.trim_sentences);
|
||||||
$("#include_newline_checkbox").prop("checked", power_user.include_newline);
|
$("#include_newline_checkbox").prop("checked", power_user.include_newline);
|
||||||
$('#render_formulas').prop("checked", power_user.render_formulas);
|
$('#render_formulas').prop("checked", power_user.render_formulas);
|
||||||
|
$('#disable_group_trimming').prop("checked", power_user.disable_group_trimming);
|
||||||
$("#markdown_escape_strings").val(power_user.markdown_escape_strings);
|
$("#markdown_escape_strings").val(power_user.markdown_escape_strings);
|
||||||
$("#fast_ui_mode").prop("checked", power_user.fast_ui_mode);
|
$("#fast_ui_mode").prop("checked", power_user.fast_ui_mode);
|
||||||
$("#waifuMode").prop("checked", power_user.waifuMode);
|
$("#waifuMode").prop("checked", power_user.waifuMode);
|
||||||
|
@ -2160,6 +2162,11 @@ $(document).ready(() => {
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#disable_group_trimming').on('input', function () {
|
||||||
|
power_user.disable_group_trimming = !!$(this).prop('checked');
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
$('#debug_menu').on('click', function () {
|
$('#debug_menu').on('click', function () {
|
||||||
showDebugMenu();
|
showDebugMenu();
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,10 @@ export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> of <%= tot
|
||||||
*/
|
*/
|
||||||
export const navigation_option = { none: 0, previous: 1, last: 2, };
|
export const navigation_option = { none: 0, previous: 1, last: 2, };
|
||||||
|
|
||||||
|
export function escapeHtml(str) {
|
||||||
|
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if a value is unique in an array.
|
* Determines if a value is unique in an array.
|
||||||
* @param {any} value Current value.
|
* @param {any} value Current value.
|
||||||
|
|
382
server.js
382
server.js
|
@ -1,10 +1,77 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// native node modules
|
||||||
|
const child_process = require('child_process')
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const fs = require('fs');
|
||||||
|
const http = require("http");
|
||||||
|
const https = require('https');
|
||||||
|
const path = require('path');
|
||||||
|
const readline = require('readline');
|
||||||
|
const util = require('util');
|
||||||
|
const { Readable } = require('stream');
|
||||||
|
const { finished } = require('stream/promises');
|
||||||
|
const { TextEncoder, TextDecoder } = require('util');
|
||||||
|
|
||||||
|
// cli/fs related library imports
|
||||||
|
const commandExistsSync = require('command-exists').sync;
|
||||||
|
const open = require('open');
|
||||||
|
const sanitize = require('sanitize-filename');
|
||||||
|
const simpleGit = require('simple-git');
|
||||||
|
const writeFileAtomicSync = require('write-file-atomic').sync;
|
||||||
|
const yargs = require('yargs/yargs');
|
||||||
|
const { hideBin } = require('yargs/helpers');
|
||||||
|
|
||||||
|
// express/server related library imports
|
||||||
|
const cors = require('cors');
|
||||||
|
const doubleCsrf = require('csrf-csrf').doubleCsrf;
|
||||||
|
const express = require('express');
|
||||||
|
const compression = require('compression');
|
||||||
|
const cookieParser = require('cookie-parser');
|
||||||
|
const multer = require("multer");
|
||||||
|
const responseTime = require('response-time');
|
||||||
|
|
||||||
|
// net related library imports
|
||||||
|
const axios = require('axios');
|
||||||
|
const DeviceDetector = require("device-detector-js");
|
||||||
|
const fetch = require('node-fetch').default;
|
||||||
|
const ipaddr = require('ipaddr.js');
|
||||||
|
const ipMatching = require('ip-matching');
|
||||||
|
const json5 = require('json5');
|
||||||
|
const RESTClient = require('node-rest-client').Client;
|
||||||
|
const WebSocket = require('ws');
|
||||||
|
|
||||||
|
// image processing related library imports
|
||||||
|
const exif = require('piexifjs');
|
||||||
|
const encode = require('png-chunks-encode');
|
||||||
|
const extract = require('png-chunks-extract');
|
||||||
|
const jimp = require('jimp');
|
||||||
|
const mime = require('mime-types');
|
||||||
|
const PNGtext = require('png-chunk-text');
|
||||||
|
const webp = require('webp-converter');
|
||||||
|
const yauzl = require('yauzl');
|
||||||
|
|
||||||
|
// tokenizing related library imports
|
||||||
|
const { SentencePieceProcessor } = require("@agnai/sentencepiece-js");
|
||||||
|
const tiktoken = require('@dqbd/tiktoken');
|
||||||
|
const { Tokenizer } = require('@agnai/web-tokenizers');
|
||||||
|
|
||||||
|
// misc/other imports
|
||||||
|
const _ = require('lodash');
|
||||||
|
const { generateRequestUrl, normaliseResponse } = require('google-translate-api-browser');
|
||||||
|
|
||||||
|
// Create files before running anything else
|
||||||
createDefaultFiles();
|
createDefaultFiles();
|
||||||
|
|
||||||
|
// local library imports
|
||||||
|
const AIHorde = require("./src/horde");
|
||||||
|
const basicAuthMiddleware = require('./src/middleware/basicAuthMiddleware');
|
||||||
|
const characterCardParser = require('./src/character-card-parser.js');
|
||||||
|
const contentManager = require('./src/content-manager');
|
||||||
|
const novelai = require('./src/novelai');
|
||||||
|
const statsHelpers = require('./statsHelpers.js');
|
||||||
|
|
||||||
function createDefaultFiles() {
|
function createDefaultFiles() {
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const files = {
|
const files = {
|
||||||
settings: 'public/settings.json',
|
settings: 'public/settings.json',
|
||||||
bg_load: 'public/css/bg_load.css',
|
bg_load: 'public/css/bg_load.css',
|
||||||
|
@ -24,13 +91,9 @@ function createDefaultFiles() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const yargs = require('yargs/yargs');
|
|
||||||
const { hideBin } = require('yargs/helpers');
|
|
||||||
const net = require("net");
|
const net = require("net");
|
||||||
// work around a node v20 bug: https://github.com/nodejs/node/issues/47822#issuecomment-1564708870
|
// @ts-ignore work around a node v20 bug: https://github.com/nodejs/node/issues/47822#issuecomment-1564708870
|
||||||
if (net.setDefaultAutoSelectFamily) {
|
if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false);
|
||||||
net.setDefaultAutoSelectFamily(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const cliArguments = yargs(hideBin(process.argv))
|
const cliArguments = yargs(hideBin(process.argv))
|
||||||
.option('disableCsrf', {
|
.option('disableCsrf', {
|
||||||
|
@ -49,57 +112,21 @@ const cliArguments = yargs(hideBin(process.argv))
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'certs/privkey.pem',
|
default: 'certs/privkey.pem',
|
||||||
describe: 'Path to your private key file.'
|
describe: 'Path to your private key file.'
|
||||||
}).argv;
|
}).parseSync();
|
||||||
|
|
||||||
// change all relative paths
|
// change all relative paths
|
||||||
const path = require('path');
|
const directory = process['pkg'] ? path.dirname(process.execPath) : __dirname;
|
||||||
const directory = process.pkg ? path.dirname(process.execPath) : __dirname;
|
console.log(process['pkg'] ? 'Running from binary' : 'Running from source');
|
||||||
console.log(process.pkg ? 'Running from binary' : 'Running from source');
|
|
||||||
process.chdir(directory);
|
process.chdir(directory);
|
||||||
|
|
||||||
const express = require('express');
|
|
||||||
const compression = require('compression');
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const responseTime = require('response-time');
|
|
||||||
const simpleGit = require('simple-git');
|
|
||||||
|
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
app.use(responseTime());
|
app.use(responseTime());
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const writeFileAtomicSync = require('write-file-atomic').sync;
|
|
||||||
const readline = require('readline');
|
|
||||||
const open = require('open');
|
|
||||||
|
|
||||||
const multer = require("multer");
|
|
||||||
const http = require("http");
|
|
||||||
const https = require('https');
|
|
||||||
const basicAuthMiddleware = require('./src/middleware/basicAuthMiddleware');
|
|
||||||
const contentManager = require('./src/content-manager');
|
|
||||||
const extract = require('png-chunks-extract');
|
|
||||||
const encode = require('png-chunks-encode');
|
|
||||||
const PNGtext = require('png-chunk-text');
|
|
||||||
|
|
||||||
const jimp = require('jimp');
|
|
||||||
const sanitize = require('sanitize-filename');
|
|
||||||
const mime = require('mime-types');
|
|
||||||
|
|
||||||
const cookieParser = require('cookie-parser');
|
|
||||||
const crypto = require('crypto');
|
|
||||||
const ipaddr = require('ipaddr.js');
|
|
||||||
const json5 = require('json5');
|
|
||||||
|
|
||||||
const exif = require('piexifjs');
|
|
||||||
const webp = require('webp-converter');
|
|
||||||
const DeviceDetector = require("device-detector-js");
|
|
||||||
const { TextEncoder, TextDecoder } = require('util');
|
|
||||||
const utf8Encode = new TextEncoder();
|
const utf8Encode = new TextEncoder();
|
||||||
const commandExistsSync = require('command-exists').sync;
|
|
||||||
|
|
||||||
// impoort from statsHelpers.js
|
// impoort from statsHelpers.js
|
||||||
const statsHelpers = require('./statsHelpers.js');
|
|
||||||
|
|
||||||
const characterCardParser = require('./src/character-card-parser.js');
|
|
||||||
const config = require(path.join(process.cwd(), './config.conf'));
|
const config = require(path.join(process.cwd(), './config.conf'));
|
||||||
|
|
||||||
const server_port = process.env.SILLY_TAVERN_PORT || config.port;
|
const server_port = process.env.SILLY_TAVERN_PORT || config.port;
|
||||||
|
@ -120,25 +147,16 @@ const enableExtensions = config.enableExtensions;
|
||||||
const listen = config.listen;
|
const listen = config.listen;
|
||||||
const allowKeysExposure = config.allowKeysExposure;
|
const allowKeysExposure = config.allowKeysExposure;
|
||||||
|
|
||||||
const axios = require('axios');
|
|
||||||
const tiktoken = require('@dqbd/tiktoken');
|
|
||||||
const WebSocket = require('ws');
|
|
||||||
|
|
||||||
function getHordeClient() {
|
function getHordeClient() {
|
||||||
const AIHorde = require("./src/horde");
|
|
||||||
const ai_horde = new AIHorde({
|
const ai_horde = new AIHorde({
|
||||||
client_agent: getVersion()?.agent || 'SillyTavern:UNKNOWN:Cohee#1207',
|
client_agent: getVersion()?.agent || 'SillyTavern:UNKNOWN:Cohee#1207',
|
||||||
});
|
});
|
||||||
return ai_horde;
|
return ai_horde;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ipMatching = require('ip-matching');
|
const restClient = new RESTClient();
|
||||||
const yauzl = require('yauzl');
|
|
||||||
|
|
||||||
const Client = require('node-rest-client').Client;
|
restClient.on('error', (err) => {
|
||||||
const client = new Client();
|
|
||||||
|
|
||||||
client.on('error', (err) => {
|
|
||||||
console.error('An error occurred:', err);
|
console.error('An error occurred:', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -183,8 +201,6 @@ function get_mancer_headers() {
|
||||||
|
|
||||||
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
|
||||||
const { SentencePieceProcessor } = require("@agnai/sentencepiece-js");
|
|
||||||
const { Tokenizer } = require('@agnai/web-tokenizers');
|
|
||||||
const CHARS_PER_TOKEN = 3.35;
|
const CHARS_PER_TOKEN = 3.35;
|
||||||
|
|
||||||
let spp_llama;
|
let spp_llama;
|
||||||
|
@ -333,8 +349,6 @@ const directories = {
|
||||||
|
|
||||||
// CSRF Protection //
|
// CSRF Protection //
|
||||||
if (cliArguments.disableCsrf === false) {
|
if (cliArguments.disableCsrf === false) {
|
||||||
const doubleCsrf = require('csrf-csrf').doubleCsrf;
|
|
||||||
|
|
||||||
const CSRF_SECRET = crypto.randomBytes(8).toString('hex');
|
const CSRF_SECRET = crypto.randomBytes(8).toString('hex');
|
||||||
const COOKIES_SECRET = crypto.randomBytes(8).toString('hex');
|
const COOKIES_SECRET = crypto.randomBytes(8).toString('hex');
|
||||||
|
|
||||||
|
@ -352,7 +366,7 @@ if (cliArguments.disableCsrf === false) {
|
||||||
|
|
||||||
app.get("/csrf-token", (req, res) => {
|
app.get("/csrf-token", (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
"token": generateToken(res)
|
"token": generateToken(res, req)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -368,7 +382,6 @@ if (cliArguments.disableCsrf === false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CORS Settings //
|
// CORS Settings //
|
||||||
const cors = require('cors');
|
|
||||||
const CORS = cors({
|
const CORS = cors({
|
||||||
origin: 'null',
|
origin: 'null',
|
||||||
methods: ['OPTIONS']
|
methods: ['OPTIONS']
|
||||||
|
@ -385,7 +398,7 @@ function getIpFromRequest(req) {
|
||||||
let clientIp = req.connection.remoteAddress;
|
let clientIp = req.connection.remoteAddress;
|
||||||
let ip = ipaddr.parse(clientIp);
|
let ip = ipaddr.parse(clientIp);
|
||||||
// Check if the IP address is IPv4-mapped IPv6 address
|
// Check if the IP address is IPv4-mapped IPv6 address
|
||||||
if (ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) {
|
if (ip.kind() === 'ipv6' && ip instanceof ipaddr.IPv6 && ip.isIPv4MappedAddress()) {
|
||||||
const ipv4 = ip.toIPv4Address().toString();
|
const ipv4 = ip.toIPv4Address().toString();
|
||||||
clientIp = ipv4;
|
clientIp = ipv4;
|
||||||
} else {
|
} else {
|
||||||
|
@ -422,7 +435,7 @@ app.use(function (req, res, next) {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.use(express.static(process.cwd() + "/public", { refresh: true }));
|
app.use(express.static(process.cwd() + "/public", {}));
|
||||||
|
|
||||||
app.use('/backgrounds', (req, res) => {
|
app.use('/backgrounds', (req, res) => {
|
||||||
const filePath = decodeURIComponent(path.join(process.cwd(), 'public/backgrounds', req.url.replace(/%20/g, ' ')));
|
const filePath = decodeURIComponent(path.join(process.cwd(), 'public/backgrounds', req.url.replace(/%20/g, ' ')));
|
||||||
|
@ -456,7 +469,7 @@ app.get("/notes/*", function (request, response) {
|
||||||
app.get('/deviceinfo', function (request, response) {
|
app.get('/deviceinfo', function (request, response) {
|
||||||
const userAgent = request.header('user-agent');
|
const userAgent = request.header('user-agent');
|
||||||
const deviceDetector = new DeviceDetector();
|
const deviceDetector = new DeviceDetector();
|
||||||
const deviceInfo = deviceDetector.parse(userAgent);
|
const deviceInfo = deviceDetector.parse(userAgent || "");
|
||||||
return response.send(deviceInfo);
|
return response.send(deviceInfo);
|
||||||
});
|
});
|
||||||
app.get('/version', function (_, response) {
|
app.get('/version', function (_, response) {
|
||||||
|
@ -465,7 +478,7 @@ app.get('/version', function (_, response) {
|
||||||
})
|
})
|
||||||
|
|
||||||
//**************Kobold api
|
//**************Kobold api
|
||||||
app.post("/generate", jsonParser, async function (request, response_generate = response) {
|
app.post("/generate", jsonParser, async function (request, response_generate) {
|
||||||
if (!request.body) return response_generate.sendStatus(400);
|
if (!request.body) return response_generate.sendStatus(400);
|
||||||
|
|
||||||
const request_prompt = request.body.prompt;
|
const request_prompt = request.body.prompt;
|
||||||
|
@ -536,10 +549,9 @@ app.post("/generate", jsonParser, async function (request, response_generate = r
|
||||||
|
|
||||||
const MAX_RETRIES = 50;
|
const MAX_RETRIES = 50;
|
||||||
const delayAmount = 2500;
|
const delayAmount = 2500;
|
||||||
let fetch, url, response;
|
let url, response;
|
||||||
for (let i = 0; i < MAX_RETRIES; i++) {
|
for (let i = 0; i < MAX_RETRIES; i++) {
|
||||||
try {
|
try {
|
||||||
fetch = require('node-fetch').default;
|
|
||||||
url = request.body.streaming ? `${api_server}/extra/generate/stream` : `${api_server}/v1/generate`;
|
url = request.body.streaming ? `${api_server}/extra/generate/stream` : `${api_server}/v1/generate`;
|
||||||
response = await fetch(url, { method: 'POST', timeout: 0, ...args });
|
response = await fetch(url, { method: 'POST', timeout: 0, ...args });
|
||||||
|
|
||||||
|
@ -596,7 +608,7 @@ app.post("/generate", jsonParser, async function (request, response_generate = r
|
||||||
});
|
});
|
||||||
|
|
||||||
//************** Text generation web UI
|
//************** Text generation web UI
|
||||||
app.post("/generate_textgenerationwebui", jsonParser, async function (request, response_generate = response) {
|
app.post("/generate_textgenerationwebui", jsonParser, async function (request, response_generate) {
|
||||||
if (!request.body) return response_generate.sendStatus(400);
|
if (!request.body) return response_generate.sendStatus(400);
|
||||||
|
|
||||||
console.log(request.body);
|
console.log(request.body);
|
||||||
|
@ -610,6 +622,10 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
|
||||||
});
|
});
|
||||||
|
|
||||||
if (request.header('X-Response-Streaming')) {
|
if (request.header('X-Response-Streaming')) {
|
||||||
|
const streamingUrlHeader = request.header('X-Streaming-URL');
|
||||||
|
if (streamingUrlHeader === undefined) return response_generate.sendStatus(400);
|
||||||
|
const streamingUrl = streamingUrlHeader.replace("localhost", "127.0.0.1");
|
||||||
|
|
||||||
response_generate.writeHead(200, {
|
response_generate.writeHead(200, {
|
||||||
'Content-Type': 'text/plain;charset=utf-8',
|
'Content-Type': 'text/plain;charset=utf-8',
|
||||||
'Transfer-Encoding': 'chunked',
|
'Transfer-Encoding': 'chunked',
|
||||||
|
@ -617,7 +633,6 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
|
||||||
});
|
});
|
||||||
|
|
||||||
async function* readWebsocket() {
|
async function* readWebsocket() {
|
||||||
const streamingUrl = request.header('X-Streaming-URL').replace("localhost", "127.0.0.1");
|
|
||||||
const websocket = new WebSocket(streamingUrl);
|
const websocket = new WebSocket(streamingUrl);
|
||||||
|
|
||||||
websocket.on('open', async function () {
|
websocket.on('open', async function () {
|
||||||
|
@ -648,7 +663,7 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
|
||||||
websocket.once('error', reject);
|
websocket.once('error', reject);
|
||||||
websocket.once('message', (data, isBinary) => {
|
websocket.once('message', (data, isBinary) => {
|
||||||
websocket.removeListener('error', reject);
|
websocket.removeListener('error', reject);
|
||||||
resolve(data, isBinary);
|
resolve(data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -711,7 +726,7 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
|
||||||
console.log("Endpoint response:", data);
|
console.log("Endpoint response:", data);
|
||||||
return response_generate.send(data);
|
return response_generate.send(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
retval = { error: true, status: error.status, response: error.statusText };
|
let retval = { error: true, status: error.status, response: error.statusText };
|
||||||
console.log("Endpoint error:", error);
|
console.log("Endpoint error:", error);
|
||||||
try {
|
try {
|
||||||
retval.response = await error.json();
|
retval.response = await error.json();
|
||||||
|
@ -837,12 +852,12 @@ function getVersion() {
|
||||||
try {
|
try {
|
||||||
const pkgJson = require('./package.json');
|
const pkgJson = require('./package.json');
|
||||||
pkgVersion = pkgJson.version;
|
pkgVersion = pkgJson.version;
|
||||||
if (!process.pkg && commandExistsSync('git')) {
|
if (!process['pkg'] && commandExistsSync('git')) {
|
||||||
gitRevision = require('child_process')
|
gitRevision = child_process
|
||||||
.execSync('git rev-parse --short HEAD', { cwd: process.cwd(), stdio: ['ignore', 'pipe', 'ignore'] })
|
.execSync('git rev-parse --short HEAD', { cwd: process.cwd(), stdio: ['ignore', 'pipe', 'ignore'] })
|
||||||
.toString().trim();
|
.toString().trim();
|
||||||
|
|
||||||
gitBranch = require('child_process')
|
gitBranch = child_process
|
||||||
.execSync('git rev-parse --abbrev-ref HEAD', { cwd: process.cwd(), stdio: ['ignore', 'pipe', 'ignore'] })
|
.execSync('git rev-parse --abbrev-ref HEAD', { cwd: process.cwd(), stdio: ['ignore', 'pipe', 'ignore'] })
|
||||||
.toString().trim();
|
.toString().trim();
|
||||||
}
|
}
|
||||||
|
@ -887,13 +902,11 @@ function convertToV2(char) {
|
||||||
|
|
||||||
|
|
||||||
function unsetFavFlag(char) {
|
function unsetFavFlag(char) {
|
||||||
const _ = require('lodash');
|
|
||||||
_.set(char, 'fav', false);
|
_.set(char, 'fav', false);
|
||||||
_.set(char, 'data.extensions.fav', false);
|
_.set(char, 'data.extensions.fav', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function readFromV2(char) {
|
function readFromV2(char) {
|
||||||
const _ = require('lodash');
|
|
||||||
if (_.isUndefined(char.data)) {
|
if (_.isUndefined(char.data)) {
|
||||||
console.warn('Spec v2 data missing');
|
console.warn('Spec v2 data missing');
|
||||||
return char;
|
return char;
|
||||||
|
@ -948,16 +961,14 @@ function readFromV2(char) {
|
||||||
//***************** Main functions
|
//***************** Main functions
|
||||||
function charaFormatData(data) {
|
function charaFormatData(data) {
|
||||||
// This is supposed to save all the foreign keys that ST doesn't care about
|
// This is supposed to save all the foreign keys that ST doesn't care about
|
||||||
const _ = require('lodash');
|
|
||||||
const char = tryParse(data.json_data) || {};
|
const char = tryParse(data.json_data) || {};
|
||||||
|
|
||||||
// This function uses _.cond() to create a series of conditional checks that return the desired output based on the input data.
|
// Checks if data.alternate_greetings is an array, a string, or neither, and acts accordingly. (expected to be an array of strings)
|
||||||
// It checks if data.alternate_greetings is an array, a string, or neither, and acts accordingly.
|
const getAlternateGreetings = data => {
|
||||||
const getAlternateGreetings = data => _.cond([
|
if (Array.isArray(data.alternate_greetings)) return data.alternate_greetings
|
||||||
[d => Array.isArray(d.alternate_greetings), d => d.alternate_greetings],
|
if (typeof data.alternate_greetings === 'string') return [data.alternate_greetings]
|
||||||
[d => typeof d.alternate_greetings === 'string', d => [d.alternate_greetings]],
|
return []
|
||||||
[_.stubTrue, _.constant([])]
|
}
|
||||||
])(data);
|
|
||||||
|
|
||||||
// Spec V1 fields
|
// Spec V1 fields
|
||||||
_.set(char, 'name', data.ch_name);
|
_.set(char, 'name', data.ch_name);
|
||||||
|
@ -1087,9 +1098,10 @@ app.post("/renamecharacter", jsonParser, async function (request, response) {
|
||||||
const newChatsPath = path.join(chatsPath, newInternalName);
|
const newChatsPath = path.join(chatsPath, newInternalName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const _ = require('lodash');
|
|
||||||
// Read old file, replace name int it
|
// Read old file, replace name int it
|
||||||
const rawOldData = await charaRead(oldAvatarPath);
|
const rawOldData = await charaRead(oldAvatarPath);
|
||||||
|
if (rawOldData === false || rawOldData === undefined) throw new Error("Failed to read character file");
|
||||||
|
|
||||||
const oldData = getCharaCardV2(json5.parse(rawOldData));
|
const oldData = getCharaCardV2(json5.parse(rawOldData));
|
||||||
_.set(oldData, 'data.name', newName);
|
_.set(oldData, 'data.name', newName);
|
||||||
_.set(oldData, 'name', newName);
|
_.set(oldData, 'name', newName);
|
||||||
|
@ -1178,26 +1190,22 @@ app.post("/editcharacterattribute", jsonParser, async function (request, respons
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const avatarPath = path.join(charactersPath, request.body.avatar_url);
|
const avatarPath = path.join(charactersPath, request.body.avatar_url);
|
||||||
charaRead(avatarPath).then((char) => {
|
let charJSON = await charaRead(avatarPath);
|
||||||
char = JSON.parse(char);
|
if (typeof charJSON !== 'string') throw new Error("Failed to read character file");
|
||||||
//check if the field exists
|
|
||||||
if (char[request.body.field] === undefined && char.data[request.body.field] === undefined) {
|
let char = JSON.parse(charJSON)
|
||||||
console.error('Error: invalid field.');
|
//check if the field exists
|
||||||
response.status(400).send('Error: invalid field.');
|
if (char[request.body.field] === undefined && char.data[request.body.field] === undefined) {
|
||||||
return;
|
console.error('Error: invalid field.');
|
||||||
}
|
response.status(400).send('Error: invalid field.');
|
||||||
char[request.body.field] = request.body.value;
|
return;
|
||||||
char.data[request.body.field] = request.body.value;
|
}
|
||||||
char = JSON.stringify(char);
|
char[request.body.field] = request.body.value;
|
||||||
return { char };
|
char.data[request.body.field] = request.body.value;
|
||||||
}).then(({ char }) => {
|
let newCharJSON = JSON.stringify(char);
|
||||||
charaWrite(avatarPath, char, (request.body.avatar_url).replace('.png', ''), response, 'Character saved');
|
await charaWrite(avatarPath, newCharJSON, (request.body.avatar_url).replace('.png', ''), response, 'Character saved');
|
||||||
}).catch((err) => {
|
} catch (err) {
|
||||||
console.error('An error occured, character edit invalidated.', err);
|
console.error('An error occured, character edit invalidated.', err);
|
||||||
});
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
console.error('An error occured, character edit invalidated.');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1237,6 +1245,10 @@ app.post("/deletecharacter", jsonParser, async function (request, response) {
|
||||||
return response.sendStatus(200);
|
return response.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {express.Response | undefined} response
|
||||||
|
* @param {{file_name: string} | string} mes
|
||||||
|
*/
|
||||||
async function charaWrite(img_url, data, target_img, response = undefined, mes = 'ok', crop = undefined) {
|
async function charaWrite(img_url, data, target_img, response = undefined, mes = 'ok', crop = undefined) {
|
||||||
try {
|
try {
|
||||||
// Read the image, resize, and save it as a PNG into the buffer
|
// Read the image, resize, and save it as a PNG into the buffer
|
||||||
|
@ -1841,8 +1853,7 @@ function getImages(path) {
|
||||||
|
|
||||||
//***********Novel.ai API
|
//***********Novel.ai API
|
||||||
|
|
||||||
app.post("/getstatus_novelai", jsonParser, function (request, response_getstatus_novel = response) {
|
app.post("/getstatus_novelai", jsonParser, async function (request, response_getstatus_novel) {
|
||||||
|
|
||||||
if (!request.body) return response_getstatus_novel.sendStatus(400);
|
if (!request.body) return response_getstatus_novel.sendStatus(400);
|
||||||
const api_key_novel = readSecret(SECRET_KEYS.NOVEL);
|
const api_key_novel = readSecret(SECRET_KEYS.NOVEL);
|
||||||
|
|
||||||
|
@ -1850,27 +1861,30 @@ app.post("/getstatus_novelai", jsonParser, function (request, response_getstatus
|
||||||
return response_getstatus_novel.sendStatus(401);
|
return response_getstatus_novel.sendStatus(401);
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = {};
|
try {
|
||||||
var args = {
|
const response = await fetch(api_novelai + "/user/subscription", {
|
||||||
data: data,
|
method: 'GET',
|
||||||
headers: { "Content-Type": "application/json", "Authorization": "Bearer " + api_key_novel }
|
headers: {
|
||||||
};
|
'Content-Type': 'application/json',
|
||||||
client.get(api_novelai + "/user/subscription", args, function (data, response) {
|
'Authorization': "Bearer " + api_key_novel,
|
||||||
if (response.statusCode == 200) {
|
},
|
||||||
//console.log(data);
|
});
|
||||||
response_getstatus_novel.send(data);//data);
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
return response_getstatus_novel.send(data);
|
||||||
|
} else if (response.status == 401) {
|
||||||
|
console.log('NovelAI Access Token is incorrect.');
|
||||||
|
return response_getstatus_novel.send({ error: true });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (response.statusCode == 401) {
|
console.log('NovelAI returned an error:', response.statusText);
|
||||||
console.log('Access Token is incorrect.');
|
return response_getstatus_novel.send({ error: true });
|
||||||
}
|
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
response_getstatus_novel.send({ error: true });
|
|
||||||
}
|
}
|
||||||
}).on('error', function () {
|
} catch (error) {
|
||||||
response_getstatus_novel.send({ error: true });
|
console.log(error);
|
||||||
});
|
return response_getstatus_novel.send({ error: true });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/generate_novelai", jsonParser, async function (request, response_generate_novel = response) {
|
app.post("/generate_novelai", jsonParser, async function (request, response_generate_novel = response) {
|
||||||
|
@ -1888,7 +1902,6 @@ app.post("/generate_novelai", jsonParser, async function (request, response_gene
|
||||||
controller.abort();
|
controller.abort();
|
||||||
});
|
});
|
||||||
|
|
||||||
const novelai = require('./src/novelai');
|
|
||||||
const isNewModel = (request.body.model.includes('clio') || request.body.model.includes('kayra'));
|
const isNewModel = (request.body.model.includes('clio') || request.body.model.includes('kayra'));
|
||||||
const badWordsList = novelai.getBadWordsList(request.body.model);
|
const badWordsList = novelai.getBadWordsList(request.body.model);
|
||||||
|
|
||||||
|
@ -1943,7 +1956,7 @@ app.post("/generate_novelai", jsonParser, async function (request, response_gene
|
||||||
"order": request.body.order
|
"order": request.body.order
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const util = require('util');
|
|
||||||
console.log(util.inspect(data, { depth: 4 }))
|
console.log(util.inspect(data, { depth: 4 }))
|
||||||
|
|
||||||
const args = {
|
const args = {
|
||||||
|
@ -1953,7 +1966,6 @@ app.post("/generate_novelai", jsonParser, async function (request, response_gene
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fetch = require('node-fetch').default;
|
|
||||||
const url = request.body.streaming ? `${api_novelai}/ai/generate-stream` : `${api_novelai}/ai/generate`;
|
const url = request.body.streaming ? `${api_novelai}/ai/generate-stream` : `${api_novelai}/ai/generate`;
|
||||||
const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
|
const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
|
||||||
|
|
||||||
|
@ -2304,7 +2316,6 @@ app.post("/exportchat", jsonParser, async function (request, response) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const readline = require('readline');
|
|
||||||
const readStream = fs.createReadStream(filename);
|
const readStream = fs.createReadStream(filename);
|
||||||
const rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
input: readStream,
|
input: readStream,
|
||||||
|
@ -3105,7 +3116,7 @@ app.get('/thumbnail', jsonParser, async function (request, response) {
|
||||||
});
|
});
|
||||||
|
|
||||||
/* OpenAI */
|
/* OpenAI */
|
||||||
app.post("/getstatus_openai", jsonParser, function (request, response_getstatus_openai = response) {
|
app.post("/getstatus_openai", jsonParser, function (request, response_getstatus_openai) {
|
||||||
if (!request.body) return response_getstatus_openai.sendStatus(400);
|
if (!request.body) return response_getstatus_openai.sendStatus(400);
|
||||||
|
|
||||||
let api_url;
|
let api_url;
|
||||||
|
@ -3133,14 +3144,14 @@ app.post("/getstatus_openai", jsonParser, function (request, response_getstatus_
|
||||||
...headers,
|
...headers,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
client.get(api_url + "/models", args, function (data, response) {
|
restClient.get(api_url + "/models", args, function (data, response) {
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
response_getstatus_openai.send(data);
|
response_getstatus_openai.send(data);
|
||||||
if (request.body.use_openrouter) {
|
if (request.body.use_openrouter) {
|
||||||
let models = [];
|
let models = [];
|
||||||
data.data.forEach(model => {
|
data.data.forEach(model => {
|
||||||
const context_length = model.context_length;
|
const context_length = model.context_length;
|
||||||
const tokens_dollar = parseFloat(1 / (1000 * model.pricing.prompt));
|
const tokens_dollar = Number(1 / (1000 * model.pricing.prompt));
|
||||||
const tokens_rounded = (Math.round(tokens_dollar * 1000) / 1000).toFixed(0);
|
const tokens_rounded = (Math.round(tokens_dollar * 1000) / 1000).toFixed(0);
|
||||||
models[model.id] = {
|
models[model.id] = {
|
||||||
tokens_per_dollar: tokens_rounded + 'k',
|
tokens_per_dollar: tokens_rounded + 'k',
|
||||||
|
@ -3283,7 +3294,6 @@ function convertClaudePrompt(messages, addHumanPrefix, addAssistantPostfix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendScaleRequest(request, response) {
|
async function sendScaleRequest(request, response) {
|
||||||
const fetch = require('node-fetch').default;
|
|
||||||
|
|
||||||
const api_url = new URL(request.body.api_url_scale).toString();
|
const api_url = new URL(request.body.api_url_scale).toString();
|
||||||
const api_key_scale = readSecret(SECRET_KEYS.SCALE);
|
const api_key_scale = readSecret(SECRET_KEYS.SCALE);
|
||||||
|
@ -3397,7 +3407,6 @@ app.post("/generate_altscale", jsonParser, function (request, response_generate_
|
||||||
});
|
});
|
||||||
|
|
||||||
async function sendClaudeRequest(request, response) {
|
async function sendClaudeRequest(request, response) {
|
||||||
const fetch = require('node-fetch').default;
|
|
||||||
|
|
||||||
const api_url = new URL(request.body.reverse_proxy || api_claude).toString();
|
const api_url = new URL(request.body.reverse_proxy || api_claude).toString();
|
||||||
const api_key_claude = request.body.reverse_proxy ? request.body.proxy_password : readSecret(SECRET_KEYS.CLAUDE);
|
const api_key_claude = request.body.reverse_proxy ? request.body.proxy_password : readSecret(SECRET_KEYS.CLAUDE);
|
||||||
|
@ -3658,7 +3667,7 @@ app.post("/generate_openai", jsonParser, function (request, response_generate_op
|
||||||
makeRequest(config, response_generate_openai, request);
|
makeRequest(config, response_generate_openai, request);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/tokenize_openai", jsonParser, function (request, response_tokenize_openai = response) {
|
app.post("/tokenize_openai", jsonParser, function (request, response_tokenize_openai) {
|
||||||
if (!request.body) return response_tokenize_openai.sendStatus(400);
|
if (!request.body) return response_tokenize_openai.sendStatus(400);
|
||||||
|
|
||||||
let num_tokens = 0;
|
let num_tokens = 0;
|
||||||
|
@ -3766,7 +3775,7 @@ async function sendAI21Request(request, response) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.post("/tokenize_ai21", jsonParser, function (request, response_tokenize_ai21 = response) {
|
app.post("/tokenize_ai21", jsonParser, function (request, response_tokenize_ai21) {
|
||||||
if (!request.body) return response_tokenize_ai21.sendStatus(400);
|
if (!request.body) return response_tokenize_ai21.sendStatus(400);
|
||||||
const options = {
|
const options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -3973,7 +3982,6 @@ app.post("/tokenize_via_api", jsonParser, async function (request, response) {
|
||||||
// ** REST CLIENT ASYNC WRAPPERS **
|
// ** REST CLIENT ASYNC WRAPPERS **
|
||||||
|
|
||||||
async function postAsync(url, args) {
|
async function postAsync(url, args) {
|
||||||
const fetch = require('node-fetch').default;
|
|
||||||
const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
|
const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
@ -3986,7 +3994,7 @@ async function postAsync(url, args) {
|
||||||
|
|
||||||
function getAsync(url, args) {
|
function getAsync(url, args) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.get(url, args, (data, response) => {
|
restClient.get(url, args, (data, response) => {
|
||||||
if (response.statusCode >= 400) {
|
if (response.statusCode >= 400) {
|
||||||
reject(data);
|
reject(data);
|
||||||
}
|
}
|
||||||
|
@ -4063,23 +4071,24 @@ if (listen && !config.whitelistMode && !config.basicAuthMode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true === cliArguments.ssl)
|
if (true === cliArguments.ssl) {
|
||||||
https.createServer(
|
https.createServer(
|
||||||
{
|
{
|
||||||
cert: fs.readFileSync(cliArguments.certPath),
|
cert: fs.readFileSync(cliArguments.certPath),
|
||||||
key: fs.readFileSync(cliArguments.keyPath)
|
key: fs.readFileSync(cliArguments.keyPath)
|
||||||
}, app)
|
}, app)
|
||||||
.listen(
|
.listen(
|
||||||
tavernUrl.port || 443,
|
Number(tavernUrl.port) || 443,
|
||||||
tavernUrl.hostname,
|
tavernUrl.hostname,
|
||||||
setupTasks
|
setupTasks
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
http.createServer(app).listen(
|
http.createServer(app).listen(
|
||||||
tavernUrl.port || 80,
|
Number(tavernUrl.port) || 80,
|
||||||
tavernUrl.hostname,
|
tavernUrl.hostname,
|
||||||
setupTasks
|
setupTasks
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function convertWebp() {
|
async function convertWebp() {
|
||||||
const files = fs.readdirSync(directories.characters).filter(e => e.endsWith(".webp"));
|
const files = fs.readdirSync(directories.characters).filter(e => e.endsWith(".webp"));
|
||||||
|
@ -4190,7 +4199,7 @@ function migrateSecrets() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let modified = false;
|
let modified = false;
|
||||||
const fileContents = fs.readFileSync(SETTINGS_FILE);
|
const fileContents = fs.readFileSync(SETTINGS_FILE, 'utf8');
|
||||||
const settings = JSON.parse(fileContents);
|
const settings = JSON.parse(fileContents);
|
||||||
const oaiKey = settings?.api_key_openai;
|
const oaiKey = settings?.api_key_openai;
|
||||||
const hordeKey = settings?.horde_settings?.api_key;
|
const hordeKey = settings?.horde_settings?.api_key;
|
||||||
|
@ -4242,7 +4251,7 @@ app.post('/readsecretstate', jsonParser, (_, response) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fileContents = fs.readFileSync(SECRETS_FILE);
|
const fileContents = fs.readFileSync(SECRETS_FILE, 'utf8');
|
||||||
const secrets = JSON.parse(fileContents);
|
const secrets = JSON.parse(fileContents);
|
||||||
const state = {};
|
const state = {};
|
||||||
|
|
||||||
|
@ -4301,7 +4310,7 @@ app.post('/viewsecrets', jsonParser, async (_, response) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fileContents = fs.readFileSync(SECRETS_FILE);
|
const fileContents = fs.readFileSync(SECRETS_FILE, 'utf-8');
|
||||||
const secrets = JSON.parse(fileContents);
|
const secrets = JSON.parse(fileContents);
|
||||||
return response.send(secrets);
|
return response.send(secrets);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -4364,6 +4373,7 @@ app.post('/horde_generateimage', jsonParser, async (request, response) => {
|
||||||
{
|
{
|
||||||
sampler_name: request.body.sampler,
|
sampler_name: request.body.sampler,
|
||||||
hires_fix: request.body.enable_hr,
|
hires_fix: request.body.enable_hr,
|
||||||
|
// @ts-ignore - use_gfpgan param is not in the type definition, need to update to new ai_horde @ https://github.com/ZeldaFan0225/ai_horde/blob/main/index.ts
|
||||||
use_gfpgan: request.body.restore_faces,
|
use_gfpgan: request.body.restore_faces,
|
||||||
cfg_scale: request.body.scale,
|
cfg_scale: request.body.scale,
|
||||||
steps: request.body.steps,
|
steps: request.body.steps,
|
||||||
|
@ -4390,6 +4400,7 @@ app.post('/horde_generateimage', jsonParser, async (request, response) => {
|
||||||
|
|
||||||
if (check.done) {
|
if (check.done) {
|
||||||
const result = await ai_horde.getImageGenerationStatus(generation.id);
|
const result = await ai_horde.getImageGenerationStatus(generation.id);
|
||||||
|
if (result.generations === undefined) return response.sendStatus(500);
|
||||||
return response.send(result.generations[0].img);
|
return response.send(result.generations[0].img);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4452,8 +4463,6 @@ app.post('/libre_translate', jsonParser, async (request, response) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/google_translate', jsonParser, async (request, response) => {
|
app.post('/google_translate', jsonParser, async (request, response) => {
|
||||||
const { generateRequestUrl, normaliseResponse } = require('google-translate-api-browser');
|
|
||||||
|
|
||||||
const text = request.body.text;
|
const text = request.body.text;
|
||||||
const lang = request.body.lang;
|
const lang = request.body.lang;
|
||||||
|
|
||||||
|
@ -4499,7 +4508,6 @@ app.post('/deepl_translate', jsonParser, async (request, response) => {
|
||||||
|
|
||||||
console.log('Input text: ' + text);
|
console.log('Input text: ' + text);
|
||||||
|
|
||||||
const fetch = require('node-fetch').default;
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.append('text', text);
|
params.append('text', text);
|
||||||
params.append('target_lang', lang);
|
params.append('target_lang', lang);
|
||||||
|
@ -4545,7 +4553,6 @@ app.post('/novel_tts', jsonParser, async (request, response) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fetch = require('node-fetch').default;
|
|
||||||
const url = `${api_novelai}/ai/generate-voice?text=${encodeURIComponent(text)}&voice=-1&seed=${encodeURIComponent(voice)}&opus=false&version=v2`;
|
const url = `${api_novelai}/ai/generate-voice?text=${encodeURIComponent(text)}&voice=-1&seed=${encodeURIComponent(voice)}&opus=false&version=v2`;
|
||||||
const result = await fetch(url, {
|
const result = await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -4718,7 +4725,7 @@ app.post('/import_custom', jsonParser, async (request, response) => {
|
||||||
return response.sendStatus(404);
|
return response.sendStatus(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.set('Content-Type', result.fileType);
|
if (result.fileType) response.set('Content-Type', result.fileType)
|
||||||
response.set('Content-Disposition', `attachment; filename="${result.fileName}"`);
|
response.set('Content-Disposition', `attachment; filename="${result.fileName}"`);
|
||||||
response.set('X-Custom-Content-Type', chubParsed?.type);
|
response.set('X-Custom-Content-Type', chubParsed?.type);
|
||||||
return response.send(result.buffer);
|
return response.send(result.buffer);
|
||||||
|
@ -4729,8 +4736,6 @@ app.post('/import_custom', jsonParser, async (request, response) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function downloadChubLorebook(id) {
|
async function downloadChubLorebook(id) {
|
||||||
const fetch = require('node-fetch').default;
|
|
||||||
|
|
||||||
const result = await fetch('https://api.chub.ai/api/lorebooks/download', {
|
const result = await fetch('https://api.chub.ai/api/lorebooks/download', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
@ -4754,8 +4759,6 @@ async function downloadChubLorebook(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadChubCharacter(id) {
|
async function downloadChubCharacter(id) {
|
||||||
const fetch = require('node-fetch').default;
|
|
||||||
|
|
||||||
const result = await fetch('https://api.chub.ai/api/characters/download', {
|
const result = await fetch('https://api.chub.ai/api/characters/download', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
@ -4776,6 +4779,11 @@ async function downloadChubCharacter(id) {
|
||||||
return { buffer, fileName, fileType };
|
return { buffer, fileName, fileType };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {String} str
|
||||||
|
* @returns { { id: string, type: "character" | "lorebook" } | null }
|
||||||
|
*/
|
||||||
function parseChubUrl(str) {
|
function parseChubUrl(str) {
|
||||||
const splitStr = str.split('/');
|
const splitStr = str.split('/');
|
||||||
const length = splitStr.length;
|
const length = splitStr.length;
|
||||||
|
@ -4880,7 +4888,7 @@ function writeSecret(key, value) {
|
||||||
writeFileAtomicSync(SECRETS_FILE, emptyFile, "utf-8");
|
writeFileAtomicSync(SECRETS_FILE, emptyFile, "utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileContents = fs.readFileSync(SECRETS_FILE);
|
const fileContents = fs.readFileSync(SECRETS_FILE, 'utf-8');
|
||||||
const secrets = JSON.parse(fileContents);
|
const secrets = JSON.parse(fileContents);
|
||||||
secrets[key] = value;
|
secrets[key] = value;
|
||||||
writeFileAtomicSync(SECRETS_FILE, JSON.stringify(secrets), "utf-8");
|
writeFileAtomicSync(SECRETS_FILE, JSON.stringify(secrets), "utf-8");
|
||||||
|
@ -4891,7 +4899,7 @@ function readSecret(key) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileContents = fs.readFileSync(SECRETS_FILE);
|
const fileContents = fs.readFileSync(SECRETS_FILE, 'utf-8');
|
||||||
const secrets = JSON.parse(fileContents);
|
const secrets = JSON.parse(fileContents);
|
||||||
return secrets[key];
|
return secrets[key];
|
||||||
}
|
}
|
||||||
|
@ -4972,7 +4980,7 @@ async function getImageBuffers(zipFilePath) {
|
||||||
/**
|
/**
|
||||||
* This function extracts the extension information from the manifest file.
|
* This function extracts the extension information from the manifest file.
|
||||||
* @param {string} extensionPath - The path of the extension folder
|
* @param {string} extensionPath - The path of the extension folder
|
||||||
* @returns {Object} - Returns the manifest data as an object
|
* @returns {Promise<Object>} - Returns the manifest data as an object
|
||||||
*/
|
*/
|
||||||
async function getManifest(extensionPath) {
|
async function getManifest(extensionPath) {
|
||||||
const manifestPath = path.join(extensionPath, 'manifest.json');
|
const manifestPath = path.join(extensionPath, 'manifest.json');
|
||||||
|
@ -4987,6 +4995,7 @@ async function getManifest(extensionPath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkIfRepoIsUpToDate(extensionPath) {
|
async function checkIfRepoIsUpToDate(extensionPath) {
|
||||||
|
// @ts-ignore - simple-git types are incorrect, this is apparently callable but no call signature
|
||||||
const git = simpleGit();
|
const git = simpleGit();
|
||||||
await git.cwd(extensionPath).fetch('origin');
|
await git.cwd(extensionPath).fetch('origin');
|
||||||
const currentBranch = await git.cwd(extensionPath).branch();
|
const currentBranch = await git.cwd(extensionPath).branch();
|
||||||
|
@ -5017,6 +5026,7 @@ async function checkIfRepoIsUpToDate(extensionPath) {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
app.post('/get_extension', jsonParser, async (request, response) => {
|
app.post('/get_extension', jsonParser, async (request, response) => {
|
||||||
|
// @ts-ignore - simple-git types are incorrect, this is apparently callable but no call signature
|
||||||
const git = simpleGit();
|
const git = simpleGit();
|
||||||
if (!request.body.url) {
|
if (!request.body.url) {
|
||||||
return response.status(400).send('Bad Request: URL is required in the request body.');
|
return response.status(400).send('Bad Request: URL is required in the request body.');
|
||||||
|
@ -5062,6 +5072,7 @@ app.post('/get_extension', jsonParser, async (request, response) => {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
app.post('/update_extension', jsonParser, async (request, response) => {
|
app.post('/update_extension', jsonParser, async (request, response) => {
|
||||||
|
// @ts-ignore - simple-git types are incorrect, this is apparently callable but no call signature
|
||||||
const git = simpleGit();
|
const git = simpleGit();
|
||||||
if (!request.body.extensionName) {
|
if (!request.body.extensionName) {
|
||||||
return response.status(400).send('Bad Request: extensionName is required in the request body.');
|
return response.status(400).send('Bad Request: extensionName is required in the request body.');
|
||||||
|
@ -5107,6 +5118,7 @@ app.post('/update_extension', jsonParser, async (request, response) => {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
app.post('/get_extension_version', jsonParser, async (request, response) => {
|
app.post('/get_extension_version', jsonParser, async (request, response) => {
|
||||||
|
// @ts-ignore - simple-git types are incorrect, this is apparently callable but no call signature
|
||||||
const git = simpleGit();
|
const git = simpleGit();
|
||||||
if (!request.body.extensionName) {
|
if (!request.body.extensionName) {
|
||||||
return response.status(400).send('Bad Request: extensionName is required in the request body.');
|
return response.status(400).send('Bad Request: extensionName is required in the request body.');
|
||||||
|
@ -5249,16 +5261,15 @@ function checkAssetFileName(inputFilename) {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
app.post('/asset_download', jsonParser, async (request, response) => {
|
app.post('/asset_download', jsonParser, async (request, response) => {
|
||||||
const { Readable } = require('stream');
|
|
||||||
const { finished } = require('stream/promises');
|
|
||||||
const url = request.body.url;
|
const url = request.body.url;
|
||||||
const inputCategory = request.body.category;
|
const inputCategory = request.body.category;
|
||||||
const inputFilename = sanitize(request.body.filename);
|
const inputFilename = sanitize(request.body.filename);
|
||||||
const validCategories = ["bgm", "ambient"];
|
const validCategories = ["bgm", "ambient"];
|
||||||
|
const fetch = require('node-fetch').default;
|
||||||
|
|
||||||
// Check category
|
// Check category
|
||||||
let category = null;
|
let category = null;
|
||||||
for (i of validCategories)
|
for (let i of validCategories)
|
||||||
if (i == inputCategory)
|
if (i == inputCategory)
|
||||||
category = i;
|
category = i;
|
||||||
|
|
||||||
|
@ -5270,7 +5281,7 @@ app.post('/asset_download', jsonParser, async (request, response) => {
|
||||||
// Sanitize filename
|
// Sanitize filename
|
||||||
const safe_input = checkAssetFileName(inputFilename);
|
const safe_input = checkAssetFileName(inputFilename);
|
||||||
if (safe_input == '')
|
if (safe_input == '')
|
||||||
return response.sendFile(400);
|
return response.sendStatus(400);
|
||||||
|
|
||||||
const temp_path = path.join(directories.assets, "temp", safe_input)
|
const temp_path = path.join(directories.assets, "temp", safe_input)
|
||||||
const file_path = path.join(directories.assets, category, safe_input)
|
const file_path = path.join(directories.assets, category, safe_input)
|
||||||
|
@ -5278,23 +5289,19 @@ app.post('/asset_download', jsonParser, async (request, response) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Download to temp
|
// Download to temp
|
||||||
const downloadFile = (async (url, temp_path) => {
|
const res = await fetch(url);
|
||||||
const res = await fetch(url);
|
if (!res.ok || res.body === null) {
|
||||||
if (!res.ok) {
|
throw new Error(`Unexpected response ${res.statusText}`);
|
||||||
throw new Error(`Unexpected response ${res.statusText}`);
|
}
|
||||||
}
|
const destination = path.resolve(temp_path);
|
||||||
const destination = path.resolve(temp_path);
|
// Delete if previous download failed
|
||||||
// Delete if previous download failed
|
if (fs.existsSync(temp_path)) {
|
||||||
if (fs.existsSync(temp_path)) {
|
fs.unlink(temp_path, (err) => {
|
||||||
fs.unlink(temp_path, (err) => {
|
if (err) throw err;
|
||||||
if (err) throw err;
|
});
|
||||||
});
|
}
|
||||||
}
|
const fileStream = fs.createWriteStream(destination, { flags: 'wx' });
|
||||||
const fileStream = fs.createWriteStream(destination, { flags: 'wx' });
|
await finished(res.body.pipe(fileStream));
|
||||||
await finished(Readable.fromWeb(res.body).pipe(fileStream));
|
|
||||||
});
|
|
||||||
|
|
||||||
await downloadFile(url, temp_path);
|
|
||||||
|
|
||||||
// Move into asset place
|
// Move into asset place
|
||||||
console.debug("Download finished, moving file from", temp_path, "to", file_path);
|
console.debug("Download finished, moving file from", temp_path, "to", file_path);
|
||||||
|
@ -5316,15 +5323,13 @@ app.post('/asset_download', jsonParser, async (request, response) => {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
app.post('/asset_delete', jsonParser, async (request, response) => {
|
app.post('/asset_delete', jsonParser, async (request, response) => {
|
||||||
const { Readable } = require('stream');
|
|
||||||
const { finished } = require('stream/promises');
|
|
||||||
const inputCategory = request.body.category;
|
const inputCategory = request.body.category;
|
||||||
const inputFilename = sanitize(request.body.filename);
|
const inputFilename = sanitize(request.body.filename);
|
||||||
const validCategories = ["bgm", "ambient"];
|
const validCategories = ["bgm", "ambient"];
|
||||||
|
|
||||||
// Check category
|
// Check category
|
||||||
let category = null;
|
let category = null;
|
||||||
for (i of validCategories)
|
for (let i of validCategories)
|
||||||
if (i == inputCategory)
|
if (i == inputCategory)
|
||||||
category = i;
|
category = i;
|
||||||
|
|
||||||
|
@ -5336,7 +5341,7 @@ app.post('/asset_delete', jsonParser, async (request, response) => {
|
||||||
// Sanitize filename
|
// Sanitize filename
|
||||||
const safe_input = checkAssetFileName(inputFilename);
|
const safe_input = checkAssetFileName(inputFilename);
|
||||||
if (safe_input == '')
|
if (safe_input == '')
|
||||||
return response.sendFile(400);
|
return response.sendStatus(400);
|
||||||
|
|
||||||
const file_path = path.join(directories.assets, category, safe_input)
|
const file_path = path.join(directories.assets, category, safe_input)
|
||||||
console.debug("Request received to delete", category, file_path);
|
console.debug("Request received to delete", category, file_path);
|
||||||
|
@ -5373,13 +5378,14 @@ app.post('/asset_delete', jsonParser, async (request, response) => {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
app.post('/get_character_assets_list', jsonParser, async (request, response) => {
|
app.post('/get_character_assets_list', jsonParser, async (request, response) => {
|
||||||
const name = sanitize(request.query.name);
|
if (request.query.name === undefined) return response.sendStatus(400);
|
||||||
|
const name = sanitize(request.query.name.toString());
|
||||||
const inputCategory = request.query.category;
|
const inputCategory = request.query.category;
|
||||||
const validCategories = ["bgm", "ambient"]
|
const validCategories = ["bgm", "ambient"]
|
||||||
|
|
||||||
// Check category
|
// Check category
|
||||||
let category = null
|
let category = null
|
||||||
for (i of validCategories)
|
for (let i of validCategories)
|
||||||
if (i == inputCategory)
|
if (i == inputCategory)
|
||||||
category = i
|
category = i
|
||||||
|
|
||||||
|
@ -5398,7 +5404,7 @@ app.post('/get_character_assets_list', jsonParser, async (request, response) =>
|
||||||
return filename != ".placeholder";
|
return filename != ".placeholder";
|
||||||
});
|
});
|
||||||
|
|
||||||
for (i of files)
|
for (let i of files)
|
||||||
output.push(`/characters/${name}/${category}/${i}`);
|
output.push(`/characters/${name}/${category}/${i}`);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue