Merge pull request #2489 from bdashore3/staging
Add tabbyAPI model downloader
This commit is contained in:
commit
cde328a43d
|
@ -2348,6 +2348,16 @@
|
|||
<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">
|
||||
</div>
|
||||
<div class="flex1">
|
||||
<h4>
|
||||
<span data-i18n="Tabby Model">Tabby Model</span>
|
||||
</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 data-tg-type="koboldcpp">
|
||||
<div class="flex-container flexFlowColumn">
|
||||
|
|
|
@ -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 { 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 { tokenizers } from './tokenizers.js';
|
||||
import { renderTemplateAsync } from './templates.js';
|
||||
import { POPUP_TYPE, callGenericPopup } from './popup.js';
|
||||
|
||||
let mancerModels = [];
|
||||
let togetherModels = [];
|
||||
|
@ -470,6 +472,74 @@ 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.status === 403) {
|
||||
toastr.error("The provided key has invalid permissions. Please use an admin key for downloading.");
|
||||
return;
|
||||
} else 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() {
|
||||
if (textgen_settings.type !== textgen_types.OPENROUTER) {
|
||||
return;
|
||||
|
@ -538,6 +608,7 @@ jQuery(function () {
|
|||
$('#vllm_model').on('change', onVllmModelSelect);
|
||||
$('#aphrodite_model').on('change', onAphroditeModelSelect);
|
||||
$('#featherless_model').on('change', onFeatherlessModelSelect);
|
||||
$('#tabby_download_model').on('click', downloadTabbyModel);
|
||||
|
||||
const providersSelect = $('.openrouter_providers');
|
||||
for (const provider of OPENROUTER_PROVIDERS) {
|
||||
|
|
|
@ -588,7 +588,53 @@ 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);
|
||||
|
||||
// Check key permissions
|
||||
const permissionResponse = await fetch(`${baseUrl}/v1/auth/permission`, {
|
||||
headers: args.headers
|
||||
});
|
||||
|
||||
if (permissionResponse.ok) {
|
||||
const permissionJson = await permissionResponse.json();
|
||||
|
||||
if (permissionJson['permission'] !== 'admin') {
|
||||
return response.status(403).send({ error: true });
|
||||
}
|
||||
} else {
|
||||
console.log('API Permission error:', permissionResponse.status, permissionResponse.statusText);
|
||||
return response.status(permissionResponse.status).send({ error: true });
|
||||
}
|
||||
|
||||
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('/llamacpp', llamacpp);
|
||||
router.use('/tabby', tabby);
|
||||
|
||||
module.exports = { router };
|
||||
|
|
Loading…
Reference in New Issue