Merge pull request #858 from 50h100a/mancer-api

Mancer API
This commit is contained in:
Cohee
2023-08-03 23:06:53 +03:00
committed by GitHub
6 changed files with 133 additions and 34 deletions

View File

@@ -1555,30 +1555,55 @@
</div> </div>
</div> </div>
<div id="textgenerationwebui_api" style="display: none;position: relative;"> <div id="textgenerationwebui_api" style="display: none;position: relative;">
<div class="flex-container"> <form action="javascript:void(null);" method="post" enctype="multipart/form-data">
<a href="https://github.com/oobabooga/text-generation-webui" target="_blank"> If you are using:
oobabooga/text-generation-webui <div class="flex-container indent20p">
</a> <a href="https://github.com/oobabooga/text-generation-webui" target="_blank">
<span data-i18n="Make sure you run it with"> oobabooga/text-generation-webui
Make sure you run it with <tt>--api</tt> flag </a>,
</span> <span data-i18n="Make sure you run it with">
</div> Make sure you run it with <tt>--api</tt> flag
<div> </span>
<div class="flex-container flexFlowColumn"> </div>
<div class="flex1"> <div class="flex-container indent20p">
<h4 data-i18n="Blocking API url">Blocking API url</h4> <a href="https://mancer.tech/" target="_blank">
<small>Example: http://127.0.0.1:5000/</small> Mancer AI
<input id="textgenerationwebui_api_url_text" name="textgenerationwebui_api_url" class="text_pole wide100p" maxlength="500" value="" autocomplete="off"> </a>,
</div> <label class="checkbox_label" for="use-mancer-api-checkbox">
<div class="flex1"> <span data-i18n="Use API key (Only required for Mancer)">
<h4 data-i18n="Streaming API url">Streaming API url</h4> Click this box (and add your API key!):
<small>Example: ws://127.0.0.1:5005/api/v1/stream</small> </span>
<input id="streaming_url_textgenerationwebui" type="text" class="text_pole wide100p" maxlength="500" value="" autocomplete="off"> <input id="use-mancer-api-checkbox" type="checkbox" />
</label>
</div>
<div id="mancer-api-ui" style="display:none;">
<h4 data-i18n="Mancer API key">Mancer API key</h4>
<div class="flex-container">
<input id="api_key_mancer" name="api_key_mancer" class="text_pole flex1 wide100p" maxlength="500" size="35" type="text" autocomplete="off">
<div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_mancer">
</div>
</div> </div>
</div> </div>
<input id="api_button_textgenerationwebui" class="menu_button" type="submit" value="Connect"> <div>
<div id="api_loading_textgenerationwebui" class="api-load-icon fa-solid fa-hourglass fa-spin"></div> <div class="flex-container flexFlowColumn">
</div> <div data-for="api_key_mancer" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page.">
For privacy reasons, your API key will be hidden after you reload the page.
</div>
<div class="flex1">
<h4 data-i18n="Blocking API url">Blocking API url</h4>
<small>Example: http://127.0.0.1:5000/</small>
<input id="textgenerationwebui_api_url_text" name="textgenerationwebui_api_url" class="text_pole wide100p" maxlength="500" value="" autocomplete="off">
</div>
<div class="flex1">
<h4 data-i18n="Streaming API url">Streaming API url</h4>
<small>Example: ws://127.0.0.1:5005/api/v1/stream</small>
<input id="streaming_url_textgenerationwebui" type="text" class="text_pole wide100p" maxlength="500" value="" autocomplete="off">
</div>
</div>
<input id="api_button_textgenerationwebui" class="menu_button" type="submit" value="Connect">
<div id="api_loading_textgenerationwebui" class="api-load-icon fa-solid fa-hourglass fa-spin"></div>
</div>
</form>
<div class="online_status4"> <div class="online_status4">
<div class="online_status_indicator4"></div> <div class="online_status_indicator4"></div>
<div class="online_status_text4" data-i18n="Not connected">Not connected</div> <div class="online_status_text4" data-i18n="Not connected">Not connected</div>

View File

