mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Textgen: Add model downloading for TabbyAPI
Tabby has a HuggingFace downloader API endpoint. Add direct support in SillyTavern by using the same method as ollama, but with the correct parameters exposed. Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
@@ -2348,6 +2348,16 @@
|
|||||||
<small data-i18n="Example: 127.0.0.1:5000">Example: http://127.0.0.1:5000</small>
|
<small data-i18n="Example: 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">
|
<input id="tabby_api_url_text" class="text_pole wide100p" maxlength="500" value="" autocomplete="off" data-server-history="tabby">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex1">
|
||||||
|
<h4>
|
||||||
|
<span data-i18n="Tabby Model">Tabby Model
|
||||||
|
</h4>
|
||||||
|
</h4>
|
||||||
|
<div id="tabby_download_model" class="menu_button menu_button_icon">
|
||||||
|
<i class="fa-solid fa-download"></i>
|
||||||
|
<span data-i18n="Download">Download</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div data-tg-type="koboldcpp">
|
<div data-tg-type="koboldcpp">
|
||||||
<div class="flex-container flexFlowColumn">
|
<div class="flex-container flexFlowColumn">
|
||||||
|
61
public/scripts/templates/tabbyDownloader.html
Normal file
61
public/scripts/templates/tabbyDownloader.html
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<div id="tabby_downloader_popup">
|
||||||
|
<div>
|
||||||
|
<h3><strong data-i18n="">Download Model</strong>
|
||||||
|
<a href="https://github.com/theroyallab/async-hf-downloader" class="notes-link" target="_blank">
|
||||||
|
<span class="note-link-span">?</span>
|
||||||
|
</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<small class="flex-container extensions_info justifyCenter">
|
||||||
|
Download a HuggingFace model with TabbyAPI
|
||||||
|
</small>
|
||||||
|
<small class="flex-container extensions_info justifyCenter">
|
||||||
|
(Requires an admin key)
|
||||||
|
</small>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<!-- Model parameter textboxes -->
|
||||||
|
Repo ID
|
||||||
|
<div class="flex-container">
|
||||||
|
<input name="hf_repo_id" class="text_pole" type="text" placeholder="Ex. turboderp/Llama-3-8B-exl2" />
|
||||||
|
</div>
|
||||||
|
<div class="range-block-title justifyCenter">
|
||||||
|
<span data-i18n="Downloader Options">Downloader Options</span>
|
||||||
|
<div class="margin5 fa-solid fa-circle-info opacity50p " data-i18n="[title]Extra parameters for downloading/HuggingFace API" title="Extra parameters for downloading/HuggingFace API. If unsure, leave these blank."></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class="flex1">
|
||||||
|
<label for="revision">
|
||||||
|
<small data-i18n="Revision">Revision</small>
|
||||||
|
</label>
|
||||||
|
<input name="revision" class="text_pole" type="text" placeholder="Ex. 6.0bpw" />
|
||||||
|
</div>
|
||||||
|
<div class="flex1">
|
||||||
|
<label for="folder_name">
|
||||||
|
<small data-i18n="Folder Name">Output Folder Name</small>
|
||||||
|
</label>
|
||||||
|
<input name="folder_name" class="text_pole" type="text" />
|
||||||
|
</div>
|
||||||
|
<div class="flex1">
|
||||||
|
<label for="hf_token">
|
||||||
|
<small data-i18n="HF Token">HF Token</small>
|
||||||
|
</label>
|
||||||
|
<input name="hf_token" class="text_pole" type="text" placeholder="For gated models" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="range-block-title justifyCenter">
|
||||||
|
<span data-i18n="Include Patterns">Include Patterns</span>
|
||||||
|
<div class="margin5 fa-solid fa-circle-info opacity50p" data-i18n="[title]Glob patterns of files to include in the download." title="Glob patterns of files to include in the download. Separate each pattern by a newline."></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<textarea class="text_pole textarea_compact" name="tabby_download_include" placeholder="Ex. *.txt"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="range-block-title justifyCenter">
|
||||||
|
<span data-i18n="Exclude Patterns">Exclude Patterns</span>
|
||||||
|
<div class="margin5 fa-solid fa-circle-info opacity50p" data-i18n="[title]Glob patterns of files to exclude in the download." title="Glob patterns of files to exclude in the download. Separate each pattern by a newline."></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<textarea class="text_pole textarea_compact" name="tabby_download_exclude" placeholder="Ex. *.txt"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -1,7 +1,9 @@
|
|||||||
import { isMobile } from './RossAscends-mods.js';
|
import { isMobile } from './RossAscends-mods.js';
|
||||||
import { amount_gen, callPopup, eventSource, event_types, getRequestHeaders, max_context, setGenerationParamsFromPreset } from '../script.js';
|
import { amount_gen, callPopup, eventSource, event_types, getRequestHeaders, max_context, online_status, setGenerationParamsFromPreset } from '../script.js';
|
||||||
import { textgenerationwebui_settings as textgen_settings, textgen_types } from './textgen-settings.js';
|
import { textgenerationwebui_settings as textgen_settings, textgen_types } from './textgen-settings.js';
|
||||||
import { tokenizers } from './tokenizers.js';
|
import { tokenizers } from './tokenizers.js';
|
||||||
|
import { renderTemplateAsync } from './templates.js';
|
||||||
|
import { POPUP_TYPE, callGenericPopup } from './popup.js';
|
||||||
|
|
||||||
let mancerModels = [];
|
let mancerModels = [];
|
||||||
let togetherModels = [];
|
let togetherModels = [];
|
||||||
@@ -470,6 +472,71 @@ async function downloadOllamaModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function downloadTabbyModel() {
|
||||||
|
try {
|
||||||
|
const serverUrl = textgen_settings.server_urls[textgen_types.TABBY];
|
||||||
|
|
||||||
|
if (online_status === 'no_connection' || !serverUrl) {
|
||||||
|
toastr.info('Please connect to a TabbyAPI server first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadHtml = $(await renderTemplateAsync('tabbyDownloader'));
|
||||||
|
const popupResult = await callGenericPopup(downloadHtml, POPUP_TYPE.CONFIRM, '', { okButton: 'Download', cancelButton: 'Cancel' });
|
||||||
|
|
||||||
|
// User cancelled the download
|
||||||
|
if (!popupResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const repoId = downloadHtml.find('input[name="hf_repo_id"]').val().toString()
|
||||||
|
if (!repoId) {
|
||||||
|
toastr.error('A HuggingFace repo ID must be provided. Skipping Download.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repoId.split("/").length !== 2) {
|
||||||
|
toastr.error('A HuggingFace repo ID must be formatted as Author/Name. Please try again.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
repo_id: repoId,
|
||||||
|
folder_name: downloadHtml.find('input[name="folder_name"]').val() || undefined,
|
||||||
|
revision: downloadHtml.find('input[name="revision"]').val() || undefined,
|
||||||
|
token: downloadHtml.find('input[name="hf_token"]').val() || undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const suffix of ["include", "exclude"]) {
|
||||||
|
const patterns = downloadHtml.find(`textarea[name="tabby_download_${suffix}"]`).val().toString();
|
||||||
|
if (patterns) {
|
||||||
|
params[suffix] = patterns.split("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params for the server side of ST
|
||||||
|
params['api_server'] = serverUrl;
|
||||||
|
params['api_type'] = textgen_settings.type
|
||||||
|
|
||||||
|
toastr.info('Downloading. Check the Tabby console for progress reports.');
|
||||||
|
|
||||||
|
const response = await fetch('/api/backends/text-completions/tabby/download', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
body: JSON.stringify(params),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
toastr.success('Download complete.');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
toastr.error('Failed to download HuggingFace model in TabbyAPI. Please try again.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function calculateOpenRouterCost() {
|
function calculateOpenRouterCost() {
|
||||||
if (textgen_settings.type !== textgen_types.OPENROUTER) {
|
if (textgen_settings.type !== textgen_types.OPENROUTER) {
|
||||||
return;
|
return;
|
||||||
@@ -538,6 +605,7 @@ jQuery(function () {
|
|||||||
$('#vllm_model').on('change', onVllmModelSelect);
|
$('#vllm_model').on('change', onVllmModelSelect);
|
||||||
$('#aphrodite_model').on('change', onAphroditeModelSelect);
|
$('#aphrodite_model').on('change', onAphroditeModelSelect);
|
||||||
$('#featherless_model').on('change', onFeatherlessModelSelect);
|
$('#featherless_model').on('change', onFeatherlessModelSelect);
|
||||||
|
$('#tabby_download_model').on('click', downloadTabbyModel);
|
||||||
|
|
||||||
const providersSelect = $('.openrouter_providers');
|
const providersSelect = $('.openrouter_providers');
|
||||||
for (const provider of OPENROUTER_PROVIDERS) {
|
for (const provider of OPENROUTER_PROVIDERS) {
|
||||||
|
@@ -588,7 +588,36 @@ llamacpp.post('/slots', jsonParser, async function (request, response) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tabby = express.Router();
|
||||||
|
|
||||||
|
tabby.post('/download', jsonParser, async function (request, response) {
|
||||||
|
try {
|
||||||
|
const baseUrl = String(request.body.api_server).replace(/\/$/, '');
|
||||||
|
|
||||||
|
const args = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(request.body),
|
||||||
|
timeout: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
setAdditionalHeaders(request, args, baseUrl);
|
||||||
|
const fetchResponse = await fetch(`${baseUrl}/v1/download`, args);
|
||||||
|
|
||||||
|
if (!fetchResponse.ok) {
|
||||||
|
console.log('Download error:', fetchResponse.status, fetchResponse.statusText);
|
||||||
|
return response.status(fetchResponse.status).send({ error: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.send({ ok: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return response.status(500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
router.use('/ollama', ollama);
|
router.use('/ollama', ollama);
|
||||||
router.use('/llamacpp', llamacpp);
|
router.use('/llamacpp', llamacpp);
|
||||||
|
router.use('/tabby', tabby);
|
||||||
|
|
||||||
module.exports = { router };
|
module.exports = { router };
|
||||||
|
Reference in New Issue
Block a user