Added stop string cleanup, better stopping string param

c5f251c6e376701a273876affaa7a87910420453

bmen25124 <bmen25124@gmail.com>

2 files changed, +81 -19Showing whitespace changes
public/scripts/custom-request.js+65 -7
@@ -2,7 +2,7 @@ import { getPresetManager } from './preset-manager.js';
2import { extractMessageFromData, getGenerateUrl, getRequestHeaders } from '../script.js';2import { extractMessageFromData, getGenerateUrl, getRequestHeaders } from '../script.js';
3import { getTextGenServer } from './textgen-settings.js';3import { getTextGenServer } from './textgen-settings.js';
4import { extractReasoningFromData } from './reasoning.js';4import { extractReasoningFromData } from './reasoning.js';
5import { formatInstructModeChat, formatInstructModePrompt, names_behavior_types } from './instruct-mode.js';5import { formatInstructModeChat, formatInstructModePrompt, getInstructStoppingSequences, names_behavior_types } from './instruct-mode.js';
6import { getStreamingReply, tryParseStreamingError } from './openai.js';6import { getStreamingReply, tryParseStreamingError } from './openai.js';
7import EventSourceStream from './sse-stream.js';7import EventSourceStream from './sse-stream.js';
88
@@ -222,10 +222,13 @@ export class TextCompletionService {
222 }222 }
223 }223 }
224224
225
226 /** @type {InstructSettings | undefined} */
227 let instructPreset;
225 // Handle instruct formatting if requested228 // Handle instruct formatting if requested
226 if (Array.isArray(prompt) && instructName) {229 if (Array.isArray(prompt) && instructName) {
227 const instructPresetManager = getPresetManager('instruct');230 const instructPresetManager = getPresetManager('instruct');
228 let instructPreset = instructPresetManager?.getCompletionPresetByName(instructName);231 instructPreset = instructPresetManager?.getCompletionPresetByName(instructName);
229 if (instructPreset) {232 if (instructPreset) {
230 // Clone the preset to avoid modifying the original233 // Clone the preset to avoid modifying the original
231 instructPreset = structuredClone(instructPreset);234 instructPreset = structuredClone(instructPreset);
@@ -266,10 +269,9 @@ export class TextCompletionService {
266 formattedMessages.push(messageContent);269 formattedMessages.push(messageContent);
267 }270 }
268 requestData.prompt = formattedMessages.join('');271 requestData.prompt = formattedMessages.join('');
269 if (instructPreset.output_suffix) {272 const stoppingStrings = getInstructStoppingSequences({ customInstruct: instructPreset, useStopString: false });
270 requestData.stop = [instructPreset.output_suffix];273 requestData.stop = stoppingStrings
271 requestData.stopping_strings = [instructPreset.output_suffix];274 requestData.stopping_strings = stoppingStrings;
272 }
273 } else {275 } else {
274 console.warn(`Instruct preset "${instructName}" not found, using basic formatting`);276 console.warn(`Instruct preset "${instructName}" not found, using basic formatting`);
275 requestData.prompt = prompt.map(x => x.content).join('\n\n');277 requestData.prompt = prompt.map(x => x.content).join('\n\n');
@@ -283,7 +285,63 @@ export class TextCompletionService {
283 // @ts-ignore285 // @ts-ignore
284 const data = this.createRequestData(requestData);286 const data = this.createRequestData(requestData);
285287
286 return await this.sendRequest(data, extractData, signal);288 const response = await this.sendRequest(data, extractData, signal);
289 // Remove stopping strings from the end
290 if (!data.stream && extractData) {
291 /** @type {ExtractedData} */
292 // @ts-ignore
293 const extractedData = response;
294
295 let message = extractedData.content;
296
297 message = message.replace(/[^\S\r\n]+$/gm, '');
298
299 if (requestData.stopping_strings) {
300 for (const stoppingString of requestData.stopping_strings) {
301 if (stoppingString.length) {
302 for (let j = stoppingString.length; j > 0; j--) {
303 if (message.slice(-j) === stoppingString.slice(0, j)) {
304 message = message.slice(0, -j);
305 break;
306 }
307 }
308 }
309 }
310 }
311
312 if (instructPreset) {
313 if (instructPreset.stop_sequence) {
314 const index = message.indexOf(instructPreset.stop_sequence);
315 if (index != -1) {
316 message = message.substring(0, index);
317 }
318 }
319 if (instructPreset.input_sequence && instructPreset.input_sequence.trim()) {
320 const index = message.indexOf(instructPreset.input_sequence);
321 if (index != -1) {
322 message = message.substring(0, index);
323 }
324 }
325 if (instructPreset.output_sequence) {
326 instructPreset.output_sequence.split('\n')
327 .filter(line => line.trim() !== '')
328 .forEach(line => {
329 message = message.replaceAll(line, '');
330 });
331 }
332 if (instructPreset.last_output_sequence) {
333 instructPreset.last_output_sequence.split('\n')
334 .filter(line => line.trim() !== '')
335 .forEach(line => {
336 message = message.replaceAll(line, '');
337 });
338 }
339 }
340
341 extractedData.content = message;
342 }
343
344 return response;
287 }345 }
288346
289 /**347 /**
public/scripts/instruct-mode.js+16 -12
@@ -243,9 +243,12 @@ export function autoSelectInstructPreset(modelId) {
243243
244/**244/**
245 * Converts instruct mode sequences to an array of stopping strings.245 * Converts instruct mode sequences to an array of stopping strings.
246 * @param {{customInstruct?: InstructSettings, useStopString?: boolean}} options
246 * @returns {string[]} Array of instruct mode stopping strings.247 * @returns {string[]} Array of instruct mode stopping strings.
247 */248 */
248export function getInstructStoppingSequences() {249export function getInstructStoppingSequences({ customInstruct = null, useStopString = false } = {}) {
250 const instruct = structuredClone(customInstruct ?? power_user.instruct);
251
249 /**252 /**
250 * Adds instruct mode sequence to the result array.253 * Adds instruct mode sequence to the result array.
251 * @param {string} sequence Sequence string.254 * @param {string} sequence Sequence string.
@@ -254,7 +257,7 @@ export function getInstructStoppingSequences() {
254 function addInstructSequence(sequence) {257 function addInstructSequence(sequence) {
255 // Cohee: oobabooga's textgen always appends newline before the sequence as a stopping string258 // Cohee: oobabooga's textgen always appends newline before the sequence as a stopping string
256 // But it's a problem for Metharme which doesn't use newlines to separate them.259 // But it's a problem for Metharme which doesn't use newlines to separate them.
257 const wrap = (s) => power_user.instruct.wrap ? '\n' + s : s;260 const wrap = (s) => instruct.wrap ? '\n' + s : s;
258 // Sequence must be a non-empty string261 // Sequence must be a non-empty string
259 if (typeof sequence === 'string' && sequence.length > 0) {262 if (typeof sequence === 'string' && sequence.length > 0) {
260 // If sequence is just a whitespace or newline - we don't want to make it a stopping string263 // If sequence is just a whitespace or newline - we don't want to make it a stopping string
@@ -262,7 +265,7 @@ export function getInstructStoppingSequences() {
262 if (sequence.trim().length > 0) {265 if (sequence.trim().length > 0) {
263 const wrappedSequence = wrap(sequence);266 const wrappedSequence = wrap(sequence);
264 // Need to respect "insert macro" setting267 // Need to respect "insert macro" setting
265 const stopString = power_user.instruct.macro ? substituteParams(wrappedSequence) : wrappedSequence;268 const stopString = instruct.macro ? substituteParams(wrappedSequence) : wrappedSequence;
266 result.push(stopString);269 result.push(stopString);
267 }270 }
268 }271 }
@@ -270,14 +273,15 @@ export function getInstructStoppingSequences() {
270273
271 const result = [];274 const result = [];
272275
273 if (power_user.instruct.enabled) {276 // Since preset's don't have "enabled", we assume it's always enabled
274 const stop_sequence = power_user.instruct.stop_sequence || '';277 if (customInstruct ?? instruct.enabled) {
275 const input_sequence = power_user.instruct.input_sequence?.replace(/{{name}}/gi, name1) || '';278 const stop_sequence = instruct.stop_sequence || '';
276 const output_sequence = power_user.instruct.output_sequence?.replace(/{{name}}/gi, name2) || '';279 const input_sequence = instruct.input_sequence?.replace(/{{name}}/gi, name1) || '';
277 const first_output_sequence = power_user.instruct.first_output_sequence?.replace(/{{name}}/gi, name2) || '';280 const output_sequence = instruct.output_sequence?.replace(/{{name}}/gi, name2) || '';
278 const last_output_sequence = power_user.instruct.last_output_sequence?.replace(/{{name}}/gi, name2) || '';281 const first_output_sequence = instruct.first_output_sequence?.replace(/{{name}}/gi, name2) || '';
279 const system_sequence = power_user.instruct.system_sequence?.replace(/{{name}}/gi, 'System') || '';282 const last_output_sequence = instruct.last_output_sequence?.replace(/{{name}}/gi, name2) || '';
280 const last_system_sequence = power_user.instruct.last_system_sequence?.replace(/{{name}}/gi, 'System') || '';283 const system_sequence = instruct.system_sequence?.replace(/{{name}}/gi, 'System') || '';
284 const last_system_sequence = instruct.last_system_sequence?.replace(/{{name}}/gi, 'System') || '';
281285
282 const combined_sequence = [286 const combined_sequence = [
283 stop_sequence,287 stop_sequence,
@@ -292,7 +296,7 @@ export function getInstructStoppingSequences() {
292 combined_sequence.split('\n').filter((line, index, self) => self.indexOf(line) === index).forEach(addInstructSequence);296 combined_sequence.split('\n').filter((line, index, self) => self.indexOf(line) === index).forEach(addInstructSequence);
293 }297 }
294298
295 if (power_user.context.use_stop_strings) {299 if (useStopString ?? power_user.context.use_stop_strings) {
296 if (power_user.context.chat_start) {300 if (power_user.context.chat_start) {
297 result.push(`\n${substituteParams(power_user.context.chat_start)}`);301 result.push(`\n${substituteParams(power_user.context.chat_start)}`);
298 }302 }