@@ -17,6 +17,7 @@ import {
loadTextGenSettings, loadTextGenSettings,
generateTextGenWithStreaming, generateTextGenWithStreaming,
getTextGenGenerationData, getTextGenGenerationData,
formatTextGenURL,
} from "./scripts/textgen-settings.js"; } from "./scripts/textgen-settings.js";
import { import {
@@ -721,6 +722,7 @@ let is_get_status = false;
let is_get_status_novel = false; let is_get_status_novel = false;
let is_api_button_press = false; let is_api_button_press = false;
let is_api_button_press_novel = false; let is_api_button_press_novel = false;
let api_use_mancer_webui = false;
let is_send_press = false; //Send generation let is_send_press = false; //Send generation
let add_mes_without_animation = false; let add_mes_without_animation = false;
@@ -854,9 +856,9 @@ async function getStatus() {
type: "POST", // type: "POST", //
url: "/getstatus", // url: "/getstatus", //
data: JSON.stringify({ data: JSON.stringify({
api_server: api_server: main_api == "kobold" ? api_server : api_server_textgenerationwebui,
main_api == "kobold" ? api_server : api_server_textgenerationwebui,
main_api: main_api, main_api: main_api,
use_mancer: main_api == "textgenerationwebui" ? api_use_mancer_webui : false,
}), }),
beforeSend: function () { }, beforeSend: function () { },
cache: false, cache: false,
@@ -883,6 +885,11 @@ async function getStatus() {
kai_settings.can_use_streaming = canUseKoboldStreaming(data.koboldVersion); kai_settings.can_use_streaming = canUseKoboldStreaming(data.koboldVersion);
} }
// We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
if (online_status == "no_connection" && data.response) {
toastr.error(data.response, "API Error", {timeOut: 5000, preventDuplicates:true})
}
//console.log(online_status); //console.log(online_status);
resultCheckStatus(); resultCheckStatus();
if (online_status !== "no_connection") { if (online_status !== "no_connection") {
@@ -2716,6 +2723,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
} }
else if (main_api == 'textgenerationwebui') { else if (main_api == 'textgenerationwebui') {
generate_data = getTextGenGenerationData(finalPromt, this_amount_gen, isImpersonate); generate_data = getTextGenGenerationData(finalPromt, this_amount_gen, isImpersonate);
generate_data.use_mancer = api_use_mancer_webui;
} }
else if (main_api == 'novel') { else if (main_api == 'novel') {
const this_settings = novelai_settings[novelai_setting_names[nai_settings.preset_settings_novel]]; const this_settings = novelai_settings[novelai_setting_names[nai_settings.preset_settings_novel]];
@@ -2978,6 +2986,13 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
activateSendButtons(); activateSendButtons();
//console.log('runGenerate calling showSwipeBtns'); //console.log('runGenerate calling showSwipeBtns');
showSwipeButtons(); showSwipeButtons();
if (main_api == 'textgenerationwebui' && api_use_mancer_webui) {
const errorText = `<h3>Inferencer endpoint is unhappy!</h3>
Returned status <tt>${data.status}</tt> with the reason:<br/>
${data.response}`;
callPopup(errorText, 'text');
}
} }
console.debug('/savechat called by /Generate'); console.debug('/savechat called by /Generate');
@@ -5088,11 +5103,13 @@ async function getSettings(type) {
setWorldInfoSettings(settings, data); setWorldInfoSettings(settings, data);
api_server_textgenerationwebui = api_server_textgenerationwebui = settings.api_server_textgenerationwebui;
settings.api_server_textgenerationwebui;
$("#textgenerationwebui_api_url_text").val( $("#textgenerationwebui_api_url_text").val(
api_server_textgenerationwebui api_server_textgenerationwebui
); );
api_use_mancer_webui = settings.api_use_mancer_webui
$('#use-mancer-api-checkbox').prop("checked", api_use_mancer_webui);
$('#use-mancer-api-checkbox').trigger("change");
selected_button = settings.selected_button; selected_button = settings.selected_button;
@@ -5126,6 +5143,7 @@ async function saveSettings(type) {
active_group: active_group, active_group: active_group,
api_server: api_server, api_server: api_server,
api_server_textgenerationwebui: api_server_textgenerationwebui, api_server_textgenerationwebui: api_server_textgenerationwebui,
api_use_mancer_webui: api_use_mancer_webui,
preset_settings: preset_settings, preset_settings: preset_settings,
user_avatar: user_avatar, user_avatar: user_avatar,
amount_gen: amount_gen, amount_gen: amount_gen,
@@ -7703,16 +7721,28 @@ $(document).ready(function () {
} }
}); });
$("#api_button_textgenerationwebui").click(function (e) { $("#use-mancer-api-checkbox").on("change", function (e) {
const enabled = $("#use-mancer-api-checkbox").prop("checked");
$("#mancer-api-ui").toggle(enabled);
api_use_mancer_webui = enabled;
saveSettingsDebounced();
getStatus();
});
$("#api_button_textgenerationwebui").click(async function (e) {
e.stopPropagation(); e.stopPropagation();
if ($("#textgenerationwebui_api_url_text").val() != "") { if ($("#textgenerationwebui_api_url_text").val() != "") {
let value = formatKoboldUrl($("#textgenerationwebui_api_url_text").val().trim()); let value = formatTextGenURL($("#textgenerationwebui_api_url_text").val().trim())
if (!value) { if (!value) {
callPopup('Please enter a valid URL.', 'text'); callPopup('Please enter a valid URL.<br/>WebUI URLs should end with <tt>/api</tt>', 'text');
return; return;
} }
const mancer_key = $("#api_key_mancer").val().trim();
if (mancer_key.length) {
await writeSecret(SECRET_KEYS.MANCER, mancer_key);
}
$("#textgenerationwebui_api_url_text").val(value); $("#textgenerationwebui_api_url_text").val(value);
$("#api_loading_textgenerationwebui").css("display", "inline-block"); $("#api_loading_textgenerationwebui").css("display", "inline-block");
$("#api_button_textgenerationwebui").css("display", "none"); $("#api_button_textgenerationwebui").css("display", "none");

View File

@@ -2,6 +2,7 @@ import { callPopup, getRequestHeaders } from "../script.js";
export const SECRET_KEYS = { export const SECRET_KEYS = {
HORDE: 'api_key_horde', HORDE: 'api_key_horde',
MANCER: 'api_key_mancer',
OPENAI: 'api_key_openai', OPENAI: 'api_key_openai',
NOVEL: 'api_key_novel', NOVEL: 'api_key_novel',
CLAUDE: 'api_key_claude', CLAUDE: 'api_key_claude',
@@ -11,6 +12,7 @@ export const SECRET_KEYS = {
const INPUT_MAP = { const INPUT_MAP = {
[SECRET_KEYS.HORDE]: '#horde_api_key', [SECRET_KEYS.HORDE]: '#horde_api_key',
[SECRET_KEYS.MANCER]: '#api_key_mancer',
[SECRET_KEYS.OPENAI]: '#api_key_openai', [SECRET_KEYS.OPENAI]: '#api_key_openai',
[SECRET_KEYS.NOVEL]: '#api_key_novel', [SECRET_KEYS.NOVEL]: '#api_key_novel',
[SECRET_KEYS.CLAUDE]: '#api_key_claude', [SECRET_KEYS.CLAUDE]: '#api_key_claude',

View File

@@ -10,6 +10,7 @@ export {
textgenerationwebui_settings, textgenerationwebui_settings,
loadTextGenSettings, loadTextGenSettings,
generateTextGenWithStreaming, generateTextGenWithStreaming,
formatTextGenURL,
} }
const textgenerationwebui_settings = { const textgenerationwebui_settings = {
@@ -94,6 +95,16 @@ function selectPreset(name) {
saveSettingsDebounced(); saveSettingsDebounced();
} }
function formatTextGenURL(value) {
try {
const url = new URL(value);
if (url.pathname.endsWith('/api')) {
return url.toString();
}
} catch { } // Try and Catch both fall through to the same return.
return null;
}
function convertPresets(presets) { function convertPresets(presets) {
return Array.isArray(presets) ? presets.map(JSON.parse) : []; return Array.isArray(presets) ? presets.map(JSON.parse) : [];
} }

View File

@@ -4422,6 +4422,10 @@ toolcool-color-picker {
width: 50px; width: 50px;
} }
.indent20p {
margin-left: 20px;
}
.wi-enter-footer-text { .wi-enter-footer-text {
font-size: calc(var(--mainFontSize) * 0.8); font-size: calc(var(--mainFontSize) * 0.8);
color: var(--SmartThemeBodyColor); color: var(--SmartThemeBodyColor);

View File

@@ -146,6 +146,14 @@ let response_getstatus;
let first_run = true; let first_run = true;
function get_mancer_headers() {
const api_key_mancer = readSecret(SECRET_KEYS.MANCER);
return api_key_mancer ? { "X-API-KEY": api_key_mancer} : {};
}
//RossAscends: Added function to format dates used in files and chat timestamps to a humanized format. //RossAscends: Added function to format dates used in files and chat timestamps to a humanized format.
//Mostly I wanted this to be for file names, but couldn't figure out exactly where the filename save code was as everything seemed to be connected. //Mostly I wanted this to be for file names, but couldn't figure out exactly where the filename save code was as everything seemed to be connected.
//During testing, this performs the same as previous date.now() structure. //During testing, this performs the same as previous date.now() structure.
@@ -640,13 +648,22 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
signal: controller.signal, signal: controller.signal,
}; };
if (request.body.use_mancer) {
args.headers = Object.assign(args.headers, get_mancer_headers());
}
try { try {
const data = await postAsync(api_server + "/v1/generate", args); const data = await postAsync(api_server + "/v1/generate", args);
console.log(data); console.log(data);
return response_generate.send(data); return response_generate.send(data);
} catch (error) { } catch (error) {
retval = { error: true, status: error.status, response: error.statusText };
console.log(error); console.log(error);
return response_generate.send({ error: true }); try {
retval.response = await error.json();
retval.response = retval.response.result;
} catch {}
return response_generate.send(retval);
} }
} }
}); });
@@ -710,6 +727,11 @@ app.post("/getstatus", jsonParser, async function (request, response_getstatus =
var args = { var args = {
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" }
}; };
if (main_api == 'textgenerationwebui' && request.body.use_mancer) {
args.headers = Object.assign(args.headers, get_mancer_headers());
}
var url = api_server + "/v1/model"; var url = api_server + "/v1/model";
let version = ''; let version = '';
let koboldVersion = {}; let koboldVersion = {};
@@ -730,18 +752,18 @@ app.post("/getstatus", jsonParser, async function (request, response_getstatus =
}; };
} }
} }
client.get(url, args, function (data, response) { client.get(url, args, async function (data, response) {
if (typeof data !== 'object') { if (typeof data !== 'object') {
data = {}; data = {};
} }
if (response.statusCode == 200) { if (response.statusCode == 200) {
data.version = version; data.version = version;
data.koboldVersion = koboldVersion; data.koboldVersion = koboldVersion;
if (data.result != "ReadOnly") { if (data.result == "ReadOnly") {
} else {
data.result = "no_connection"; data.result = "no_connection";
} }
} else { } else {
data.response = data.result;
data.result = "no_connection"; data.result = "no_connection";
} }
response_getstatus.send(data); response_getstatus.send(data);
@@ -3454,6 +3476,10 @@ app.post("/tokenize_via_api", jsonParser, async function (request, response) {
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" }
}; };
if (main_api == 'textgenerationwebui' && request.body.use_mancer) {
args.headers = Object.assign(args.headers, get_mancer_headers());
}
const data = await postAsync(api_server + "/v1/token-count", args); const data = await postAsync(api_server + "/v1/token-count", args);
console.log(data); console.log(data);
return response.send({ count: data['results'][0]['tokens'] }); return response.send({ count: data['results'][0]['tokens'] });
@@ -3659,6 +3685,7 @@ const SECRETS_FILE = './secrets.json';
const SETTINGS_FILE = './public/settings.json'; const SETTINGS_FILE = './public/settings.json';
const SECRET_KEYS = { const SECRET_KEYS = {
HORDE: 'api_key_horde', HORDE: 'api_key_horde',
MANCER: 'api_key_mancer',
OPENAI: 'api_key_openai', OPENAI: 'api_key_openai',
NOVEL: 'api_key_novel', NOVEL: 'api_key_novel',
CLAUDE: 'api_key_claude', CLAUDE: 'api_key_claude',