feat: Add core suppression logic and integrate into prompt injector
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
lastActionWasSwipe,
|
||||
setLastActionWasSwipe
|
||||
} from '../../core/state.js';
|
||||
import { evaluateSuppression } from './suppression.js';
|
||||
import { parseUserStats } from './parser.js';
|
||||
import {
|
||||
generateTrackerExample,
|
||||
@@ -44,7 +45,28 @@ export function onGenerationStarted(type, data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chat = getContext().chat;
|
||||
const context = getContext();
|
||||
const chat = context.chat;
|
||||
// Detect if a guided generation is active (GuidedGenerations and similar extensions
|
||||
// inject an ephemeral 'instruct' injection into chatMetadata.script_injects).
|
||||
// If present, we should avoid injecting RPG tracker instructions that ask
|
||||
// the model to include stats/etc. This prevents conflicts when guided prompts
|
||||
// are used (e.g., GuidedGenerations Extension).
|
||||
// Evaluate suppression using the shared helper
|
||||
const suppression = evaluateSuppression(extensionSettings, context, data);
|
||||
const { shouldSuppress, skipMode, isGuidedGeneration, isImpersonationGeneration, hasQuietPrompt, instructContent, quietPromptRaw, matchedPattern } = suppression;
|
||||
|
||||
if (shouldSuppress) {
|
||||
// Debugging: indicate active suppression and which source triggered it
|
||||
console.debug(`[RPG Companion] Suppression active (mode=${skipMode}). isGuided=${isGuidedGeneration}, isImpersonation=${isImpersonationGeneration}, hasQuietPrompt=${hasQuietPrompt} - skipping RPG tracker injections for this generation.`);
|
||||
|
||||
// Also clear any existing RPG Companion prompts so they do not leak into this generation
|
||||
// (e.g., previously set extension prompts should not be used alongside a guided prompt)
|
||||
setExtensionPrompt('rpg-companion-inject', '', extension_prompt_types.IN_CHAT, 0, false);
|
||||
setExtensionPrompt('rpg-companion-example', '', extension_prompt_types.IN_CHAT, 0, false);
|
||||
setExtensionPrompt('rpg-companion-html', '', extension_prompt_types.IN_CHAT, 0, false);
|
||||
setExtensionPrompt('rpg-companion-context', '', extension_prompt_types.IN_CHAT, 1, false);
|
||||
}
|
||||
const lastMessage = chat && chat.length > 0 ? chat[chat.length - 1] : null;
|
||||
|
||||
// For SEPARATE mode only: Check if we need to commit extension data
|
||||
@@ -145,7 +167,7 @@ export function onGenerationStarted(type, data) {
|
||||
}
|
||||
|
||||
// If we have previous tracker data and found an assistant message, inject it as an assistant message
|
||||
if (example && lastAssistantDepth > 0) {
|
||||
if (!shouldSuppress && example && lastAssistantDepth > 0) {
|
||||
setExtensionPrompt('rpg-companion-example', example, extension_prompt_types.IN_CHAT, lastAssistantDepth, false, extension_prompt_roles.ASSISTANT);
|
||||
// console.log('[RPG Companion] Injected tracker example as assistant message at depth:', lastAssistantDepth);
|
||||
} else {
|
||||
@@ -153,11 +175,15 @@ export function onGenerationStarted(type, data) {
|
||||
}
|
||||
|
||||
// Inject the instructions as a user message at depth 0 (right before generation)
|
||||
setExtensionPrompt('rpg-companion-inject', instructions, extension_prompt_types.IN_CHAT, 0, false, extension_prompt_roles.USER);
|
||||
// If this is a guided generation (user explicitly injected 'instruct'), skip adding
|
||||
// our tracker instructions to avoid clobbering the guided prompt.
|
||||
if (!shouldSuppress) {
|
||||
setExtensionPrompt('rpg-companion-inject', instructions, extension_prompt_types.IN_CHAT, 0, false, extension_prompt_roles.USER);
|
||||
}
|
||||
// console.log('[RPG Companion] Injected RPG tracking instructions at depth 0 (right before generation)');
|
||||
|
||||
// Inject HTML prompt separately at depth 0 if enabled (prevents duplication on swipes)
|
||||
if (extensionSettings.enableHtmlPrompt) {
|
||||
if (extensionSettings.enableHtmlPrompt && !shouldSuppress) {
|
||||
const htmlPrompt = `\nIf appropriate, include inline HTML, CSS, and JS elements for creative, visual storytelling throughout your response:
|
||||
- Use them liberally to depict any in-world content that can be visualized (screens, posters, books, signs, letters, logos, crests, seals, medallions, labels, etc.), with creative license for animations, 3D effects, pop-ups, dropdowns, websites, and so on.
|
||||
- Style them thematically to match the theme (e.g., sleek for sci-fi, rustic for fantasy), ensuring text is visible.
|
||||
@@ -186,7 +212,10 @@ Ensure these details naturally reflect and influence the narrative. Character be
|
||||
`;
|
||||
|
||||
// Inject context at depth 1 (before last user message) as SYSTEM
|
||||
setExtensionPrompt('rpg-companion-context', wrappedContext, extension_prompt_types.IN_CHAT, 1, false);
|
||||
// Skip when a guided generation injection is present to avoid conflicting instructions
|
||||
if (!shouldSuppress) {
|
||||
setExtensionPrompt('rpg-companion-context', wrappedContext, extension_prompt_types.IN_CHAT, 1, false);
|
||||
}
|
||||
// console.log('[RPG Companion] Injected contextual summary for separate mode:', contextSummary);
|
||||
} else {
|
||||
// Clear if no data yet
|
||||
@@ -194,7 +223,7 @@ Ensure these details naturally reflect and influence the narrative. Character be
|
||||
}
|
||||
|
||||
// Inject HTML prompt separately at depth 0 if enabled (same as together mode pattern)
|
||||
if (extensionSettings.enableHtmlPrompt) {
|
||||
if (extensionSettings.enableHtmlPrompt && !shouldSuppress) {
|
||||
const htmlPrompt = `\nIf appropriate, include inline HTML, CSS, and JS elements for creative, visual storytelling throughout your response:
|
||||
- Use them liberally to depict any in-world content that can be visualized (screens, posters, books, signs, letters, logos, crests, seals, medallions, labels, etc.), with creative license for animations, 3D effects, pop-ups, dropdowns, websites, and so on.
|
||||
- Style them thematically to match the theme (e.g., sleek for sci-fi, rustic for fantasy), ensuring text is visible.
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Suppression helper for guided generation injection behavior.
|
||||
*
|
||||
* This module exports a pure function `evaluateSuppression` that computes
|
||||
* whether RPG Companion should suppress tracker and HTML injections for a
|
||||
* given generation request, based on runtime settings, extended context, and
|
||||
* generation data (quiet prompt flags, etc.).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine if suppression should be applied for this generation.
|
||||
*
|
||||
* @param {any} extensionSettings - extension settings object (may contain skipInjectionsForGuided)
|
||||
* @param {any} context - SillyTavern context object (used to find chatMetadata.script_injects.instruct)
|
||||
* @param {any} data - Generation data (contains quiet_prompt/quietPrompt flags)
|
||||
* @returns {Object} - An object describing the suppression decision.
|
||||
*/
|
||||
export function evaluateSuppression(extensionSettings, context, data) {
|
||||
// Detect presence of any injected `instruct` script
|
||||
const instructObj = context?.chatMetadata?.script_injects?.instruct;
|
||||
const isGuidedGeneration = !!instructObj;
|
||||
const quietPromptRaw = data?.quiet_prompt || data?.quietPrompt || '';
|
||||
const hasQuietPrompt = !!quietPromptRaw;
|
||||
|
||||
// Normalize the injected instruction body (it may be an object with a 'value' field or a raw string)
|
||||
let instructContent = '';
|
||||
if (instructObj) {
|
||||
if (typeof instructObj === 'object') {
|
||||
instructContent = String(instructObj.value || instructObj || '');
|
||||
} else {
|
||||
instructContent = String(instructObj);
|
||||
}
|
||||
}
|
||||
|
||||
const IMPERSONATION_PATTERNS = [
|
||||
{ id: 'first-perspective', re: /write in first person perspective from/i },
|
||||
{ id: 'second-perspective', re: /write in second person perspective from/i },
|
||||
{ id: 'third-perspective', re: /write in third person perspective from/i },
|
||||
{ id: 'you-yours', re: /using you\/yours for/i },
|
||||
{ id: 'third-person-pronouns', re: /third-person pronouns for/i },
|
||||
{ id: 'impersonate-word', re: /\bimpersonat(e|ion)?\b/i },
|
||||
{ id: 'assume-role', re: /assume the role of/i },
|
||||
{ id: 'play-role', re: /play the role of/i },
|
||||
{ id: 'impersonate-command', re: /\/impersonate await=true/i },
|
||||
{ id: 'generic-first', re: /\bfirst person\b/i },
|
||||
{ id: 'generic-second', re: /\bsecond person\b/i },
|
||||
{ id: 'generic-third', re: /\bthird person\b/i }
|
||||
];
|
||||
|
||||
// Include quietPrompt raw text in detection; guided impersonation flows may pass it directly here
|
||||
const combinedTextForDetection = [instructContent, quietPromptRaw].filter(Boolean).join('\n');
|
||||
|
||||
let matchedPattern = '';
|
||||
let isImpersonationGeneration = false;
|
||||
if (combinedTextForDetection.length) {
|
||||
for (const pat of IMPERSONATION_PATTERNS) {
|
||||
if (pat.re.test(combinedTextForDetection)) {
|
||||
matchedPattern = pat.id;
|
||||
isImpersonationGeneration = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const skipMode = (extensionSettings && extensionSettings.skipInjectionsForGuided) || 'none';
|
||||
|
||||
// Compute suppression according to mode
|
||||
const shouldSuppress = skipMode === 'guided'
|
||||
? (isGuidedGeneration || hasQuietPrompt)
|
||||
: (skipMode === 'impersonation' ? isImpersonationGeneration : false);
|
||||
|
||||
return {
|
||||
shouldSuppress,
|
||||
skipMode,
|
||||
isGuidedGeneration,
|
||||
isImpersonationGeneration,
|
||||
hasQuietPrompt,
|
||||
instructContent,
|
||||
quietPromptRaw,
|
||||
matchedPattern
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user