Text Generation: Add TabbyAPI support
TabbyAPI is an exllamav2 only API server that aims to provide a simple experience for loading and chatting with exl2 models. SillyTavern currently doesn't have the ability to load and unload models, so only add the OAI compatible completion endpoints. The repository can be found here: https://github.com/theroyallab/tabbyAPI Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
323b338cdd
commit
f31b996cb5
|
@ -1599,6 +1599,7 @@
|
||||||
<option value="ooba">Default (oobabooga)</option>
|
<option value="ooba">Default (oobabooga)</option>
|
||||||
<option value="mancer">Mancer</option>
|
<option value="mancer">Mancer</option>
|
||||||
<option value="aphrodite">Aphrodite</option>
|
<option value="aphrodite">Aphrodite</option>
|
||||||
|
<option value="tabby">TabbyAPI</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div data-tg-type="mancer" class="flex-container flexFlowColumn">
|
<div data-tg-type="mancer" class="flex-container flexFlowColumn">
|
||||||
|
@ -1659,8 +1660,29 @@
|
||||||
<input id="aphrodite_api_url_text" class="text_pole wide100p" maxlength="500" value="" autocomplete="off" data-server-history="aphrodite">
|
<input id="aphrodite_api_url_text" class="text_pole wide100p" maxlength="500" value="" autocomplete="off" data-server-history="aphrodite">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div data-tg-type="tabby">
|
||||||
|
<div class="flex-container flexFlowColumn">
|
||||||
|
<a href="https://github.com/theroyallab/tabbyAPI" target="_blank">
|
||||||
|
theroyallab/tabbyAPI
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<h4 data-i18n="Tabby API key">Tabby API key</h4>
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<div id="api_button_textgenerationwebui" class="api_button menu_button" type="submit" data-i18n="Connect" data-server-connect="ooba_blocking,aphrodite">Connect</div>
|
<input id="api_key_tabby" name="api_key_tabby" 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_tabby">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div data-for="api_key_tabby" 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="API url">API URL</h4>
|
||||||
|
<small data-i18n="Example: http://127.0.0.1:5000">Example: http://127.0.0.1:5000</small>
|
||||||
|
<input id="tabby_api_url_text" class="text_pole wide100p" maxlength="500" value="" autocomplete="off" data-server-history="tabby">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div id="api_button_textgenerationwebui" class="api_button menu_button" type="submit" data-i18n="Connect" data-server-connect="ooba_blocking,aphrodite,tabby">Connect</div>
|
||||||
<div class="api_loading menu_button" data-i18n="Cancel">Cancel</div>
|
<div class="api_loading menu_button" data-i18n="Cancel">Cancel</div>
|
||||||
</div>
|
</div>
|
||||||
<label class="checkbox_label margin-bot-10px" for="legacy_api_textgenerationwebui">
|
<label class="checkbox_label margin-bot-10px" for="legacy_api_textgenerationwebui">
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
getTextGenUrlSourceId,
|
getTextGenUrlSourceId,
|
||||||
isMancer,
|
isMancer,
|
||||||
isAphrodite,
|
isAphrodite,
|
||||||
|
isTabby,
|
||||||
textgen_types,
|
textgen_types,
|
||||||
textgenerationwebui_banned_in_macros,
|
textgenerationwebui_banned_in_macros,
|
||||||
isOoba,
|
isOoba,
|
||||||
|
@ -882,6 +883,7 @@ async function getStatus() {
|
||||||
use_mancer: main_api == "textgenerationwebui" ? isMancer() : false,
|
use_mancer: main_api == "textgenerationwebui" ? isMancer() : false,
|
||||||
use_aphrodite: main_api == "textgenerationwebui" ? isAphrodite() : false,
|
use_aphrodite: main_api == "textgenerationwebui" ? isAphrodite() : false,
|
||||||
use_ooba: main_api == "textgenerationwebui" ? isOoba() : false,
|
use_ooba: main_api == "textgenerationwebui" ? isOoba() : false,
|
||||||
|
use_tabby: main_api == "textgenerationwebui" ? isTabby() : false,
|
||||||
legacy_api: main_api == "textgenerationwebui" ? textgenerationwebui_settings.legacy_api && !isMancer() : false,
|
legacy_api: main_api == "textgenerationwebui" ? textgenerationwebui_settings.legacy_api && !isMancer() : false,
|
||||||
}),
|
}),
|
||||||
signal: abortStatusCheck.signal,
|
signal: abortStatusCheck.signal,
|
||||||
|
@ -5411,6 +5413,7 @@ async function getSettings() {
|
||||||
api_server_textgenerationwebui = settings.api_server_textgenerationwebui;
|
api_server_textgenerationwebui = settings.api_server_textgenerationwebui;
|
||||||
$("#textgenerationwebui_api_url_text").val(api_server_textgenerationwebui);
|
$("#textgenerationwebui_api_url_text").val(api_server_textgenerationwebui);
|
||||||
$("#aphrodite_api_url_text").val(api_server_textgenerationwebui);
|
$("#aphrodite_api_url_text").val(api_server_textgenerationwebui);
|
||||||
|
$("#tabby_api_url_text").val(api_server_textgenerationwebui);
|
||||||
|
|
||||||
selected_button = settings.selected_button;
|
selected_button = settings.selected_button;
|
||||||
|
|
||||||
|
@ -8008,6 +8011,11 @@ jQuery(async function () {
|
||||||
await writeSecret(SECRET_KEYS.APHRODITE, aphroditeKey);
|
await writeSecret(SECRET_KEYS.APHRODITE, aphroditeKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tabbyKey = String($("#api_key_tabby").val()).trim();
|
||||||
|
if (tabbyKey.length) {
|
||||||
|
await writeSecret(SECRET_KEYS.TABBY, tabbyKey)
|
||||||
|
}
|
||||||
|
|
||||||
const urlSourceId = getTextGenUrlSourceId();
|
const urlSourceId = getTextGenUrlSourceId();
|
||||||
|
|
||||||
if (urlSourceId && $(urlSourceId).val() !== "") {
|
if (urlSourceId && $(urlSourceId).val() !== "") {
|
||||||
|
|
|
@ -4,6 +4,7 @@ export const SECRET_KEYS = {
|
||||||
HORDE: 'api_key_horde',
|
HORDE: 'api_key_horde',
|
||||||
MANCER: 'api_key_mancer',
|
MANCER: 'api_key_mancer',
|
||||||
APHRODITE: 'api_key_aphrodite',
|
APHRODITE: 'api_key_aphrodite',
|
||||||
|
TABBY: 'api_key_tabby',
|
||||||
OPENAI: 'api_key_openai',
|
OPENAI: 'api_key_openai',
|
||||||
NOVEL: 'api_key_novel',
|
NOVEL: 'api_key_novel',
|
||||||
CLAUDE: 'api_key_claude',
|
CLAUDE: 'api_key_claude',
|
||||||
|
@ -27,6 +28,7 @@ const INPUT_MAP = {
|
||||||
[SECRET_KEYS.SCALE_COOKIE]: '#scale_cookie',
|
[SECRET_KEYS.SCALE_COOKIE]: '#scale_cookie',
|
||||||
[SECRET_KEYS.PALM]: '#api_key_palm',
|
[SECRET_KEYS.PALM]: '#api_key_palm',
|
||||||
[SECRET_KEYS.APHRODITE]: '#api_key_aphrodite',
|
[SECRET_KEYS.APHRODITE]: '#api_key_aphrodite',
|
||||||
|
[SECRET_KEYS.TABBY]: '#api_key_tabby'
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearSecret() {
|
async function clearSecret() {
|
||||||
|
|
|
@ -28,6 +28,7 @@ export const textgen_types = {
|
||||||
OOBA: 'ooba',
|
OOBA: 'ooba',
|
||||||
MANCER: 'mancer',
|
MANCER: 'mancer',
|
||||||
APHRODITE: 'aphrodite',
|
APHRODITE: 'aphrodite',
|
||||||
|
TABBY: 'tabby'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Maybe let it be configurable in the future?
|
// Maybe let it be configurable in the future?
|
||||||
|
@ -283,6 +284,10 @@ export function isAphrodite() {
|
||||||
return textgenerationwebui_settings.type === textgen_types.APHRODITE;
|
return textgenerationwebui_settings.type === textgen_types.APHRODITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isTabby() {
|
||||||
|
return textgenerationwebui_settings.type === textgen_types.TABBY;
|
||||||
|
}
|
||||||
|
|
||||||
export function isOoba() {
|
export function isOoba() {
|
||||||
return textgenerationwebui_settings.type === textgen_types.OOBA;
|
return textgenerationwebui_settings.type === textgen_types.OOBA;
|
||||||
}
|
}
|
||||||
|
@ -293,6 +298,8 @@ export function getTextGenUrlSourceId() {
|
||||||
return "#textgenerationwebui_api_url_text";
|
return "#textgenerationwebui_api_url_text";
|
||||||
case textgen_types.APHRODITE:
|
case textgen_types.APHRODITE:
|
||||||
return "#aphrodite_api_url_text";
|
return "#aphrodite_api_url_text";
|
||||||
|
case textgen_types.TABBY:
|
||||||
|
return "#tabby_api_url_text";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,6 +558,7 @@ export function getTextGenGenerationData(finalPrompt, this_amount_gen, isImperso
|
||||||
'custom_token_bans': isAphrodite() ? toIntArray(getCustomTokenBans()) : getCustomTokenBans(),
|
'custom_token_bans': isAphrodite() ? toIntArray(getCustomTokenBans()) : getCustomTokenBans(),
|
||||||
'use_mancer': isMancer(),
|
'use_mancer': isMancer(),
|
||||||
'use_aphrodite': isAphrodite(),
|
'use_aphrodite': isAphrodite(),
|
||||||
|
'use_tabby': isTabby(),
|
||||||
'use_ooba': isOoba(),
|
'use_ooba': isOoba(),
|
||||||
'api_server': isMancer() ? MANCER_SERVER : api_server_textgenerationwebui,
|
'api_server': isMancer() ? MANCER_SERVER : api_server_textgenerationwebui,
|
||||||
'legacy_api': textgenerationwebui_settings.legacy_api && !isMancer(),
|
'legacy_api': textgenerationwebui_settings.legacy_api && !isMancer(),
|
||||||
|
|
27
server.js
27
server.js
|
@ -164,6 +164,15 @@ function getAphroditeHeaders() {
|
||||||
}) : {};
|
}) : {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTabbyHeaders() {
|
||||||
|
const apiKey = readSecret(SECRET_KEYS.TABBY)
|
||||||
|
|
||||||
|
return apiKey ? ({
|
||||||
|
"x-api-key": apiKey,
|
||||||
|
"Authorization": `Bearer ${apiKey}`,
|
||||||
|
}) : {};
|
||||||
|
}
|
||||||
|
|
||||||
function getOverrideHeaders(urlHost) {
|
function getOverrideHeaders(urlHost) {
|
||||||
const overrideHeaders = config.requestOverrides?.find((e) => e.hosts?.includes(urlHost))?.headers;
|
const overrideHeaders = config.requestOverrides?.find((e) => e.hosts?.includes(urlHost))?.headers;
|
||||||
if (overrideHeaders && urlHost) {
|
if (overrideHeaders && urlHost) {
|
||||||
|
@ -186,6 +195,8 @@ function setAdditionalHeaders(request, args, server) {
|
||||||
headers = getMancerHeaders();
|
headers = getMancerHeaders();
|
||||||
} else if (request.body.use_aphrodite) {
|
} else if (request.body.use_aphrodite) {
|
||||||
headers = getAphroditeHeaders();
|
headers = getAphroditeHeaders();
|
||||||
|
} else if (request.body.use_tabby) {
|
||||||
|
headers = getTabbyHeaders();
|
||||||
} else {
|
} else {
|
||||||
headers = server ? getOverrideHeaders((new URL(server))?.host) : {};
|
headers = server ? getOverrideHeaders((new URL(server))?.host) : {};
|
||||||
}
|
}
|
||||||
|
@ -520,6 +531,9 @@ app.post("/api/textgenerationwebui/status", jsonParser, async function (request,
|
||||||
else if (request.body.use_mancer) {
|
else if (request.body.use_mancer) {
|
||||||
url += "/oai/v1/models";
|
url += "/oai/v1/models";
|
||||||
}
|
}
|
||||||
|
else if (request.body.use_tabby) {
|
||||||
|
url += "/v1/model/list"
|
||||||
|
}
|
||||||
|
|
||||||
const modelsReply = await fetch(url, args);
|
const modelsReply = await fetch(url, args);
|
||||||
|
|
||||||
|
@ -546,20 +560,21 @@ app.post("/api/textgenerationwebui/status", jsonParser, async function (request,
|
||||||
// Set result to the first model ID
|
// Set result to the first model ID
|
||||||
result = modelIds[0] || 'Valid';
|
result = modelIds[0] || 'Valid';
|
||||||
|
|
||||||
if (request.body.use_ooba) {
|
if (request.body.use_ooba || request.body.use_tabby) {
|
||||||
try {
|
try {
|
||||||
const modelInfoUrl = baseUrl + '/v1/internal/model/info';
|
const modelInfoPath = request.body.use_ooba ? "/v1/internal/model/info" : "/v1/model"
|
||||||
|
const modelInfoUrl = baseUrl + modelInfoPath;
|
||||||
const modelInfoReply = await fetch(modelInfoUrl, args);
|
const modelInfoReply = await fetch(modelInfoUrl, args);
|
||||||
|
|
||||||
if (modelInfoReply.ok) {
|
if (modelInfoReply.ok) {
|
||||||
const modelInfo = await modelInfoReply.json();
|
const modelInfo = await modelInfoReply.json();
|
||||||
console.log('Ooba model info:', modelInfo);
|
console.log(`${request.body.use_ooba ? "Ooba" : "Tabby"} model info: ${modelInfo}`);
|
||||||
|
|
||||||
const modelName = modelInfo?.model_name;
|
const modelName = request.body.use_ooba ? modelInfo?.model_name : modelInfo?.id;
|
||||||
result = modelName || result;
|
result = modelName || result;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get Ooba model info:', error);
|
console.error(`Failed to get ${request.body.use_ooba ? "Ooba" : "Tabby"} model info: ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,7 +608,7 @@ app.post("/api/textgenerationwebui/generate", jsonParser, async function (reques
|
||||||
if (request.body.legacy_api) {
|
if (request.body.legacy_api) {
|
||||||
url += "/v1/generate";
|
url += "/v1/generate";
|
||||||
}
|
}
|
||||||
else if (request.body.use_aphrodite || request.body.use_ooba) {
|
else if (request.body.use_aphrodite || request.body.use_ooba || request.body.use_tabby) {
|
||||||
url += "/v1/completions";
|
url += "/v1/completions";
|
||||||
}
|
}
|
||||||
else if (request.body.use_mancer) {
|
else if (request.body.use_mancer) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ const SECRET_KEYS = {
|
||||||
HORDE: 'api_key_horde',
|
HORDE: 'api_key_horde',
|
||||||
MANCER: 'api_key_mancer',
|
MANCER: 'api_key_mancer',
|
||||||
APHRODITE: 'api_key_aphrodite',
|
APHRODITE: 'api_key_aphrodite',
|
||||||
|
TABBY: 'api_key_tabby',
|
||||||
OPENAI: 'api_key_openai',
|
OPENAI: 'api_key_openai',
|
||||||
NOVEL: 'api_key_novel',
|
NOVEL: 'api_key_novel',
|
||||||
CLAUDE: 'api_key_claude',
|
CLAUDE: 'api_key_claude',
|
||||||
|
|
Loading…
Reference in New Issue