Merge pull request #1565 from SillyTavern/togetherai

Add TogetherAI as a text completion source
This commit is contained in:
Cohee
2023-12-18 14:52:36 +02:00
committed by GitHub
18 changed files with 483 additions and 97 deletions

View File

@@ -382,7 +382,9 @@ function RA_autoconnect(PrevApi) {
}
break;
case 'textgenerationwebui':
if (textgen_settings.type === textgen_types.MANCER && secret_state[SECRET_KEYS.MANCER]) {
if ((textgen_settings.type === textgen_types.MANCER && secret_state[SECRET_KEYS.MANCER]) ||
(textgen_settings.type === textgen_types.TOGETHERAI && secret_state[SECRET_KEYS.TOGETHERAI])
) {
$('#api_button_textgenerationwebui').trigger('click');
}
else if (api_server_textgenerationwebui && isValidUrl(api_server_textgenerationwebui)) {

View File

@@ -46,6 +46,7 @@ const sources = {
vlad: 'vlad',
openai: 'openai',
comfy: 'comfy',
togetherai: 'togetherai',
};
const generationMode = {
@@ -917,7 +918,7 @@ async function onModelChange() {
extension_settings.sd.model = $('#sd_model').find(':selected').val();
saveSettingsDebounced();
const cloudSources = [sources.horde, sources.novel, sources.openai];
const cloudSources = [sources.horde, sources.novel, sources.openai, sources.togetherai];
if (cloudSources.includes(extension_settings.sd.source)) {
return;
@@ -1050,11 +1051,14 @@ async function loadSamplers() {
samplers = await loadVladSamplers();
break;
case sources.openai:
samplers = await loadOpenAiSamplers();
samplers = ['N/A'];
break;
case sources.comfy:
samplers = await loadComfySamplers();
break;
case sources.togetherai:
samplers = ['N/A'];
break;
}
for (const sampler of samplers) {
@@ -1064,6 +1068,11 @@ async function loadSamplers() {
option.selected = sampler === extension_settings.sd.sampler;
$('#sd_sampler').append(option);
}
if (!extension_settings.sd.sampler && samplers.length > 0) {
extension_settings.sd.sampler = samplers[0];
$('#sd_sampler').val(extension_settings.sd.sampler).trigger('change');
}
}
async function loadHordeSamplers() {
@@ -1120,10 +1129,6 @@ async function loadAutoSamplers() {
}
}
async function loadOpenAiSamplers() {
return ['N/A'];
}
async function loadVladSamplers() {
if (!extension_settings.sd.vlad_url) {
return [];
@@ -1212,6 +1217,9 @@ async function loadModels() {
case sources.comfy:
models = await loadComfyModels();
break;
case sources.togetherai:
models = await loadTogetherAIModels();
break;
}
for (const model of models) {
@@ -1221,6 +1229,30 @@ async function loadModels() {
option.selected = model.value === extension_settings.sd.model;
$('#sd_model').append(option);
}
if (!extension_settings.sd.model && models.length > 0) {
extension_settings.sd.model = models[0].value;
$('#sd_model').val(extension_settings.sd.model).trigger('change');
}
}
async function loadTogetherAIModels() {
if (!secret_state[SECRET_KEYS.TOGETHERAI]) {
console.debug('TogetherAI API key is not set.');
return [];
}
const result = await fetch('/api/sd/together/models', {
method: 'POST',
headers: getRequestHeaders(),
});
if (result.ok) {
const data = await result.json();
return data;
}
return [];
}
async function loadHordeModels() {
@@ -1434,6 +1466,9 @@ async function loadSchedulers() {
case sources.openai:
schedulers = ['N/A'];
break;
case sources.togetherai:
schedulers = ['N/A'];
break;
case sources.comfy:
schedulers = await loadComfySchedulers();
break;
@@ -1493,6 +1528,9 @@ async function loadVaes() {
case sources.openai:
vaes = ['N/A'];
break;
case sources.togetherai:
vaes = ['N/A'];
break;
case sources.comfy:
vaes = await loadComfyVaes();
break;
@@ -1873,6 +1911,9 @@ async function sendGenerationRequest(generationType, prompt, characterName = nul
case sources.comfy:
result = await generateComfyImage(prefixedPrompt);
break;
case sources.togetherai:
result = await generateTogetherAIImage(prefixedPrompt);
break;
}
if (!result.data) {
@@ -1895,6 +1936,29 @@ async function sendGenerationRequest(generationType, prompt, characterName = nul
callback ? callback(prompt, base64Image, generationType) : sendMessage(prompt, base64Image, generationType);
}
async function generateTogetherAIImage(prompt) {
const result = await fetch('/api/sd/together/generate', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify({
prompt: prompt,
negative_prompt: extension_settings.sd.negative_prompt,
model: extension_settings.sd.model,
steps: extension_settings.sd.steps,
width: extension_settings.sd.width,
height: extension_settings.sd.height,
}),
});
if (result.ok) {
const data = await result.json();
return { format: 'jpg', data: data?.output?.choices?.[0]?.image_base64 };
} else {
const text = await result.text();
throw new Error(text);
}
}
/**
* Generates an "extras" image using a provided prompt and other settings.
*
@@ -2435,6 +2499,8 @@ function isValidState() {
return secret_state[SECRET_KEYS.OPENAI];
case sources.comfy:
return true;
case sources.togetherai:
return secret_state[SECRET_KEYS.TOGETHERAI];
}
}

View File

@@ -35,6 +35,7 @@
<option value="novel">NovelAI Diffusion</option>
<option value="openai">OpenAI (DALL-E)</option>
<option value="comfy">ComfyUI</option>
<option value="togetherai">TogetherAI</option>
</select>
<div data-sd-source="auto">
<label for="sd_auto_url">SD Web UI URL</label>

View File

@@ -1,64 +0,0 @@
import { setGenerationParamsFromPreset } from '../script.js';
import { isMobile } from './RossAscends-mods.js';
import { textgenerationwebui_settings as textgen_settings } from './textgen-settings.js';
let models = [];
export async function loadMancerModels(data) {
if (!Array.isArray(data)) {
console.error('Invalid Mancer models data', data);
return;
}
models = data;
$('#mancer_model').empty();
for (const model of data) {
const option = document.createElement('option');
option.value = model.id;
option.text = model.name;
option.selected = model.id === textgen_settings.mancer_model;
$('#mancer_model').append(option);
}
}
function onMancerModelSelect() {
const modelId = String($('#mancer_model').val());
textgen_settings.mancer_model = modelId;
$('#api_button_textgenerationwebui').trigger('click');
const limits = models.find(x => x.id === modelId)?.limits;
setGenerationParamsFromPreset({ max_length: limits.context, genamt: limits.completion });
}
function getMancerModelTemplate(option) {
const model = models.find(x => x.id === option?.element?.value);
if (!option.id || !model) {
return option.text;
}
const creditsPerPrompt = (model.limits?.context - model.limits?.completion) * model.pricing?.prompt;
const creditsPerCompletion = model.limits?.completion * model.pricing?.completion;
const creditsTotal = Math.round(creditsPerPrompt + creditsPerCompletion).toFixed(0);
return $((`
<div class="flex-container flexFlowColumn">
<div><strong>${DOMPurify.sanitize(model.name)}</strong> | <span>${model.limits?.context} ctx</span> / <span>${model.limits?.completion} res</span> | <small>Credits per request (max): ${creditsTotal}</small></div>
</div>
`));
}
jQuery(function () {
$('#mancer_model').on('change', onMancerModelSelect);
if (!isMobile()) {
$('#mancer_model').select2({
placeholder: 'Select a model',
searchInputPlaceholder: 'Search models...',
searchInputCssClass: 'text_pole',
width: '100%',
templateResult: getMancerModelTemplate,
});
}
});

View File

@@ -15,6 +15,7 @@ export const SECRET_KEYS = {
MAKERSUITE: 'api_key_makersuite',
SERPAPI: 'api_key_serpapi',
MISTRALAI: 'api_key_mistralai',
TOGETHERAI: 'api_key_togetherai',
};
const INPUT_MAP = {
@@ -31,6 +32,7 @@ const INPUT_MAP = {
[SECRET_KEYS.APHRODITE]: '#api_key_aphrodite',
[SECRET_KEYS.TABBY]: '#api_key_tabby',
[SECRET_KEYS.MISTRALAI]: '#api_key_mistralai',
[SECRET_KEYS.TOGETHERAI]: '#api_key_togetherai',
};
async function clearSecret() {

View File

@@ -0,0 +1,120 @@
import { setGenerationParamsFromPreset } from '../script.js';
import { isMobile } from './RossAscends-mods.js';
import { textgenerationwebui_settings as textgen_settings } from './textgen-settings.js';
let mancerModels = [];
let togetherModels = [];
export async function loadTogetherAIModels(data) {
if (!Array.isArray(data)) {
console.error('Invalid Together AI models data', data);
return;
}
togetherModels = data;
$('#model_togetherai_select').empty();
for (const model of data) {
// Hey buddy, I think you've got the wrong door.
if (model.display_type === 'image') {
continue;
}
const option = document.createElement('option');
option.value = model.name;
option.text = model.display_name;
option.selected = model.name === textgen_settings.togetherai_model;
$('#model_togetherai_select').append(option);
}
}
export async function loadMancerModels(data) {
if (!Array.isArray(data)) {
console.error('Invalid Mancer models data', data);
return;
}
mancerModels = data;
$('#mancer_model').empty();
for (const model of data) {
const option = document.createElement('option');
option.value = model.id;
option.text = model.name;
option.selected = model.id === textgen_settings.mancer_model;
$('#mancer_model').append(option);
}
}
function onMancerModelSelect() {
const modelId = String($('#mancer_model').val());
textgen_settings.mancer_model = modelId;
$('#api_button_textgenerationwebui').trigger('click');
const limits = mancerModels.find(x => x.id === modelId)?.limits;
setGenerationParamsFromPreset({ max_length: limits.context, genamt: limits.completion });
}
function onTogetherModelSelect() {
const modelName = String($('#model_togetherai_select').val());
textgen_settings.togetherai_model = modelName;
$('#api_button_textgenerationwebui').trigger('click');
const model = togetherModels.find(x => x.name === modelName);
setGenerationParamsFromPreset({ max_length: model.context_length });
}
function getMancerModelTemplate(option) {
const model = mancerModels.find(x => x.id === option?.element?.value);
if (!option.id || !model) {
return option.text;
}
const creditsPerPrompt = (model.limits?.context - model.limits?.completion) * model.pricing?.prompt;
const creditsPerCompletion = model.limits?.completion * model.pricing?.completion;
const creditsTotal = Math.round(creditsPerPrompt + creditsPerCompletion).toFixed(0);
return $((`
<div class="flex-container flexFlowColumn">
<div><strong>${DOMPurify.sanitize(model.name)}</strong> | <span>${model.limits?.context} ctx</span> / <span>${model.limits?.completion} res</span> | <small>Credits per request (max): ${creditsTotal}</small></div>
</div>
`));
}
function getTogetherModelTemplate(option) {
const model = togetherModels.find(x => x.name === option?.element?.value);
if (!option.id || !model) {
return option.text;
}
return $((`
<div class="flex-container flexFlowColumn">
<div><strong>${DOMPurify.sanitize(model.name)}</strong> | <span>${model.context_length || '???'} tokens</span></div>
<div><small>${DOMPurify.sanitize(model.description)}</small></div>
</div>
`));
}
jQuery(function () {
$('#mancer_model').on('change', onMancerModelSelect);
$('#model_togetherai_select').on('change', onTogetherModelSelect);
if (!isMobile()) {
$('#mancer_model').select2({
placeholder: 'Select a model',
searchInputPlaceholder: 'Search models...',
searchInputCssClass: 'text_pole',
width: '100%',
templateResult: getMancerModelTemplate,
});
$('#model_togetherai_select').select2({
placeholder: 'Select a model',
searchInputPlaceholder: 'Search models...',
searchInputCssClass: 'text_pole',
width: '100%',
templateResult: getTogetherModelTemplate,
});
}
});

View File

@@ -31,15 +31,17 @@ export const textgen_types = {
APHRODITE: 'aphrodite',
TABBY: 'tabby',
KOBOLDCPP: 'koboldcpp',
TOGETHERAI: 'togetherai',
};
const { MANCER, APHRODITE } = textgen_types;
const { MANCER, APHRODITE, TOGETHERAI } = textgen_types;
// Maybe let it be configurable in the future?
// (7 days later) The future has come.
const MANCER_SERVER_KEY = 'mancer_server';
const MANCER_SERVER_DEFAULT = 'https://neuro.mancer.tech';
export let MANCER_SERVER = localStorage.getItem(MANCER_SERVER_KEY) ?? MANCER_SERVER_DEFAULT;
let MANCER_SERVER = localStorage.getItem(MANCER_SERVER_KEY) ?? MANCER_SERVER_DEFAULT;
let TOGETHERAI_SERVER = 'https://api.together.xyz';
const KOBOLDCPP_ORDER = [6, 0, 1, 3, 4, 2, 5];
const settings = {
@@ -89,6 +91,7 @@ const settings = {
//prompt_log_probs_aphrodite: 0,
type: textgen_types.OOBA,
mancer_model: 'mytholite',
togetherai_model: 'Gryphe/MythoMax-L2-13b',
legacy_api: false,
sampler_order: KOBOLDCPP_ORDER,
n: 1,
@@ -164,8 +167,8 @@ async function selectPreset(name) {
function formatTextGenURL(value) {
try {
// Mancer doesn't need any formatting (it's hardcoded)
if (settings.type === MANCER) {
// Mancer/Together doesn't need any formatting (it's hardcoded)
if (settings.type === MANCER || settings.type === TOGETHERAI) {
return value;
}
@@ -546,6 +549,10 @@ function getModel() {
return settings.mancer_model;
}
if (settings.type === TOGETHERAI) {
return settings.togetherai_model;
}
if (settings.type === APHRODITE) {
return online_status;
}
@@ -553,6 +560,18 @@ function getModel() {
return undefined;
}
export function getTextGenServer() {
if (settings.type === MANCER) {
return MANCER_SERVER;
}
if (settings.type === TOGETHERAI) {
return TOGETHERAI_SERVER;
}
return api_server_textgenerationwebui;
}
export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, isContinue, cfgValues, type) {
const canMultiSwipe = !isContinue && !isImpersonate && type !== 'quiet';
let APIflags = {
@@ -590,10 +609,8 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
toIntArray(getCustomTokenBans()) :
getCustomTokenBans(),
'api_type': settings.type,
'api_server': settings.type === MANCER ?
MANCER_SERVER :
api_server_textgenerationwebui,
'legacy_api': settings.legacy_api && settings.type !== MANCER,
'api_server': getTextGenServer(),
'legacy_api': settings.legacy_api && settings.type !== MANCER && settings.type !== TOGETHERAI,
'sampler_order': settings.type === textgen_types.KOBOLDCPP ?
settings.sampler_order :
undefined,

View File

@@ -6,7 +6,7 @@ import { getStringHash } from './utils.js';
import { kai_flags } from './kai-settings.js';
import { textgen_types, textgenerationwebui_settings as textgen_settings } from './textgen-settings.js';
const { OOBA, TABBY, KOBOLDCPP, MANCER } = textgen_types;
const { OOBA, TABBY, KOBOLDCPP, MANCER, TOGETHERAI } = textgen_types;
export const CHARACTERS_PER_TOKEN_RATIO = 3.35;
const TOKENIZER_WARNING_KEY = 'tokenizationWarningShown';
@@ -540,7 +540,8 @@ function getTextgenAPITokenizationParams(str) {
url: api_server_textgenerationwebui,
legacy_api:
textgen_settings.legacy_api &&
textgen_settings.type !== MANCER,
textgen_settings.type !== MANCER &&
textgen_settings.type !== TOGETHERAI,
};
}