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