Merge branch 'staging' into parser-followup-2
This commit is contained in:
commit
67dfe7354b
File diff suppressed because it is too large
Load Diff
|
@ -1696,16 +1696,20 @@
|
|||
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<label class="checkbox_label flexWrap alignItemsCenter" for="continue_postfix_none">
|
||||
<input type="radio" id="continue_postfix_none" name="continue_postfix" value="0">
|
||||
<span data-i18n="None">None</span>
|
||||
</label>
|
||||
<label class="checkbox_label flexWrap alignItemsCenter" for="continue_postfix_space">
|
||||
<input type="radio" id="continue_postfix_space" name="continue_postfix" value="0">
|
||||
<input type="radio" id="continue_postfix_space" name="continue_postfix" value="1">
|
||||
<span data-i18n="Space">Space</span>
|
||||
</label>
|
||||
<label class="checkbox_label flexWrap alignItemsCenter" for="continue_postfix_newline">
|
||||
<input type="radio" id="continue_postfix_newline" name="continue_postfix" value="1">
|
||||
<input type="radio" id="continue_postfix_newline" name="continue_postfix" value="2">
|
||||
<span data-i18n="Newline">Newline</span>
|
||||
</label>
|
||||
<label class="checkbox_label flexWrap alignItemsCenter" for="continue_postfix_double_newline">
|
||||
<input type="radio" id="continue_postfix_double_newline" name="continue_postfix" value="2">
|
||||
<input type="radio" id="continue_postfix_double_newline" name="continue_postfix" value="3">
|
||||
<span data-i18n="Double Newline">Double Newline</span>
|
||||
</label>
|
||||
<!-- Hidden input for loading radio buttons from presets. Don't remove! -->
|
||||
|
@ -2348,6 +2352,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">
|
||||
|
@ -2450,17 +2464,6 @@
|
|||
<span data-i18n="Alternative server URL (leave empty to use the default value).">
|
||||
Alternative server URL (leave empty to use the default value).<br>
|
||||
</span>
|
||||
<div id="ReverseProxyWarningMessage" class="reverse_proxy_warning">
|
||||
<b data-i18n="Remove your real OAI API Key from the API panel BEFORE typing anything into this box">
|
||||
Remove your real OAI API Key from the API panel BEFORE typing anything
|
||||
into this box.
|
||||
</b>
|
||||
<hr>
|
||||
<b data-i18n="We cannot provide support for problems encountered while using an unofficial OpenAI proxy">
|
||||
We cannot provide support for problems encountered while using an
|
||||
unofficial OpenAI proxy.
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wide100p">
|
||||
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" maxlength="5000" />
|
||||
|
@ -2482,6 +2485,20 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ReverseProxyWarningMessage" class="reverse_proxy_warning">
|
||||
<b>
|
||||
<div data-i18n="Using a proxy that you're not running yourself is a risk to your data privacy.">
|
||||
Using a proxy that you're not running yourself is a risk to your data privacy.
|
||||
</div>
|
||||
<div data-i18n="ANY support requests will be REFUSED if you are using a proxy.">
|
||||
ANY support requests will be REFUSED if you are using a proxy.
|
||||
</div>
|
||||
<hr>
|
||||
<i data-i18n="Do not proceed if you do not agree to this!">
|
||||
Do not proceed if you do not agree to this!
|
||||
</i>
|
||||
</b>
|
||||
</div>
|
||||
<form id="openai_form" data-source="openai" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||
<h4><span data-i18n="OpenAI API key">OpenAI API key</span></h4>
|
||||
<div>
|
||||
|
@ -2837,6 +2854,7 @@
|
|||
<option value="llama3-70b-8192">llama3-70b-8192</option>
|
||||
<option value="mixtral-8x7b-32768">mixtral-8x7b-32768</option>
|
||||
<option value="gemma-7b-it">gemma-7b-it</option>
|
||||
<option value="gemma2-9b-it">gemma2-9b-it</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="perplexity_form" data-source="perplexity">
|
||||
|
|
|
@ -155,6 +155,7 @@ import {
|
|||
ensureImageFormatSupported,
|
||||
flashHighlight,
|
||||
isTrueBoolean,
|
||||
debouncedThrottle,
|
||||
} from './scripts/utils.js';
|
||||
import { debounce_timeout } from './scripts/constants.js';
|
||||
|
||||
|
@ -9236,7 +9237,7 @@ jQuery(async function () {
|
|||
e.style.height = `${e.scrollHeight + 4}px`;
|
||||
is_use_scroll_holder = true;
|
||||
}
|
||||
const autoFitEditTextAreaDebounced = debounce(autoFitEditTextArea, debounce_timeout.short);
|
||||
const autoFitEditTextAreaDebounced = debouncedThrottle(autoFitEditTextArea, debounce_timeout.standard);
|
||||
document.addEventListener('input', e => {
|
||||
if (e.target instanceof HTMLTextAreaElement && e.target.classList.contains('edit_textarea')) {
|
||||
const immediately = e.target.scrollHeight > e.target.offsetHeight || e.target.value === '';
|
||||
|
|
|
@ -191,6 +191,7 @@ const character_names_behavior = {
|
|||
};
|
||||
|
||||
const continue_postfix_types = {
|
||||
NONE: '',
|
||||
SPACE: ' ',
|
||||
NEWLINE: '\n',
|
||||
DOUBLE_NEWLINE: '\n\n',
|
||||
|
@ -3138,6 +3139,9 @@ function setNamesBehaviorControls() {
|
|||
|
||||
function setContinuePostfixControls() {
|
||||
switch (oai_settings.continue_postfix) {
|
||||
case continue_postfix_types.NONE:
|
||||
$('#continue_postfix_none').prop('checked', true);
|
||||
break;
|
||||
case continue_postfix_types.SPACE:
|
||||
$('#continue_postfix_space').prop('checked', true);
|
||||
break;
|
||||
|
@ -4112,7 +4116,7 @@ async function onModelChange() {
|
|||
if (oai_settings.max_context_unlocked) {
|
||||
$('#openai_max_context').attr('max', unlocked_max);
|
||||
}
|
||||
else if (['llama3-8b-8192', 'llama3-70b-8192', 'gemma-7b-it'].includes(oai_settings.groq_model)) {
|
||||
else if (['llama3-8b-8192', 'llama3-70b-8192', 'gemma-7b-it', 'gemma2-9b-it'].includes(oai_settings.groq_model)) {
|
||||
$('#openai_max_context').attr('max', max_8k);
|
||||
}
|
||||
else if (['mixtral-8x7b-32768'].includes(oai_settings.groq_model)) {
|
||||
|
@ -5078,6 +5082,12 @@ $(document).ready(async function () {
|
|||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#continue_postfix_none').on('input', function () {
|
||||
oai_settings.continue_postfix = continue_postfix_types.NONE;
|
||||
setContinuePostfixControls();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#continue_postfix_space').on('input', function () {
|
||||
oai_settings.continue_postfix = continue_postfix_types.SPACE;
|
||||
setContinuePostfixControls();
|
||||
|
|
|
@ -332,6 +332,16 @@ export function initDefaultSlashCommands() {
|
|||
name: 'continue',
|
||||
callback: continueChatCallback,
|
||||
aliases: ['cont'],
|
||||
namedArgumentList: [
|
||||
new SlashCommandNamedArgument(
|
||||
'await',
|
||||
'Whether to await for the continued generation before proceeding',
|
||||
[ARGUMENT_TYPE.BOOLEAN],
|
||||
false,
|
||||
false,
|
||||
'false',
|
||||
),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument(
|
||||
'prompt', [ARGUMENT_TYPE.STRING], false,
|
||||
|
@ -341,16 +351,19 @@ export function initDefaultSlashCommands() {
|
|||
<div>
|
||||
Continues the last message in the chat, with an optional additional prompt.
|
||||
</div>
|
||||
<div>
|
||||
If <code>await=true</code> named argument is passed, the command will await for the continued generation before proceeding.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Example:</strong>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>/continue</code></pre>
|
||||
Continues the chat with no additional prompt.
|
||||
Continues the chat with no additional prompt and immediately proceeds to the next command.
|
||||
</li>
|
||||
<li>
|
||||
<pre><code>/continue Let's explore this further...</code></pre>
|
||||
Continues the chat with the provided prompt.
|
||||
<pre><code>/continue await=true Let's explore this further...</code></pre>
|
||||
Continues the chat with the provided prompt and waits for the generation to finish.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -2635,19 +2648,35 @@ async function openChat(id) {
|
|||
await reloadCurrentChat();
|
||||
}
|
||||
|
||||
function continueChatCallback(_, prompt) {
|
||||
setTimeout(async () => {
|
||||
async function continueChatCallback(args, prompt) {
|
||||
const shouldAwait = isTrueBoolean(args?.await);
|
||||
|
||||
const outerPromise = new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);
|
||||
} catch {
|
||||
console.warn('Timeout waiting for generation unlock');
|
||||
toastr.warning('Cannot run /continue command while the reply is being generated.');
|
||||
return reject();
|
||||
}
|
||||
|
||||
// Prevent infinite recursion
|
||||
$('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));
|
||||
$('#option_continue').trigger('click', { fromSlashCommand: true, additionalPrompt: prompt });
|
||||
}, 1);
|
||||
try {
|
||||
// Prevent infinite recursion
|
||||
$('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));
|
||||
|
||||
const options = prompt?.trim() ? { quiet_prompt: prompt.trim(), quietToLoud: true } : {};
|
||||
await Generate('continue', options);
|
||||
|
||||
resolve();
|
||||
} catch (error) {
|
||||
console.error('Error running /continue command:', error);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldAwait) {
|
||||
await outerPromise;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -9,24 +9,21 @@ import {
|
|||
eventSource,
|
||||
event_types,
|
||||
DEFAULT_PRINT_TIMEOUT,
|
||||
substituteParams,
|
||||
printCharacters,
|
||||
} from '../script.js';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { FILTER_TYPES, FILTER_STATES, DEFAULT_FILTER_STATE, isFilterState, FilterHelper } from './filters.js';
|
||||
|
||||
import { groupCandidatesFilter, groups, select_group_chats, selected_group } from './group-chats.js';
|
||||
import { groupCandidatesFilter, groups, selected_group } from './group-chats.js';
|
||||
import { download, onlyUnique, parseJsonFile, uuidv4, getSortableDelay, flashHighlight, equalsIgnoreCaseAndAccents, includesIgnoreCaseAndAccents, removeFromArray, getFreeName, debounce } from './utils.js';
|
||||
import { power_user } from './power-user.js';
|
||||
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
||||
import { SlashCommand } from './slash-commands/SlashCommand.js';
|
||||
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
|
||||
import { isMobile } from './RossAscends-mods.js';
|
||||
import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
|
||||
import { POPUP_RESULT, POPUP_TYPE, callGenericPopup } from './popup.js';
|
||||
import { debounce_timeout } from './constants.js';
|
||||
import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js';
|
||||
import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
|
||||
import { SlashCommandExecutor } from './slash-commands/SlashCommandExecutor.js';
|
||||
import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||
import { renderTemplateAsync } from './templates.js';
|
||||
|
||||
|
@ -53,6 +50,7 @@ export {
|
|||
removeTagFromMap,
|
||||
};
|
||||
|
||||
/** @typedef {import('../scripts/popup.js').Popup} Popup */
|
||||
/** @typedef {import('../script.js').Character} Character */
|
||||
|
||||
const CHARACTER_FILTER_SELECTOR = '#rm_characters_block .rm_tag_filter';
|
||||
|
@ -717,7 +715,7 @@ async function importTags(character, { forceShow = false } = {}) {
|
|||
// Gather the tags to import based on the selected setting
|
||||
const tagNamesToImport = await handleTagImport(character, { forceShow });
|
||||
if (!tagNamesToImport?.length) {
|
||||
toastr.info('No tags to import', 'Importing Tags');
|
||||
console.debug('No tags to import');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -815,7 +813,7 @@ async function showTagImportPopup(character, existingTags, newTags, folderTags)
|
|||
wider: true, okButton: 'Import', cancelButton: true,
|
||||
customButtons: Object.values(importButtons),
|
||||
customInputs: [{ id: 'import_remember_option', label: 'Remember my choice', tooltip: 'Remember the chosen import option\nIf anything besides \'Cancel\' is selected, this dialog will not show up anymore.\nTo change this, go to the settings and modify "Tag Import Option".\n\nIf the "Import" option is chosen, the global setting will stay on "Ask".' }],
|
||||
onClose: onCloseRemember
|
||||
onClose: onCloseRemember,
|
||||
});
|
||||
if (!result) {
|
||||
return [];
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -301,6 +301,32 @@ export function throttle(func, limit = 300) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a debounced throttle function that only invokes func at most once per every limit milliseconds.
|
||||
* @param {function} func The function to throttle.
|
||||
* @param {number} [limit=300] The limit in milliseconds.
|
||||
* @returns {function} The throttled function.
|
||||
*/
|
||||
export function debouncedThrottle(func, limit = 300) {
|
||||
let last, deferTimer;
|
||||
let db = debounce(func);
|
||||
|
||||
return function() {
|
||||
let now = +new Date, args = arguments;
|
||||
if(!last || (last && now < last + limit)) {
|
||||
clearTimeout(deferTimer);
|
||||
db.apply(this, args);
|
||||
deferTimer = setTimeout(function() {
|
||||
last = now;
|
||||
func.apply(this, args);
|
||||
}, limit);
|
||||
} else {
|
||||
last = now;
|
||||
func.apply(this, args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an element is in the viewport.
|
||||
* @param {Element} el The element to check.
|
||||
|
|
|
@ -138,6 +138,7 @@ body {
|
|||
/*fallback for JS load*/
|
||||
height: 100vh;
|
||||
height: 100svh;
|
||||
height: 100dvh;
|
||||
/*defaults as 100%, then reassigned via JS as pixels, will work on PC and Android*/
|
||||
/*height: calc(var(--doc-height) - 1px);*/
|
||||
background-color: var(--greyCAIbg);
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -94,7 +94,11 @@ function getModelForTask(task) {
|
|||
*/
|
||||
async function getPipeline(task, forceModel = '') {
|
||||
if (tasks[task].pipeline) {
|
||||
return tasks[task].pipeline;
|
||||
if (forceModel === '' || tasks[task].currentModel === forceModel) {
|
||||
return tasks[task].pipeline;
|
||||
}
|
||||
console.log('Disposing transformers.js pipeline for for task', task, 'with model', tasks[task].currentModel);
|
||||
await tasks[task].pipeline.dispose();
|
||||
}
|
||||
|
||||
const cache_dir = path.join(process.cwd(), 'cache');
|
||||
|
@ -103,6 +107,7 @@ async function getPipeline(task, forceModel = '') {
|
|||
console.log('Initializing transformers.js pipeline for task', task, 'with model', model);
|
||||
const instance = await pipeline(task, model, { cache_dir, quantized: tasks[task].quantized ?? true, local_files_only: localOnly });
|
||||
tasks[task].pipeline = instance;
|
||||
tasks[task].currentModel = model;
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue