Merge pull request #2489 from bdashore3/staging

Add tabbyAPI model downloader
This commit is contained in:
Cohee 2024-07-07 22:38:40 +03:00 committed by GitHub
commit cde328a43d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 189 additions and 1 deletions

View File

@ -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">

View 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.&#13;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.&#13;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.&#13;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>

View File

@ -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) {

View File

@ -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 };