From 2065f95edc1af16c17df73c762c12668f9b9d6e5 Mon Sep 17 00:00:00 2001 From: kalomaze <66376113+kalomaze@users.noreply.github.com> Date: Sat, 10 Feb 2024 14:57:41 -0600 Subject: [PATCH 01/29] Sampler priority support --- default/settings.json | 1 + public/index.html | 10 ++++++++++ public/scripts/textgen-settings.js | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/default/settings.json b/default/settings.json index bd11111d9..bdd82b11a 100644 --- a/default/settings.json +++ b/default/settings.json @@ -47,6 +47,7 @@ "ban_eos_token": false, "skip_special_tokens": true, "streaming": false, + "sampler_priority": "temperature\ndynamic_temperature\nquadratic_sampling\ntop_k\ntop_p\ntypical_p\nepsilon_cutoff\neta_cutoff\ntfs\ntop_a\nmin_p\nmirostat" "mirostat_mode": 0, "mirostat_tau": 5, "mirostat_eta": 0.1, diff --git a/public/index.html b/public/index.html index 94427889c..e4fd16838 100644 --- a/public/index.html +++ b/public/index.html @@ -1451,6 +1451,16 @@ +
+
+

+ Sampler Priority +
+

+
+ +
+
Logit Bias diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index b1a292e68..b190e3880 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -96,6 +96,7 @@ const settings = { negative_prompt: '', grammar_string: '', banned_tokens: '', + sampler_priority: '', //n_aphrodite: 1, //best_of_aphrodite: 1, ignore_eos_token_aphrodite: false, @@ -170,6 +171,7 @@ const setting_names = [ //'log_probs_aphrodite', //'prompt_log_probs_aphrodite' 'sampler_order', + 'sampler_priority', 'n', 'logit_bias', 'custom_model', @@ -827,6 +829,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, 'dynatemp_range': settings.dynatemp ? (settings.max_temp - settings.min_temp) / 2 : 0, 'dynatemp_exponent': settings.dynatemp ? settings.dynatemp_exponent : 1, 'smoothing_factor': settings.smoothing_factor, + 'sampler_priority': (settings.type === OOBA || settings.type === APHRODITE || settings.type == TABBY) ? settings.sampler_priority : undefined, 'stopping_strings': getStoppingStrings(isImpersonate, isContinue), 'stop': getStoppingStrings(isImpersonate, isContinue), 'truncation_length': max_context, @@ -860,6 +863,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, 'guidance_scale': cfgValues?.guidanceScale?.value ?? settings.guidance_scale ?? 1, 'negative_prompt': cfgValues?.negativePrompt ?? substituteParams(settings.negative_prompt) ?? '', 'grammar_string': settings.grammar_string, + 'sampler_priority': (settings.type === OOBA || settings.type === APHRODITE || settings.type == TABBY) ? settings.sampler_priority : undefined, // llama.cpp aliases. In case someone wants to use LM Studio as Text Completion API 'repeat_penalty': settings.rep_pen, 'tfs_z': settings.tfs, From 818029288ed25df2b4018d5ef567409165580895 Mon Sep 17 00:00:00 2001 From: kalomaze <66376113+kalomaze@users.noreply.github.com> Date: Sat, 10 Feb 2024 15:22:24 -0600 Subject: [PATCH 02/29] Remove sending it if it's Aphrodite or TabbyAPI --- public/scripts/textgen-settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index b190e3880..d5bd3ed68 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -829,7 +829,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, 'dynatemp_range': settings.dynatemp ? (settings.max_temp - settings.min_temp) / 2 : 0, 'dynatemp_exponent': settings.dynatemp ? settings.dynatemp_exponent : 1, 'smoothing_factor': settings.smoothing_factor, - 'sampler_priority': (settings.type === OOBA || settings.type === APHRODITE || settings.type == TABBY) ? settings.sampler_priority : undefined, + 'sampler_priority': (settings.type === OOBA) ? settings.sampler_priority : undefined, 'stopping_strings': getStoppingStrings(isImpersonate, isContinue), 'stop': getStoppingStrings(isImpersonate, isContinue), 'truncation_length': max_context, @@ -863,7 +863,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, 'guidance_scale': cfgValues?.guidanceScale?.value ?? settings.guidance_scale ?? 1, 'negative_prompt': cfgValues?.negativePrompt ?? substituteParams(settings.negative_prompt) ?? '', 'grammar_string': settings.grammar_string, - 'sampler_priority': (settings.type === OOBA || settings.type === APHRODITE || settings.type == TABBY) ? settings.sampler_priority : undefined, + 'sampler_priority': (settings.type === OOBA) ? settings.sampler_priority : undefined, // llama.cpp aliases. In case someone wants to use LM Studio as Text Completion API 'repeat_penalty': settings.rep_pen, 'tfs_z': settings.tfs, From 70deb11d27630508eca07c10a2938bdb5da635ad Mon Sep 17 00:00:00 2001 From: kalomaze <66376113+kalomaze@users.noreply.github.com> Date: Sat, 10 Feb 2024 16:32:46 -0600 Subject: [PATCH 03/29] implement jank js + drag n drop sampler priority --- public/index.html | 29 ++++++++++++++++++---- public/scripts/textgen-settings.js | 40 +++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/public/index.html b/public/index.html index e4fd16838..4a01ccf28 100644 --- a/public/index.html +++ b/public/index.html @@ -1451,14 +1451,33 @@
-
-
+
+

Sampler Priority -
+

-
- +
+ Ooba only. Determines the order of samplers. +
+
+
Temperature
+
Dynamic Temperature
+
Quadratic / Smooth Sampling
+
Top K
+
Top P
+
Typical P
+
Epsilon Cutoff
+
Eta Cutoff
+
Tail Free Sampling
+
Top A
+
Min P
+
Mirostat
+
+ +
diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index d5bd3ed68..b31c47aaa 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -435,6 +435,16 @@ function sortItemsByOrder(orderArray) { } } +function sortOobaItemsByOrder(orderArray) { + console.debug('Preset samplers order: ', orderArray); + const $container = $('#sampler_priority_container'); + + orderArray.forEach((name) => { + const $item = $container.find(`[data-name="${name}"]`).detach(); + $container.append($item); + }); +} + jQuery(function () { $('#koboldcpp_order').sortable({ delay: getSortableDelay(), @@ -451,9 +461,37 @@ jQuery(function () { $('#koboldcpp_default_order').on('click', function () { settings.sampler_order = KOBOLDCPP_ORDER; - sortItemsByOrder(settings.sampler_order); + sortOobaItemsByOrder(settings.sampler_order); saveSettingsDebounced(); }); + +jQuery(function($) { + $('#sampler_priority_container').sortable({ + delay: getSortableDelay(), + stop: function() { + const order = []; + $('#sampler_priority_container').children().each(function() { + order.push($(this).data('name')); + }); + settings.sampler_priority = order.join('\n'); + console.log('Samplers reordered:', settings.sampler_priority); + saveSettingsDebounced(); + $('#sampler_priority_textgenerationwebui').val(settings.sampler_priority); + } + }); + + $('#textgenerationwebui_default_order').on('click', function () { + const defaultOrder = ['temperature', 'dynamic_temperature', 'quadratic_sampling', 'top_k', 'top_p', 'typical_p', 'epsilon_cutoff', 'eta_cutoff', 'tfs', 'top_a', 'min_p', 'mirostat']; + + sortOobaItemsByOrder(defaultOrder); + settings.sampler_priority = defaultOrder.join('\n'); + console.log('Default samplers order loaded:', settings.sampler_priority); + saveSettingsDebounced(); + $('#sampler_priority_textgenerationwebui').val(settings.sampler_priority); + }); + +}); + $('#textgen_type').on('change', function () { const type = String($(this).val()); From 9fed7ed7424cb9d6f1db197a68ccd21e5cfc8190 Mon Sep 17 00:00:00 2001 From: kalomaze <66376113+kalomaze@users.noreply.github.com> Date: Sat, 10 Feb 2024 17:57:00 -0600 Subject: [PATCH 04/29] Make the neutralize option turn off quad sampling --- public/scripts/textgen-settings.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index b31c47aaa..11efdf952 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -576,6 +576,7 @@ jQuery(function($) { 'penalty_alpha_textgenerationwebui': 0, 'typical_p_textgenerationwebui': 1, // Added entry 'guidance_scale_textgenerationwebui': 1, + 'smoothing_factor_textgenerationwebui': 0, }; for (const [id, value] of Object.entries(inputs)) { From c8b0030f6e3e6e38d0547cbc46122bae8469b643 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:03:56 +0200 Subject: [PATCH 05/29] Extract PNG read/write methods --- src/character-card-parser.js | 89 +++++++++++++++++++++++--------- src/endpoints/characters.js | 21 ++------ src/endpoints/content-manager.js | 67 +++++++++++++++++++++++- 3 files changed, 134 insertions(+), 43 deletions(-) diff --git a/src/character-card-parser.js b/src/character-card-parser.js index 53d430b36..9e9cbd1a7 100644 --- a/src/character-card-parser.js +++ b/src/character-card-parser.js @@ -1,41 +1,80 @@ const fs = require('fs'); +const encode = require('png-chunks-encode'); const extract = require('png-chunks-extract'); const PNGtext = require('png-chunk-text'); -const parse = async (cardUrl, format) => { +/** + * Writes Character metadata to a PNG image buffer. + * @param {Buffer} image PNG image buffer + * @param {string} data Character data to write + * @returns {Buffer} PNG image buffer with metadata + */ +const write = (image, data) => { + const chunks = extract(image); + const tEXtChunks = chunks.filter(chunk => chunk.name === 'tEXt'); + + // Remove all existing tEXt chunks + for (let tEXtChunk of tEXtChunks) { + chunks.splice(chunks.indexOf(tEXtChunk), 1); + } + // Add new chunks before the IEND chunk + const base64EncodedData = Buffer.from(data, 'utf8').toString('base64'); + chunks.splice(-1, 0, PNGtext.encode('chara', base64EncodedData)); + const newBuffer = Buffer.from(encode(chunks)); + return newBuffer; +}; + +/** + * Reads Character metadata from a PNG image buffer. + * @param {Buffer} image PNG image buffer + * @returns {string} Character data + */ +const read = (image) => { + const chunks = extract(image); + + const textChunks = chunks.filter(function (chunk) { + return chunk.name === 'tEXt'; + }).map(function (chunk) { + return PNGtext.decode(chunk.data); + }); + + if (textChunks.length === 0) { + console.error('PNG metadata does not contain any text chunks.'); + throw new Error('No PNG metadata.'); + } + + let index = textChunks.findIndex((chunk) => chunk.keyword.toLowerCase() == 'chara'); + + if (index === -1) { + console.error('PNG metadata does not contain any character data.'); + throw new Error('No PNG metadata.'); + } + + return Buffer.from(textChunks[index].text, 'base64').toString('utf8'); +}; + +/** + * Parses a card image and returns the character metadata. + * @param {string} cardUrl Path to the card image + * @param {string} format File format + * @returns {string} Character data + */ +const parse = (cardUrl, format) => { let fileFormat = format === undefined ? 'png' : format; switch (fileFormat) { case 'png': { const buffer = fs.readFileSync(cardUrl); - const chunks = extract(buffer); - - const textChunks = chunks.filter(function (chunk) { - return chunk.name === 'tEXt'; - }).map(function (chunk) { - return PNGtext.decode(chunk.data); - }); - - if (textChunks.length === 0) { - console.error('PNG metadata does not contain any text chunks.'); - throw new Error('No PNG metadata.'); - } - - let index = textChunks.findIndex((chunk) => chunk.keyword.toLowerCase() == 'chara'); - - if (index === -1) { - console.error('PNG metadata does not contain any character data.'); - throw new Error('No PNG metadata.'); - } - - return Buffer.from(textChunks[index].text, 'base64').toString('utf8'); + return read(buffer); } - default: - break; } + + throw new Error('Unsupported format'); }; module.exports = { - parse: parse, + parse, + write, + read, }; diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index 0cd1c4a02..91c46a2df 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -7,9 +7,6 @@ const writeFileAtomicSync = require('write-file-atomic').sync; const yaml = require('yaml'); const _ = require('lodash'); -const encode = require('png-chunks-encode'); -const extract = require('png-chunks-extract'); -const PNGtext = require('png-chunk-text'); const jimp = require('jimp'); const { DIRECTORIES, UPLOADS_PATH, AVATAR_WIDTH, AVATAR_HEIGHT } = require('../constants'); @@ -33,7 +30,7 @@ const characterDataCache = new Map(); * @param {string} input_format - 'png' * @returns {Promise} - Character card data */ -async function charaRead(img_url, input_format) { +async function charaRead(img_url, input_format = 'png') { const stat = fs.statSync(img_url); const cacheKey = `${img_url}-${stat.mtimeMs}`; if (characterDataCache.has(cacheKey)) { @@ -59,22 +56,12 @@ async function charaWrite(img_url, data, target_img, response = undefined, mes = } } // Read the image, resize, and save it as a PNG into the buffer - const image = await tryReadImage(img_url, crop); + const inputImage = await tryReadImage(img_url, crop); // Get the chunks - const chunks = extract(image); - const tEXtChunks = chunks.filter(chunk => chunk.name === 'tEXt'); + const outputImage = characterCardParser.write(inputImage, data); - // Remove all existing tEXt chunks - for (let tEXtChunk of tEXtChunks) { - chunks.splice(chunks.indexOf(tEXtChunk), 1); - } - // Add new chunks before the IEND chunk - const base64EncodedData = Buffer.from(data, 'utf8').toString('base64'); - chunks.splice(-1, 0, PNGtext.encode('chara', base64EncodedData)); - //chunks.splice(-1, 0, text.encode('lorem', 'ipsum')); - - writeFileAtomicSync(DIRECTORIES.characters + target_img + '.png', Buffer.from(encode(chunks))); + writeFileAtomicSync(DIRECTORIES.characters + target_img + '.png', outputImage); if (response !== undefined) response.send(mes); return true; } catch (err) { diff --git a/src/endpoints/content-manager.js b/src/endpoints/content-manager.js index 191b4f4ce..2cd2d9ce6 100644 --- a/src/endpoints/content-manager.js +++ b/src/endpoints/content-manager.js @@ -10,6 +10,7 @@ const contentLogPath = path.join(contentDirectory, 'content.log'); const contentIndexPath = path.join(contentDirectory, 'index.json'); const { DIRECTORIES } = require('../constants'); const presetFolders = [DIRECTORIES.koboldAI_Settings, DIRECTORIES.openAI_Settings, DIRECTORIES.novelAI_Settings, DIRECTORIES.textGen_Settings]; +const characterCardParser = require('../character-card-parser.js'); /** * Gets the default presets from the content directory. @@ -219,6 +220,60 @@ async function downloadChubCharacter(id) { return { buffer, fileName, fileType }; } +/** + * Downloads a character card from the Pygsite. + * @param {string} id UUID of the character + * @returns {Promise<{buffer: Buffer, fileName: string, fileType: string}>} + */ +async function downloadPygmalionCharacter(id) { + const result = await fetch('https://server.pygmalion.chat/galatea.v1.PublicCharacterService/CharacterExport', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ 'character_id': id }), + }); + + if (!result.ok) { + const text = await result.text(); + console.log('Pygsite returned error', result.statusText, text); + throw new Error('Failed to download character'); + } + + const jsonData = await result.json(); + const card = jsonData?.card; + + if (!card || typeof card !== 'object') { + console.error('Pygsite returned invalid character data', jsonData); + throw new Error('Failed to download character'); + } + + try { + const avatarUrl = card?.data?.avatar; + + if (!avatarUrl) { + console.error('Pygsite character does not have an avatar', card); + throw new Error('Failed to download avatar'); + } + + const avatarResult = await fetch(avatarUrl); + const avatarBuffer = await avatarResult.buffer(); + + const cardBuffer = characterCardParser.write(avatarBuffer, JSON.stringify(card)); + + return { + buffer: cardBuffer, + fileName: `${sanitize(id)}.png`, + fileType: 'image/png', + }; + } catch(e) { + console.error('Failed to download avatar, using JSON instead', e); + return { + buffer: Buffer.from(JSON.stringify(jsonData)), + fileName: `${sanitize(id)}.json`, + fileType: 'application/json', + }; + } +} + /** * * @param {String} str @@ -317,7 +372,17 @@ router.post('/import', jsonParser, async (request, response) => { let type; const isJannnyContent = url.includes('janitorai'); - if (isJannnyContent) { + const isPygmalionContent = url.includes('pygmalion.chat'); + + if (isPygmalionContent) { + const uuid = url.split('/').pop(); + if (!uuid) { + return response.sendStatus(404); + } + + type = 'character'; + result = await downloadPygmalionCharacter(uuid); + } else if (isJannnyContent) { const uuid = parseJannyUrl(url); if (!uuid) { return response.sendStatus(404); From 7fbef328697576f0512278b5fa8fe4a26af7c5dd Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 16 Feb 2024 20:24:06 +0200 Subject: [PATCH 06/29] Use uuid extraction from Pygsite URL --- src/endpoints/content-manager.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/endpoints/content-manager.js b/src/endpoints/content-manager.js index 2cd2d9ce6..2f9812270 100644 --- a/src/endpoints/content-manager.js +++ b/src/endpoints/content-manager.js @@ -234,7 +234,7 @@ async function downloadPygmalionCharacter(id) { if (!result.ok) { const text = await result.text(); - console.log('Pygsite returned error', result.statusText, text); + console.log('Pygsite returned error', result.status, text); throw new Error('Failed to download character'); } @@ -264,7 +264,7 @@ async function downloadPygmalionCharacter(id) { fileName: `${sanitize(id)}.png`, fileType: 'image/png', }; - } catch(e) { + } catch (e) { console.error('Failed to download avatar, using JSON instead', e); return { buffer: Buffer.from(JSON.stringify(jsonData)), @@ -349,7 +349,7 @@ async function downloadJannyCharacter(uuid) { * @param {String} url * @returns {String | null } UUID of the character */ -function parseJannyUrl(url) { +function getUuidFromUrl(url) { // Extract UUID from URL const uuidRegex = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/; const matches = url.match(uuidRegex); @@ -375,7 +375,7 @@ router.post('/import', jsonParser, async (request, response) => { const isPygmalionContent = url.includes('pygmalion.chat'); if (isPygmalionContent) { - const uuid = url.split('/').pop(); + const uuid = getUuidFromUrl(url); if (!uuid) { return response.sendStatus(404); } @@ -383,7 +383,7 @@ router.post('/import', jsonParser, async (request, response) => { type = 'character'; result = await downloadPygmalionCharacter(uuid); } else if (isJannnyContent) { - const uuid = parseJannyUrl(url); + const uuid = getUuidFromUrl(url); if (!uuid) { return response.sendStatus(404); } From c20a9fb5f5e65706e5f776f9ec64cb5c15ba3006 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 17 Feb 2024 03:52:45 +0200 Subject: [PATCH 07/29] Add HTTP/2 workaround for pygsite import --- src/endpoints/content-manager.js | 77 ++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/src/endpoints/content-manager.js b/src/endpoints/content-manager.js index 2f9812270..b6abef760 100644 --- a/src/endpoints/content-manager.js +++ b/src/endpoints/content-manager.js @@ -220,25 +220,76 @@ async function downloadChubCharacter(id) { return { buffer, fileName, fileType }; } +/** + * Makes an HTTP/2 request to the specified endpoint. + * + * THIS IS A WORKAROUND FOR NODE-FETCH NOT SUPPORTING HTTP/2 AND PYGSITE USING BROKEN AHH AWS LOAD BALANCER. + * @param {string} endpoint URL to make the request to + * @param {string} method HTTP method to use + * @param {string} body Request body + * @param {object} headers Request headers + * @returns {Promise} Response body + */ +function makeHttp2Request(endpoint, method, body, headers) { + return new Promise((resolve, reject) => { + try { + const http2 = require('http2'); + const url = new URL(endpoint); + const client = http2.connect(url.origin); + + const req = client.request({ + ':method': method, + ':path': url.pathname, + ...headers, + }); + req.setEncoding('utf8'); + + req.on('response', (headers) => { + const status = Number(headers[':status']); + + if (status < 200 || status >= 300) { + reject(new Error(`Request failed with status ${status}`)); + } + + let data = ''; + + req.on('data', (chunk) => { + data += chunk; + }); + + req.on('end', () => { + resolve(data); + }); + }); + + req.on('error', (err) => { + reject(err); + }); + + if (body) { + req.write(body); + } + + req.end(); + } catch (e) { + reject(e); + } + }); +} + /** * Downloads a character card from the Pygsite. * @param {string} id UUID of the character * @returns {Promise<{buffer: Buffer, fileName: string, fileType: string}>} */ async function downloadPygmalionCharacter(id) { - const result = await fetch('https://server.pygmalion.chat/galatea.v1.PublicCharacterService/CharacterExport', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ 'character_id': id }), - }); - - if (!result.ok) { - const text = await result.text(); - console.log('Pygsite returned error', result.status, text); - throw new Error('Failed to download character'); - } - - const jsonData = await result.json(); + const result = await makeHttp2Request( + 'https://server.pygmalion.chat/galatea.v1.PublicCharacterService/CharacterExport', + 'POST', + JSON.stringify({ 'character_id': id }), + { 'content-type': 'application/json' }, + ); + const jsonData = JSON.parse(result); const card = jsonData?.card; if (!card || typeof card !== 'object') { From e4a48cd28f0c77f90fdc514802cd36264bcb1290 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 17 Feb 2024 03:54:13 +0200 Subject: [PATCH 08/29] Add pyg hint to import UI --- public/script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/script.js b/public/script.js index 066a02f99..a11c594e5 100644 --- a/public/script.js +++ b/public/script.js @@ -9856,6 +9856,7 @@ jQuery(async function () {
  • Chub characters (direct link or id)
    Example: Anonymous/example-character
  • Chub lorebooks (direct link or id)
    Example: lorebooks/bartleby/example-lorebook
  • JanitorAI character (direct link or id)
    Example: https://janitorai.com/characters/ddd1498a-a370-4136-b138-a8cd9461fdfe_character-aqua-the-useless-goddess
  • +
  • Pygmalion.chat character (link)
    Example: https://pygmalion.chat/character/a7ca95a1-0c88-4e23-91b3-149db1e78ab9
  • More coming soon...
    • `; const input = await callPopup(html, 'input', '', { okButton: 'Import', rows: 4 }); From 0391179c3ce5ac852347e1c235ce5cd985594878 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 17 Feb 2024 17:04:37 +0200 Subject: [PATCH 09/29] Remove HTTP/2 workaround for pygsite --- src/endpoints/content-manager.js | 77 ++++++-------------------------- src/util.js | 61 ++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 65 deletions(-) diff --git a/src/endpoints/content-manager.js b/src/endpoints/content-manager.js index b6abef760..2f9812270 100644 --- a/src/endpoints/content-manager.js +++ b/src/endpoints/content-manager.js @@ -220,76 +220,25 @@ async function downloadChubCharacter(id) { return { buffer, fileName, fileType }; } -/** - * Makes an HTTP/2 request to the specified endpoint. - * - * THIS IS A WORKAROUND FOR NODE-FETCH NOT SUPPORTING HTTP/2 AND PYGSITE USING BROKEN AHH AWS LOAD BALANCER. - * @param {string} endpoint URL to make the request to - * @param {string} method HTTP method to use - * @param {string} body Request body - * @param {object} headers Request headers - * @returns {Promise} Response body - */ -function makeHttp2Request(endpoint, method, body, headers) { - return new Promise((resolve, reject) => { - try { - const http2 = require('http2'); - const url = new URL(endpoint); - const client = http2.connect(url.origin); - - const req = client.request({ - ':method': method, - ':path': url.pathname, - ...headers, - }); - req.setEncoding('utf8'); - - req.on('response', (headers) => { - const status = Number(headers[':status']); - - if (status < 200 || status >= 300) { - reject(new Error(`Request failed with status ${status}`)); - } - - let data = ''; - - req.on('data', (chunk) => { - data += chunk; - }); - - req.on('end', () => { - resolve(data); - }); - }); - - req.on('error', (err) => { - reject(err); - }); - - if (body) { - req.write(body); - } - - req.end(); - } catch (e) { - reject(e); - } - }); -} - /** * Downloads a character card from the Pygsite. * @param {string} id UUID of the character * @returns {Promise<{buffer: Buffer, fileName: string, fileType: string}>} */ async function downloadPygmalionCharacter(id) { - const result = await makeHttp2Request( - 'https://server.pygmalion.chat/galatea.v1.PublicCharacterService/CharacterExport', - 'POST', - JSON.stringify({ 'character_id': id }), - { 'content-type': 'application/json' }, - ); - const jsonData = JSON.parse(result); + const result = await fetch('https://server.pygmalion.chat/galatea.v1.PublicCharacterService/CharacterExport', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ 'character_id': id }), + }); + + if (!result.ok) { + const text = await result.text(); + console.log('Pygsite returned error', result.status, text); + throw new Error('Failed to download character'); + } + + const jsonData = await result.json(); const card = jsonData?.card; if (!card || typeof card !== 'object') { diff --git a/src/util.js b/src/util.js index 1d437379e..4f05fc0c6 100644 --- a/src/util.js +++ b/src/util.js @@ -365,7 +365,7 @@ function getImages(path) { /** * Pipe a fetch() response to an Express.js Response, including status code. * @param {import('node-fetch').Response} from The Fetch API response to pipe from. - * @param {Express.Response} to The Express response to pipe to. + * @param {import('express').Response} to The Express response to pipe to. */ function forwardFetchResponse(from, to) { let statusCode = from.status; @@ -399,6 +399,64 @@ function forwardFetchResponse(from, to) { }); } +/** + * Makes an HTTP/2 request to the specified endpoint. + * + * @deprecated Use `node-fetch` if possible. + * @param {string} endpoint URL to make the request to + * @param {string} method HTTP method to use + * @param {string} body Request body + * @param {object} headers Request headers + * @returns {Promise} Response body + */ +function makeHttp2Request(endpoint, method, body, headers) { + return new Promise((resolve, reject) => { + try { + const http2 = require('http2'); + const url = new URL(endpoint); + const client = http2.connect(url.origin); + + const req = client.request({ + ':method': method, + ':path': url.pathname, + ...headers, + }); + req.setEncoding('utf8'); + + req.on('response', (headers) => { + const status = Number(headers[':status']); + + if (status < 200 || status >= 300) { + reject(new Error(`Request failed with status ${status}`)); + } + + let data = ''; + + req.on('data', (chunk) => { + data += chunk; + }); + + req.on('end', () => { + console.log(data); + resolve(data); + }); + }); + + req.on('error', (err) => { + reject(err); + }); + + if (body) { + req.write(body); + } + + req.end(); + } catch (e) { + reject(e); + } + }); +} + /** * Adds YAML-serialized object to the object. * @param {object} obj Object @@ -547,4 +605,5 @@ module.exports = { excludeKeysByYaml, trimV1, Cache, + makeHttp2Request, }; From 2e00a1baaf34cc1790bea058ccf0f93ec4fa727f Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 19 Feb 2024 19:37:18 +0200 Subject: [PATCH 10/29] [FEATURE_REQUEST] Can the unlocked max context size for OpenAI completion be increased from 102k to 200k for example? #1842 --- public/scripts/openai.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index e1552090f..e655d6a18 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -119,7 +119,7 @@ const scale_max = 8191; const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k) const claude_100k_max = 99000; let ai21_max = 9200; //can easily fit 9k gpt tokens because j2's tokenizer is efficient af -const unlocked_max = 100 * 1024; +const unlocked_max = max_200k; const oai_max_temp = 2.0; const claude_max_temp = 1.0; //same as j2 const j2_max_topk = 10.0; From 550d8483cc527ff2b30534702ab631e945cbfda0 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 19 Feb 2024 01:17:04 +0100 Subject: [PATCH 11/29] Extend impersonate/continue/regenerate with possible custom prompts - Use custom prompt provided via slash command arguments (similar to /sysgen and others) - Use written text from textbox, if the popout menu actions are clicked --- public/script.js | 17 ++++++++++++----- public/scripts/slash-commands.js | 4 ++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/public/script.js b/public/script.js index b9df03842..188e0900f 100644 --- a/public/script.js +++ b/public/script.js @@ -7864,9 +7864,9 @@ async function importFromURL(items, files) { } } -async function doImpersonate() { +async function doImpersonate(_, prompt) { $('#send_textarea').val(''); - $('#option_impersonate').trigger('click', { fromSlashCommand: true }); + $('#option_impersonate').trigger('click', { fromSlashCommand: true, additionalPrompt: prompt }); } async function doDeleteChat() { @@ -8682,6 +8682,13 @@ jQuery(async function () { const fromSlashCommand = customData?.fromSlashCommand || false; var id = $(this).attr('id'); + // Check whether a custom prompt was provided via custom data (for example through a slash command), otherwise fall back and use the text from the textbox, if there is any + const additionalPrompt = (customData?.additionalPrompt && customData.additionalPrompt.trim()) || (String($('#send_textarea').val()).trim() || undefined); + const buildOrFillAdditionalArgs = (args = {}) => ({ + ...args, + ...(additionalPrompt !== undefined && { quiet_prompt: additionalPrompt, quietToLoud: true }), + }); + if (id == 'option_select_chat') { if ((selected_group && !is_group_generating) || (this_chid !== undefined && !is_send_press) || fromSlashCommand) { await displayPastChats(); @@ -8717,7 +8724,7 @@ jQuery(async function () { } else { is_send_press = true; - Generate('regenerate'); + Generate('regenerate', buildOrFillAdditionalArgs()); } } } @@ -8725,14 +8732,14 @@ jQuery(async function () { else if (id == 'option_impersonate') { if (is_send_press == false || fromSlashCommand) { is_send_press = true; - Generate('impersonate'); + Generate('impersonate', buildOrFillAdditionalArgs()); } } else if (id == 'option_continue') { if (is_send_press == false || fromSlashCommand) { is_send_press = true; - Generate('continue'); + Generate('continue', buildOrFillAdditionalArgs()); } } diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 9a4175a1e..820b43ea6 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -1168,7 +1168,7 @@ async function openChat(id) { await reloadCurrentChat(); } -function continueChatCallback() { +function continueChatCallback(_, prompt) { setTimeout(async () => { try { await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100); @@ -1179,7 +1179,7 @@ function continueChatCallback() { // Prevent infinite recursion $('#send_textarea').val('').trigger('input'); - $('#option_continue').trigger('click', { fromSlashCommand: true }); + $('#option_continue').trigger('click', { fromSlashCommand: true, additionalPrompt: prompt }); }, 1); return ''; From a5ee46cb2a4c0ba3aa1fea2ff3ddfb2c21753a28 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 19 Feb 2024 22:36:32 +0100 Subject: [PATCH 12/29] Only respect slash command, ignore text field --- public/script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/script.js b/public/script.js index 188e0900f..c38eab959 100644 --- a/public/script.js +++ b/public/script.js @@ -8682,8 +8682,8 @@ jQuery(async function () { const fromSlashCommand = customData?.fromSlashCommand || false; var id = $(this).attr('id'); - // Check whether a custom prompt was provided via custom data (for example through a slash command), otherwise fall back and use the text from the textbox, if there is any - const additionalPrompt = (customData?.additionalPrompt && customData.additionalPrompt.trim()) || (String($('#send_textarea').val()).trim() || undefined); + // Check whether a custom prompt was provided via custom data (for example through a slash command) + const additionalPrompt = customData?.additionalPrompt?.trim() || undefined; const buildOrFillAdditionalArgs = (args = {}) => ({ ...args, ...(additionalPrompt !== undefined && { quiet_prompt: additionalPrompt, quietToLoud: true }), From 061b7c6922c9530101566888c14c021c5b3fc5d8 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 20 Feb 2024 02:09:01 +0200 Subject: [PATCH 13/29] Don't try to execute script commands if the message doesn't start with slash --- public/script.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/public/script.js b/public/script.js index b9df03842..3087922b8 100644 --- a/public/script.js +++ b/public/script.js @@ -2270,12 +2270,21 @@ export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, q return generateFinished; } +/** + * Executes slash commands and returns the new text and whether the generation was interrupted. + * @param {string} message Text to be sent + * @returns {Promise} Whether the message sending was interrupted + */ async function processCommands(message) { + if (!message || !message.trim().startsWith('/')) { + return false; + } + const previousText = String($('#send_textarea').val()); const result = await executeSlashCommands(message); if (!result || typeof result !== 'object') { - return null; + return false; } const currentText = String($('#send_textarea').val()); @@ -2878,7 +2887,7 @@ async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, qu let message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `; if (!(dryRun || type == 'regenerate' || type == 'swipe' || type == 'quiet')) { - const interruptedByCommand = await processCommands($('#send_textarea').val()); + const interruptedByCommand = await processCommands(String($('#send_textarea').val())); if (interruptedByCommand) { //$("#send_textarea").val('').trigger('input'); From 32ee58e5e6a3a324fa94ed1c756b84128541d5a1 Mon Sep 17 00:00:00 2001 From: kalomaze <66376113+kalomaze@users.noreply.github.com> Date: Mon, 19 Feb 2024 18:12:56 -0600 Subject: [PATCH 14/29] fix kcpp order reset --- public/scripts/textgen-settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index 11efdf952..405f34cd3 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -461,7 +461,7 @@ jQuery(function () { $('#koboldcpp_default_order').on('click', function () { settings.sampler_order = KOBOLDCPP_ORDER; - sortOobaItemsByOrder(settings.sampler_order); + sortItemsByOrder(settings.sampler_order); saveSettingsDebounced(); }); From f3971686eaa1277dd9e971f1b1a501299aa19377 Mon Sep 17 00:00:00 2001 From: kalomaze <66376113+kalomaze@users.noreply.github.com> Date: Mon, 19 Feb 2024 18:18:57 -0600 Subject: [PATCH 15/29] Move text-gen-webui sampler order under kcpp order --- public/index.html | 58 +++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/public/index.html b/public/index.html index 4a01ccf28..1dc60cc0b 100644 --- a/public/index.html +++ b/public/index.html @@ -1451,35 +1451,6 @@
    -
    -
    -

    - Sampler Priority -
    -

    -
    - Ooba only. Determines the order of samplers. -
    -
    -
    Temperature
    -
    Dynamic Temperature
    -
    Quadratic / Smooth Sampling
    -
    Top K
    -
    Top P
    -
    Typical P
    -
    Epsilon Cutoff
    -
    Eta Cutoff
    -
    Tail Free Sampling
    -
    Top A
    -
    Min P
    -
    Mirostat
    -
    - - -
    Logit Bias @@ -1570,6 +1541,35 @@ +
    +
    +

    + Sampler Priority +
    +

    +
    + Ooba only. Determines the order of samplers. +
    +
    +
    Temperature
    +
    Dynamic Temperature
    +
    Quadratic / Smooth Sampling
    +
    Top K
    +
    Top P
    +
    Typical P
    +
    Epsilon Cutoff
    +
    Eta Cutoff
    +
    Tail Free Sampling
    +
    Top A
    +
    Min P
    +
    Mirostat
    +
    + + +
    From cec0698400cd08fb7fe23a994f6175147003d361 Mon Sep 17 00:00:00 2001 From: kalomaze <66376113+kalomaze@users.noreply.github.com> Date: Mon, 19 Feb 2024 18:24:04 -0600 Subject: [PATCH 16/29] Oopsie --- public/index.html | 56 +++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/public/index.html b/public/index.html index 1dc60cc0b..de450ac6a 100644 --- a/public/index.html +++ b/public/index.html @@ -1541,36 +1541,36 @@ -
    -
    -

    - Sampler Priority -
    -

    -
    - Ooba only. Determines the order of samplers. -
    -
    -
    Temperature
    -
    Dynamic Temperature
    -
    Quadratic / Smooth Sampling
    -
    Top K
    -
    Top P
    -
    Typical P
    -
    Epsilon Cutoff
    -
    Eta Cutoff
    -
    Tail Free Sampling
    -
    Top A
    -
    Min P
    -
    Mirostat
    -
    - -
    +
    +
    +

    + Sampler Priority +
    +

    +
    + Ooba only. Determines the order of samplers. +
    +
    +
    Temperature
    +
    Dynamic Temperature
    +
    Quadratic / Smooth Sampling
    +
    Top K
    +
    Top P
    +
    Typical P
    +
    Epsilon Cutoff
    +
    Eta Cutoff
    +
    Tail Free Sampling
    +
    Top A
    +
    Min P
    +
    Mirostat
    +
    + +
    From 8e66a14e37453bedb7564b4efd19739c332e4c82 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 20 Feb 2024 02:29:14 +0200 Subject: [PATCH 17/29] Add hints to doc strings about additional command prompts --- public/script.js | 2 +- public/scripts/slash-commands.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/script.js b/public/script.js index 064f5a46c..8ee918f39 100644 --- a/public/script.js +++ b/public/script.js @@ -8042,7 +8042,7 @@ jQuery(async function () { registerSlashCommand('dupe', DupeChar, [], '– duplicates the currently selected character', true, true); registerSlashCommand('api', connectAPISlash, [], `(${Object.keys(CONNECT_API_MAP).join(', ')}) – connect to an API`, true, true); - registerSlashCommand('impersonate', doImpersonate, ['imp'], '– calls an impersonation response', true, true); + registerSlashCommand('impersonate', doImpersonate, ['imp'], '[prompt] – calls an impersonation response, with an optional additional prompt', true, true); registerSlashCommand('delchat', doDeleteChat, [], '– deletes the current chat', true, true); registerSlashCommand('getchatname', doGetChatName, [], '– returns the name of the current chat file into the pipe', false, true); registerSlashCommand('closechat', doCloseChat, [], '– closes the current chat', true, true); diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 820b43ea6..fdfc1acd4 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -150,7 +150,7 @@ parser.addCommand('comment', sendCommentMessage, [], '(t parser.addCommand('single', setStoryModeCallback, ['story'], ' – sets the message style to single document mode without names or avatars visible', true, true); parser.addCommand('bubble', setBubbleModeCallback, ['bubbles'], ' – sets the message style to bubble chat mode', true, true); parser.addCommand('flat', setFlatModeCallback, ['default'], ' – sets the message style to flat chat mode', true, true); -parser.addCommand('continue', continueChatCallback, ['cont'], ' – continues the last message in the chat', true, true); +parser.addCommand('continue', continueChatCallback, ['cont'], '[prompt] – continues the last message in the chat, with an optional additional prompt', true, true); parser.addCommand('go', goToCharacterCallback, ['char'], '(name) – opens up a chat with the character or group by its name', true, true); parser.addCommand('sysgen', generateSystemMessage, [], '(prompt) – generates a system message using a specified prompt', true, true); parser.addCommand('ask', askCharacter, [], '(prompt) – asks a specified character card a prompt', true, true); From 095cd873ded1812a384edec7813eb9ac93eee2c7 Mon Sep 17 00:00:00 2001 From: Sneha C <136328295+underscorex86@users.noreply.github.com> Date: Tue, 20 Feb 2024 16:48:43 +0400 Subject: [PATCH 18/29] Update slash-commands.js added the word "persona" to the /sync description to make it easier for users to find. --- public/scripts/slash-commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index fdfc1acd4..ddc31dfc9 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -140,7 +140,7 @@ const getSlashCommandsHelp = parser.getHelpString.bind(parser); parser.addCommand('?', helpCommandCallback, ['help'], ' – get help on macros, chat formatting and commands', true, true); parser.addCommand('name', setNameCallback, ['persona'], '(name) – sets user name and persona avatar (if set)', true, true); -parser.addCommand('sync', syncCallback, [], ' – syncs user name in user-attributed messages in the current chat', true, true); +parser.addCommand('sync', syncCallback, [], ' – syncs user name persona in user-attributed messages in the current chat', true, true); parser.addCommand('lock', bindCallback, ['bind'], ' – locks/unlocks a persona (name and avatar) to the current chat', true, true); parser.addCommand('bg', setBackgroundCallback, ['background'], '(filename) – sets a background according to filename, partial names allowed', false, true); parser.addCommand('sendas', sendMessageAs, [], ' – sends message as a specific character. Uses character avatar if it exists in the characters list. Example that will send "Hello, guys!" from "Chloe": /sendas name="Chloe" Hello, guys!', true, true); From f0141b4dd13c780f445454aa85f5118fd6dbc45a Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 20 Feb 2024 16:57:00 +0200 Subject: [PATCH 19/29] Update slash-commands.js --- public/scripts/slash-commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index ddc31dfc9..e6c50ebb3 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -140,7 +140,7 @@ const getSlashCommandsHelp = parser.getHelpString.bind(parser); parser.addCommand('?', helpCommandCallback, ['help'], ' – get help on macros, chat formatting and commands', true, true); parser.addCommand('name', setNameCallback, ['persona'], '(name) – sets user name and persona avatar (if set)', true, true); -parser.addCommand('sync', syncCallback, [], ' – syncs user name persona in user-attributed messages in the current chat', true, true); +parser.addCommand('sync', syncCallback, [], ' – syncs the user persona in user-attributed messages in the current chat', true, true); parser.addCommand('lock', bindCallback, ['bind'], ' – locks/unlocks a persona (name and avatar) to the current chat', true, true); parser.addCommand('bg', setBackgroundCallback, ['background'], '(filename) – sets a background according to filename, partial names allowed', false, true); parser.addCommand('sendas', sendMessageAs, [], ' – sends message as a specific character. Uses character avatar if it exists in the characters list. Example that will send "Hello, guys!" from "Chloe": /sendas name="Chloe" Hello, guys!', true, true); From 0c1cf9ff2eecb5b18210252de709bb6560216fe7 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 21 Feb 2024 00:53:54 +0200 Subject: [PATCH 20/29] Send sampler priority as array --- default/settings.json | 15 +++++++- public/index.html | 2 - public/scripts/textgen-settings.js | 59 ++++++++++++++++++------------ 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/default/settings.json b/default/settings.json index bdd82b11a..4383798ff 100644 --- a/default/settings.json +++ b/default/settings.json @@ -47,7 +47,20 @@ "ban_eos_token": false, "skip_special_tokens": true, "streaming": false, - "sampler_priority": "temperature\ndynamic_temperature\nquadratic_sampling\ntop_k\ntop_p\ntypical_p\nepsilon_cutoff\neta_cutoff\ntfs\ntop_a\nmin_p\nmirostat" + "sampler_priority": [ + "temperature", + "dynamic_temperature", + "quadratic_sampling", + "top_k", + "top_p", + "typical_p", + "epsilon_cutoff", + "eta_cutoff", + "tfs", + "top_a", + "min_p", + "mirostat" + ], "mirostat_mode": 0, "mirostat_tau": 5, "mirostat_eta": 0.1, diff --git a/public/index.html b/public/index.html index f78c37a74..f820d9b26 100644 --- a/public/index.html +++ b/public/index.html @@ -1574,8 +1574,6 @@
    Min P
    Mirostat
    - diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index 405f34cd3..3c3236c6d 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -34,6 +34,20 @@ export const textgen_types = { }; const { MANCER, APHRODITE, TABBY, TOGETHERAI, OOBA, OLLAMA, LLAMACPP } = textgen_types; +const OOBA_DEFAULT_ORDER = [ + 'temperature', + 'dynamic_temperature', + 'quadratic_sampling', + 'top_k', + 'top_p', + 'typical_p', + 'epsilon_cutoff', + 'eta_cutoff', + 'tfs', + 'top_a', + 'min_p', + 'mirostat', +]; const BIAS_KEY = '#textgenerationwebui_api-settings'; // Maybe let it be configurable in the future? @@ -96,7 +110,7 @@ const settings = { negative_prompt: '', grammar_string: '', banned_tokens: '', - sampler_priority: '', + sampler_priority: OOBA_DEFAULT_ORDER, //n_aphrodite: 1, //best_of_aphrodite: 1, ignore_eos_token_aphrodite: false, @@ -424,7 +438,7 @@ function loadTextGenSettings(data, loadedSettings) { * Sorts the sampler items by the given order. * @param {any[]} orderArray Sampler order array. */ -function sortItemsByOrder(orderArray) { +function sortKoboldItemsByOrder(orderArray) { console.debug('Preset samplers order: ' + orderArray); const $draggableItems = $('#koboldcpp_order'); @@ -461,37 +475,29 @@ jQuery(function () { $('#koboldcpp_default_order').on('click', function () { settings.sampler_order = KOBOLDCPP_ORDER; - sortItemsByOrder(settings.sampler_order); + sortKoboldItemsByOrder(settings.sampler_order); saveSettingsDebounced(); }); - -jQuery(function($) { + $('#sampler_priority_container').sortable({ delay: getSortableDelay(), - stop: function() { + stop: function () { const order = []; - $('#sampler_priority_container').children().each(function() { + $('#sampler_priority_container').children().each(function () { order.push($(this).data('name')); }); - settings.sampler_priority = order.join('\n'); + settings.sampler_priority = order; console.log('Samplers reordered:', settings.sampler_priority); saveSettingsDebounced(); - $('#sampler_priority_textgenerationwebui').val(settings.sampler_priority); - } + }, }); - $('#textgenerationwebui_default_order').on('click', function () { - const defaultOrder = ['temperature', 'dynamic_temperature', 'quadratic_sampling', 'top_k', 'top_p', 'typical_p', 'epsilon_cutoff', 'eta_cutoff', 'tfs', 'top_a', 'min_p', 'mirostat']; - - sortOobaItemsByOrder(defaultOrder); - settings.sampler_priority = defaultOrder.join('\n'); - console.log('Default samplers order loaded:', settings.sampler_priority); - saveSettingsDebounced(); - $('#sampler_priority_textgenerationwebui').val(settings.sampler_priority); - }); - -}); - + $('#textgenerationwebui_default_order').on('click', function () { + sortOobaItemsByOrder(OOBA_DEFAULT_ORDER); + settings.sampler_priority = OOBA_DEFAULT_ORDER; + console.log('Default samplers order loaded:', settings.sampler_priority); + saveSettingsDebounced(); + }); $('#textgen_type').on('change', function () { const type = String($(this).val()); @@ -656,11 +662,18 @@ function setSettingByName(setting, value, trigger) { if ('sampler_order' === setting) { value = Array.isArray(value) ? value : KOBOLDCPP_ORDER; - sortItemsByOrder(value); + sortKoboldItemsByOrder(value); settings.sampler_order = value; return; } + if ('sampler_priority' === setting) { + value = Array.isArray(value) ? value : OOBA_DEFAULT_ORDER; + sortOobaItemsByOrder(value); + settings.sampler_priority = value; + return; + } + if ('logit_bias' === setting) { settings.logit_bias = Array.isArray(value) ? value : []; return; From 96f1ce1fcea57d021de565b5339c5c35686540a5 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 21 Feb 2024 00:55:30 +0200 Subject: [PATCH 21/29] Skill issue? --- public/index.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/index.html b/public/index.html index f820d9b26..0d7b1cab7 100644 --- a/public/index.html +++ b/public/index.html @@ -1549,9 +1549,8 @@ - -
    +

    Sampler Priority From d353fa58d0107b0ddd4a6019cf561f7bfb01bbd4 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 21 Feb 2024 00:56:40 +0200 Subject: [PATCH 22/29] Close div properly --- public/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/public/index.html b/public/index.html index 0d7b1cab7..e7f8ef81d 100644 --- a/public/index.html +++ b/public/index.html @@ -1576,6 +1576,7 @@ +

    From 10fb69f36aaeb4d4fae6a8332b1181f59c85a037 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 21 Feb 2024 00:59:38 +0200 Subject: [PATCH 23/29] Widen the block --- public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index e7f8ef81d..1c542af34 100644 --- a/public/index.html +++ b/public/index.html @@ -1550,7 +1550,7 @@ Load default order
    -
    +

    Sampler Priority From d31195a704bf507cfb363b9c7981a07fe58d0f5b Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 21 Feb 2024 01:02:23 +0200 Subject: [PATCH 24/29] Apply same width for Kobold order Just in case --- public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 1c542af34..642344aa0 100644 --- a/public/index.html +++ b/public/index.html @@ -1507,7 +1507,7 @@

    -
    +

    Samplers Order From 92af4137a95643be7dc225f1ae48ec4c3dd6c77c Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 21 Feb 2024 11:28:59 +0200 Subject: [PATCH 25/29] Use new export endpoint --- src/endpoints/content-manager.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/endpoints/content-manager.js b/src/endpoints/content-manager.js index 2f9812270..727715a95 100644 --- a/src/endpoints/content-manager.js +++ b/src/endpoints/content-manager.js @@ -226,10 +226,8 @@ async function downloadChubCharacter(id) { * @returns {Promise<{buffer: Buffer, fileName: string, fileType: string}>} */ async function downloadPygmalionCharacter(id) { - const result = await fetch('https://server.pygmalion.chat/galatea.v1.PublicCharacterService/CharacterExport', { + const result = await fetch(`https://server.pygmalion.chat/api/export/character/${id}/v2`, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ 'character_id': id }), }); if (!result.ok) { From fb6fa54c7fb3ad317d5293a92d9ad03d30530cd7 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 21 Feb 2024 19:57:38 +0200 Subject: [PATCH 26/29] Fix import fetch HTTP method --- src/endpoints/content-manager.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/endpoints/content-manager.js b/src/endpoints/content-manager.js index 727715a95..32877173d 100644 --- a/src/endpoints/content-manager.js +++ b/src/endpoints/content-manager.js @@ -226,9 +226,7 @@ async function downloadChubCharacter(id) { * @returns {Promise<{buffer: Buffer, fileName: string, fileType: string}>} */ async function downloadPygmalionCharacter(id) { - const result = await fetch(`https://server.pygmalion.chat/api/export/character/${id}/v2`, { - method: 'POST', - }); + const result = await fetch(`https://server.pygmalion.chat/api/export/character/${id}/v2`); if (!result.ok) { const text = await result.text(); @@ -237,25 +235,25 @@ async function downloadPygmalionCharacter(id) { } const jsonData = await result.json(); - const card = jsonData?.card; + const characterData = jsonData?.character; - if (!card || typeof card !== 'object') { + if (!characterData || typeof characterData !== 'object') { console.error('Pygsite returned invalid character data', jsonData); throw new Error('Failed to download character'); } try { - const avatarUrl = card?.data?.avatar; + const avatarUrl = characterData?.data?.avatar; if (!avatarUrl) { - console.error('Pygsite character does not have an avatar', card); + console.error('Pygsite character does not have an avatar', characterData); throw new Error('Failed to download avatar'); } const avatarResult = await fetch(avatarUrl); const avatarBuffer = await avatarResult.buffer(); - const cardBuffer = characterCardParser.write(avatarBuffer, JSON.stringify(card)); + const cardBuffer = characterCardParser.write(avatarBuffer, JSON.stringify(characterData)); return { buffer: cardBuffer, From 0ccdfe4bb7d7c1e8cac8cbbc7c1cf31970e5a446 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 22 Feb 2024 02:45:35 +0200 Subject: [PATCH 27/29] Fix duped line --- public/scripts/textgen-settings.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index 3c3236c6d..b0cae37c7 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -881,7 +881,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, 'dynatemp_range': settings.dynatemp ? (settings.max_temp - settings.min_temp) / 2 : 0, 'dynatemp_exponent': settings.dynatemp ? settings.dynatemp_exponent : 1, 'smoothing_factor': settings.smoothing_factor, - 'sampler_priority': (settings.type === OOBA) ? settings.sampler_priority : undefined, + 'sampler_priority': settings.type === OOBA ? settings.sampler_priority : undefined, 'stopping_strings': getStoppingStrings(isImpersonate, isContinue), 'stop': getStoppingStrings(isImpersonate, isContinue), 'truncation_length': max_context, @@ -915,7 +915,6 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, 'guidance_scale': cfgValues?.guidanceScale?.value ?? settings.guidance_scale ?? 1, 'negative_prompt': cfgValues?.negativePrompt ?? substituteParams(settings.negative_prompt) ?? '', 'grammar_string': settings.grammar_string, - 'sampler_priority': (settings.type === OOBA) ? settings.sampler_priority : undefined, // llama.cpp aliases. In case someone wants to use LM Studio as Text Completion API 'repeat_penalty': settings.rep_pen, 'tfs_z': settings.tfs, From ece3b2a7c1c9457c993e72a7dcccb2ec6b524b36 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 22 Feb 2024 04:36:06 +0200 Subject: [PATCH 28/29] Fix Chat Completions status check on settings loading if another API is selected --- public/scripts/openai.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index e655d6a18..28cdeccec 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -3739,9 +3739,11 @@ async function testApiConnection() { } function reconnectOpenAi() { - setOnlineStatus('no_connection'); - resultCheckStatus(); - $('#api_button_openai').trigger('click'); + if (main_api == 'openai') { + setOnlineStatus('no_connection'); + resultCheckStatus(); + $('#api_button_openai').trigger('click'); + } } function onProxyPasswordShowClick() { @@ -4202,11 +4204,7 @@ $(document).ready(async function () { oai_settings.chat_completion_source = String($(this).find(':selected').val()); toggleChatCompletionForms(); saveSettingsDebounced(); - - if (main_api == 'openai') { - reconnectOpenAi(); - } - + reconnectOpenAi(); eventSource.emit(event_types.CHATCOMPLETION_SOURCE_CHANGED, oai_settings.chat_completion_source); }); From beb5e470a2e934d373b0d59988f3fb1d9632f859 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 22 Feb 2024 04:48:46 +0200 Subject: [PATCH 29/29] #1069 Fix hoisting of pristine cards in newest sort --- src/endpoints/characters.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index 2c8b36e98..3cad53ffd 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -139,13 +139,13 @@ const processCharacter = async (item, i) => { const img_data = await charaRead(DIRECTORIES.characters + item); if (img_data === undefined) throw new Error('Failed to read character file'); - let jsonObject = getCharaCardV2(JSON.parse(img_data)); + let jsonObject = getCharaCardV2(JSON.parse(img_data), false); jsonObject.avatar = item; characters[i] = jsonObject; characters[i]['json_data'] = img_data; const charStat = fs.statSync(path.join(DIRECTORIES.characters, item)); - characters[i]['date_added'] = charStat.birthtimeMs; - characters[i]['create_date'] = jsonObject['create_date'] || humanizedISO8601DateTime(charStat.birthtimeMs); + characters[i]['date_added'] = charStat.ctimeMs; + characters[i]['create_date'] = jsonObject['create_date'] || humanizedISO8601DateTime(charStat.ctimeMs); const char_dir = path.join(DIRECTORIES.chats, item.replace('.png', '')); const { chatSize, dateLastChat } = calculateChatSize(char_dir); @@ -170,15 +170,30 @@ const processCharacter = async (item, i) => { } }; -function getCharaCardV2(jsonObject) { +/** + * Convert a character object to Spec V2 format. + * @param {object} jsonObject Character object + * @param {boolean} hoistDate Will set the chat and create_date fields to the current date if they are missing + * @returns {object} Character object in Spec V2 format + */ +function getCharaCardV2(jsonObject, hoistDate = true) { if (jsonObject.spec === undefined) { jsonObject = convertToV2(jsonObject); + + if (hoistDate && !jsonObject.create_date) { + jsonObject.create_date = humanizedISO8601DateTime(); + } } else { jsonObject = readFromV2(jsonObject); } return jsonObject; } +/** + * Convert a character object to Spec V2 format. + * @param {object} char Character object + * @returns {object} Character object in Spec V2 format + */ function convertToV2(char) { // Simulate incoming data from frontend form const result = charaFormatData({ @@ -199,7 +214,8 @@ function convertToV2(char) { }); result.chat = char.chat ?? humanizedISO8601DateTime(); - result.create_date = char.create_date ?? humanizedISO8601DateTime(); + result.create_date = char.create_date; + return result; }