Text Completion: Add context-specific {{timestamp}} macro

This commit is contained in:
Cohee
2025-02-18 22:12:32 +02:00
parent 1a4bcbb794
commit 44182970bc
2 changed files with 71 additions and 38 deletions

View File

@@ -3429,6 +3429,15 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0; const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;
const isInstruct = power_user.instruct.enabled && api !== 'openai' && api !== 'novel' && !instructOverride; const isInstruct = power_user.instruct.enabled && api !== 'openai' && api !== 'novel' && !instructOverride;
const isQuiet = true; const isQuiet = true;
const getInstructFormatParams = (/** @type {string} */ mes) => ({
name: name1,
mes: mes,
isUser: false,
isNarrator: true,
forceAvatar: '',
forceOutputSequence: false,
timestamp: moment(),
});
let eventHook = () => { }; let eventHook = () => { };
if (systemPrompt) { if (systemPrompt) {
@@ -3439,8 +3448,8 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
prompt = substituteParams(prompt); prompt = substituteParams(prompt);
prompt = api == 'novel' ? adjustNovelInstructionPrompt(prompt) : prompt; prompt = api == 'novel' ? adjustNovelInstructionPrompt(prompt) : prompt;
prompt = isInstruct ? formatInstructModeChat(name1, prompt, false, true, '', name1, name2, false) : prompt; prompt = isInstruct ? formatInstructModeChat(getInstructFormatParams(prompt)) : prompt;
prompt = isInstruct ? (prompt + formatInstructModePrompt(name2, false, '', name1, name2, isQuiet, quietToLoud)) : (prompt + '\n'); prompt = isInstruct ? (prompt + formatInstructModePrompt(name2, false, '', isQuiet, quietToLoud)) : (prompt + '\n');
try { try {
if (responseLengthCustomized) { if (responseLengthCustomized) {
@@ -4016,7 +4025,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
const mesExamplesRawArray = [...mesExamplesArray]; const mesExamplesRawArray = [...mesExamplesArray];
if (mesExamplesArray && isInstruct) { if (mesExamplesArray && isInstruct) {
mesExamplesArray = formatInstructModeExamples(mesExamplesArray, name1, name2); mesExamplesArray = formatInstructModeExamples(mesExamplesArray);
} }
if (skipWIAN !== true) { if (skipWIAN !== true) {
@@ -4336,7 +4345,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
// here name1 is forced for all quiet prompts..why? // here name1 is forced for all quiet prompts..why?
const name = name1; const name = name1;
//checks if we are in instruct, if so, formats the chat as such, otherwise just adds the quiet prompt //checks if we are in instruct, if so, formats the chat as such, otherwise just adds the quiet prompt
const quietAppend = isInstruct ? formatInstructModeChat(name, quiet_prompt, false, true, '', name1, name2, false) : `\n${quiet_prompt}`; const quietInstructParams = {
name: name,
mes: quiet_prompt,
isUser: false,
isNarrator: true,
forceAvatar: '',
forceOutputSequence: false,
timestamp: moment(),
};
const quietAppend = isInstruct ? formatInstructModeChat(quietInstructParams) : `\n${quiet_prompt}`;
//This begins to fix quietPrompts (particularly /sysgen) for instruct //This begins to fix quietPrompts (particularly /sysgen) for instruct
//previously instruct input sequence was being appended to the last chat message w/o '\n' //previously instruct input sequence was being appended to the last chat message w/o '\n'
@@ -4366,7 +4384,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
if (isInstruct && !isContinue) { if (isInstruct && !isContinue) {
const name = (quiet_prompt && !quietToLoud && !isImpersonate) ? (quietName ?? 'System') : (isImpersonate ? name1 : name2); const name = (quiet_prompt && !quietToLoud && !isImpersonate) ? (quietName ?? 'System') : (isImpersonate ? name1 : name2);
const isQuiet = quiet_prompt && type == 'quiet'; const isQuiet = quiet_prompt && type == 'quiet';
lastMesString += formatInstructModePrompt(name, isImpersonate, promptBias, name1, name2, isQuiet, quietToLoud); lastMesString += formatInstructModePrompt(name, isImpersonate, promptBias, isQuiet, quietToLoud);
} }
// Get non-instruct impersonation line // Get non-instruct impersonation line
@@ -5147,7 +5165,16 @@ function formatMessageHistoryItem(chatItem, isInstruct, forceOutputSequence) {
let textResult = chatItem?.name && shouldPrependName ? `${itemName}: ${chatItem.mes}\n` : `${chatItem.mes}\n`; let textResult = chatItem?.name && shouldPrependName ? `${itemName}: ${chatItem.mes}\n` : `${chatItem.mes}\n`;
if (isInstruct) { if (isInstruct) {
textResult = formatInstructModeChat(itemName, chatItem.mes, chatItem.is_user, isNarratorType, chatItem.force_avatar, name1, name2, forceOutputSequence); const instructFormatParams = {
name: itemName,
mes: chatItem.mes,
isUser: chatItem.is_user,
isNarrator: isNarratorType,
forceAvatar: chatItem.force_avatar,
forceOutputSequence: forceOutputSequence,
timestamp: timestampToMoment(chatItem.send_date),
};
textResult = formatInstructModeChat(instructFormatParams);
} }
return textResult; return textResult;

View File

@@ -8,6 +8,7 @@ import {
context_presets, context_presets,
} from './power-user.js'; } from './power-user.js';
import { regexFromString, resetScrollHeight } from './utils.js'; import { regexFromString, resetScrollHeight } from './utils.js';
import { moment } from '../lib.js';
/** /**
* @type {any[]} Instruct mode presets. * @type {any[]} Instruct mode presets.
@@ -254,8 +255,10 @@ export function getInstructStoppingSequences() {
// Cohee: oobabooga's textgen always appends newline before the sequence as a stopping string // Cohee: oobabooga's textgen always appends newline before the sequence as a stopping string
// But it's a problem for Metharme which doesn't use newlines to separate them. // But it's a problem for Metharme which doesn't use newlines to separate them.
const wrap = (s) => power_user.instruct.wrap ? '\n' + s : s; const wrap = (s) => power_user.instruct.wrap ? '\n' + s : s;
// Should not contain timestamp macros
const hasTimestamp = /{{timestamp}}/gi.test(sequence) || /{{timestamp::(.*?)}}/gi.test(sequence);
// Sequence must be a non-empty string // Sequence must be a non-empty string
if (typeof sequence === 'string' && sequence.length > 0) { if (typeof sequence === 'string' && sequence.length > 0 && !hasTimestamp) {
// If sequence is just a whitespace or newline - we don't want to make it a stopping string // If sequence is just a whitespace or newline - we don't want to make it a stopping string
// User can always add it as a custom stop string if really needed // User can always add it as a custom stop string if really needed
if (sequence.trim().length > 0) { if (sequence.trim().length > 0) {
@@ -309,19 +312,35 @@ export const force_output_sequence = {
LAST: 2, LAST: 2,
}; };
/**
* Replaces instruct mode macros in the given sequence string.
* @param {string} sequence Sequence string.
* @param {string} name Item name.
* @param {import('moment').Moment} timestamp Message timestamp.
* @returns {string} Sequence string with macros replaced.
*/
function replaceSequenceMacros(sequence, name, timestamp) {
sequence = substituteParams(sequence);
sequence = sequence.replace(/{{name}}/gi, name || 'System');
sequence = sequence.replace(/{{timestamp}}/gi, () => timestamp.format('YYYY-MM-DD HH:mm:ss'));
sequence = sequence.replace(/{{timestamp::(.*?)}}/gi, (_, format) => timestamp.format(format));
return sequence;
}
/** /**
* Formats instruct mode chat message. * Formats instruct mode chat message.
* @param {string} name Character name. * @param {InstructFormatParams} params Message parameters.
* @param {string} mes Message text. * @typedef {object} InstructFormatParams Instruct mode chat message parameters.
* @param {boolean} isUser Is the message from the user. * @property {string} name Character name.
* @param {boolean} isNarrator Is the message from the narrator. * @property {string} mes Message text.
* @param {string} forceAvatar Force avatar string. * @property {boolean} isUser Is the message from the user.
* @param {string} name1 User name. * @property {boolean} isNarrator Is the message from the narrator.
* @param {string} name2 Character name. * @property {string} forceAvatar Force avatar string.
* @param {boolean|number} forceOutputSequence Force to use first/last output sequence (if configured). * @property {boolean|number} forceOutputSequence Force to use first/last output sequence (if configured).
* @property {import('moment').Moment} timestamp Message timestamp.
* @returns {string} Formatted instruct mode chat message. * @returns {string} Formatted instruct mode chat message.
*/ */
export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2, forceOutputSequence) { export function formatInstructModeChat({ name, mes, isUser, isNarrator, forceAvatar, forceOutputSequence, timestamp }) {
let includeNames = isNarrator ? false : power_user.instruct.names_behavior === names_behavior_types.ALWAYS; let includeNames = isNarrator ? false : power_user.instruct.names_behavior === names_behavior_types.ALWAYS;
if (!isNarrator && power_user.instruct.names_behavior === names_behavior_types.FORCE && ((selected_group && name !== name1) || (forceAvatar && name !== name1))) { if (!isNarrator && power_user.instruct.names_behavior === names_behavior_types.FORCE && ((selected_group && name !== name1) || (forceAvatar && name !== name1))) {
@@ -372,11 +391,8 @@ export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvata
let suffix = getSuffix() || ''; let suffix = getSuffix() || '';
if (power_user.instruct.macro) { if (power_user.instruct.macro) {
prefix = substituteParams(prefix, name1, name2); prefix = replaceSequenceMacros(prefix, (name || 'System'), timestamp);
prefix = prefix.replace(/{{name}}/gi, name || 'System'); suffix = replaceSequenceMacros(suffix, (name || 'System'), timestamp);
suffix = substituteParams(suffix, name1, name2);
suffix = suffix.replace(/{{name}}/gi, name || 'System');
} }
if (!suffix && power_user.instruct.wrap) { if (!suffix && power_user.instruct.wrap) {
@@ -420,11 +436,9 @@ export function formatInstructModeSystemPrompt(systemPrompt) {
/** /**
* Formats example messages according to instruct mode settings. * Formats example messages according to instruct mode settings.
* @param {string[]} mesExamplesArray Example messages array. * @param {string[]} mesExamplesArray Example messages array.
* @param {string} name1 User name.
* @param {string} name2 Character name.
* @returns {string[]} Formatted example messages string. * @returns {string[]} Formatted example messages string.
*/ */
export function formatInstructModeExamples(mesExamplesArray, name1, name2) { export function formatInstructModeExamples(mesExamplesArray) {
const blockHeading = power_user.context.example_separator ? `${substituteParams(power_user.context.example_separator)}\n` : ''; const blockHeading = power_user.context.example_separator ? `${substituteParams(power_user.context.example_separator)}\n` : '';
if (power_user.instruct.skip_examples) { if (power_user.instruct.skip_examples) {
@@ -440,15 +454,10 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
let outputSuffix = power_user.instruct.output_suffix || ''; let outputSuffix = power_user.instruct.output_suffix || '';
if (power_user.instruct.macro) { if (power_user.instruct.macro) {
inputPrefix = substituteParams(inputPrefix, name1, name2); inputPrefix = replaceSequenceMacros(inputPrefix, name1, moment());
outputPrefix = substituteParams(outputPrefix, name1, name2); outputPrefix = replaceSequenceMacros(outputPrefix, name2, moment());
inputSuffix = substituteParams(inputSuffix, name1, name2); inputSuffix = replaceSequenceMacros(inputSuffix, name1, moment());
outputSuffix = substituteParams(outputSuffix, name1, name2); outputSuffix = replaceSequenceMacros(outputSuffix, name2, moment());
inputPrefix = inputPrefix.replace(/{{name}}/gi, name1);
outputPrefix = outputPrefix.replace(/{{name}}/gi, name2);
inputSuffix = inputSuffix.replace(/{{name}}/gi, name1);
outputSuffix = outputSuffix.replace(/{{name}}/gi, name2);
if (!inputSuffix && power_user.instruct.wrap) { if (!inputSuffix && power_user.instruct.wrap) {
inputSuffix = '\n'; inputSuffix = '\n';
@@ -499,13 +508,11 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
* @param {string} name Character name. * @param {string} name Character name.
* @param {boolean} isImpersonate Is generation in impersonation mode. * @param {boolean} isImpersonate Is generation in impersonation mode.
* @param {string} promptBias Prompt bias string. * @param {string} promptBias Prompt bias string.
* @param {string} name1 User name.
* @param {string} name2 Character name.
* @param {boolean} isQuiet Is quiet mode generation. * @param {boolean} isQuiet Is quiet mode generation.
* @param {boolean} isQuietToLoud Is quiet to loud generation. * @param {boolean} isQuietToLoud Is quiet to loud generation.
* @returns {string} Formatted instruct mode last prompt line. * @returns {string} Formatted instruct mode last prompt line.
*/ */
export function formatInstructModePrompt(name, isImpersonate, promptBias, name1, name2, isQuiet, isQuietToLoud) { export function formatInstructModePrompt(name, isImpersonate, promptBias, isQuiet, isQuietToLoud) {
const includeNames = name && (power_user.instruct.names_behavior === names_behavior_types.ALWAYS || (!!selected_group && power_user.instruct.names_behavior === names_behavior_types.FORCE)) && !(isQuiet && !isQuietToLoud); const includeNames = name && (power_user.instruct.names_behavior === names_behavior_types.ALWAYS || (!!selected_group && power_user.instruct.names_behavior === names_behavior_types.FORCE)) && !(isQuiet && !isQuietToLoud);
function getSequence() { function getSequence() {
@@ -545,8 +552,7 @@ export function formatInstructModePrompt(name, isImpersonate, promptBias, name1,
} }
if (power_user.instruct.macro) { if (power_user.instruct.macro) {
sequence = substituteParams(sequence, name1, name2); sequence = replaceSequenceMacros(sequence, (name || 'System'), moment());
sequence = sequence.replace(/{{name}}/gi, name || 'System');
} }
const separator = power_user.instruct.wrap ? '\n' : ''; const separator = power_user.instruct.wrap ? '\n' : '';