mirror of
https://github.com/Darkatse/TauriTavern.git
synced 2026-10-19 15:45:12 +00:00
feat: 支持将提示词附着到已有聊天消息
This commit is contained in:
parent
8d76910137
commit
8588005309
5 changed files with 172 additions and 8 deletions
|
|
@ -7188,8 +7188,9 @@
|
|||
<select id="completion_prompt_manager_popup_entry_form_injection_position" class="text_pole" name="injection_position">
|
||||
<option data-i18n="prompt_manager_relative" value="0">Relative</option>
|
||||
<option data-i18n="prompt_manager_in_chat" value="1">In-chat</option>
|
||||
<option data-i18n="prompt_manager_attach_existing" value="2">Attach to existing message</option>
|
||||
</select>
|
||||
<div class="text_muted" data-i18n="Relative (to other prompts in prompt manager) or In-chat @ Depth.">Relative (to other prompts in prompt manager) or In-chat @ Depth.</div>
|
||||
<div class="text_muted" data-i18n="Relative (to other prompts in prompt manager), In-chat @ Depth, or attached to an existing message.">Relative (to other prompts in prompt manager), In-chat @ Depth, or attached to an existing message.</div>
|
||||
</div>
|
||||
<div id="completion_prompt_manager_depth_block" class="completion_prompt_manager_popup_entry_form_control flex1">
|
||||
<label for="completion_prompt_manager_popup_entry_form_injection_depth">
|
||||
|
|
@ -7207,6 +7208,36 @@
|
|||
<div class="text_muted" data-i18n="Ordered from low/top to high/bottom, and at same order: Assistant, User, System.">Ordered from low/top to high/bottom, and at same order: Assistant, User, System.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="completion_prompt_manager_attach_block" class="flex-container gap10px" style="display: none; margin-top: 10px;">
|
||||
<div class="completion_prompt_manager_popup_entry_form_control flex1">
|
||||
<label for="completion_prompt_manager_popup_entry_form_attach_role">
|
||||
<span data-i18n="prompt_manager_attach_role">Attach to Role</span>
|
||||
</label>
|
||||
<select id="completion_prompt_manager_popup_entry_form_attach_role" class="text_pole" name="attach_role">
|
||||
<option value="user">User</option>
|
||||
<option value="assistant">Assistant</option>
|
||||
<option value="system">System</option>
|
||||
</select>
|
||||
<div class="text_muted" data-i18n="Select the role of the message you want to target.">Select the role of the message you want to target.</div>
|
||||
</div>
|
||||
<div class="completion_prompt_manager_popup_entry_form_control flex1">
|
||||
<label for="completion_prompt_manager_popup_entry_form_attach_index">
|
||||
<span data-i18n="prompt_manager_attach_index">Message Index</span>
|
||||
</label>
|
||||
<input id="completion_prompt_manager_popup_entry_form_attach_index" class="text_pole" type="number" name="attach_index" value="1" />
|
||||
<div class="text_muted" data-i18n="Positive: count from start (1 = first). Negative: count from end (-1 = last). 0 defaults to 1. Only counts messages of selected role.">Positive: count from start (1 = first). Negative: count from end (-1 = last). 0 defaults to 1. Only counts messages of selected role.</div>
|
||||
</div>
|
||||
<div class="completion_prompt_manager_popup_entry_form_control flex1">
|
||||
<label for="completion_prompt_manager_popup_entry_form_attach_side">
|
||||
<span data-i18n="prompt_manager_attach_side">Attach Side</span>
|
||||
</label>
|
||||
<select id="completion_prompt_manager_popup_entry_form_attach_side" class="text_pole" name="attach_side">
|
||||
<option value="end">Append (End)</option>
|
||||
<option value="start">Prepend (Start)</option>
|
||||
</select>
|
||||
<div class="text_muted" data-i18n="Prepend to the start or append to the end of the message content.">Prepend to the start or append to the end of the message content.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="completion_prompt_manager_popup_entry_form_control">
|
||||
<div class="flex-container alignItemsCenter" data-i18n="[external_piece_text]The content of this prompt is pulled from elsewhere and cannot be edited here." external_piece_text="The content of this prompt is pulled from elsewhere and cannot be edited here.">
|
||||
<div class="flex1">
|
||||
|
|
|
|||
|
|
@ -1343,7 +1343,14 @@
|
|||
"prompt_manager_position": "位置",
|
||||
"prompt_manager_relative": "相对",
|
||||
"prompt_manager_in_chat": "聊天中",
|
||||
"Relative (to other prompts in prompt manager) or In-chat @ Depth.": "相对(相对于提示管理器中的其他提示)或在聊天中@深度。",
|
||||
"prompt_manager_attach_existing": "附着到已有消息",
|
||||
"Relative (to other prompts in prompt manager), In-chat @ Depth, or attached to an existing message.": "相对(相对于提示管理器中的其他提示)、在聊天中@深度,或者附着到已有的聊天消息上。",
|
||||
"prompt_manager_attach_role": "附着目标角色",
|
||||
"Select the role of the message you want to target.": "选择你想要附着的目标消息角色。",
|
||||
"prompt_manager_attach_index": "消息顺序编号",
|
||||
"Positive: count from start (1 = first). Negative: count from end (-1 = last). 0 defaults to 1. Only counts messages of selected role.": "正数从前往后数(1=第一条),负数从后往前数(-1=最后一条)。0会按1处理。只计算符合所选角色的消息。",
|
||||
"prompt_manager_attach_side": "附着拼接位置",
|
||||
"Prepend to the start or append to the end of the message content.": "拼接到消息内容的开头 (Prepend) 或结尾 (Append)。",
|
||||
"prompt_manager_depth": "深度",
|
||||
"0 = after the last message, 1 = before the last message, etc.": "“0”为在最后一条消息之后,“1”为在最后一条消息之前,等等。",
|
||||
"prompt_manager_order": "排序",
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ const DEFAULT_ORDER = 100;
|
|||
export const INJECTION_POSITION = {
|
||||
RELATIVE: 0,
|
||||
ABSOLUTE: 1,
|
||||
ATTACH_EXISTING: 2,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -178,8 +179,11 @@ class Prompt {
|
|||
* @param {string[]} [param0.injection_trigger] - The generation type trigger for the prompt injection.
|
||||
* @param {boolean} [param0.forbid_overrides] - Indicates if the prompt should not be overridden.
|
||||
* @param {boolean} [param0.extension] - Prompt is added by an extension.
|
||||
* @param {string} [param0.attach_role] - The role of the message to attach to.
|
||||
* @param {number} [param0.attach_index] - The ordinal index of the message to attach to.
|
||||
* @param {string} [param0.attach_side] - The side of the message to attach to (start/end).
|
||||
*/
|
||||
constructor({ identifier, role, content, name, system_prompt, position, injection_depth, injection_position, forbid_overrides, extension, injection_order, injection_trigger } = {}) {
|
||||
constructor({ identifier, role, content, name, system_prompt, position, injection_depth, injection_position, forbid_overrides, extension, injection_order, injection_trigger, attach_role, attach_index, attach_side } = {}) {
|
||||
this.identifier = identifier;
|
||||
this.role = role;
|
||||
this.content = content;
|
||||
|
|
@ -192,6 +196,9 @@ class Prompt {
|
|||
this.extension = extension ?? false;
|
||||
this.injection_order = injection_order ?? DEFAULT_ORDER;
|
||||
this.injection_trigger = injection_trigger ?? [];
|
||||
this.attach_role = attach_role ?? 'user';
|
||||
this.attach_index = attach_index ?? 1;
|
||||
this.attach_side = attach_side ?? 'end';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -551,12 +558,16 @@ class PromptManager {
|
|||
const injectionDepthField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth'));
|
||||
const injectionOrderField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order'));
|
||||
const injectionTriggerField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_trigger'));
|
||||
const attachRoleField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_role'));
|
||||
const attachIndexField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_index'));
|
||||
const attachSideField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_side'));
|
||||
const depthBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block'));
|
||||
const orderBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_order_block'));
|
||||
const forbidOverridesField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides'));
|
||||
const forbidOverridesBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block'));
|
||||
const entrySourceBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block'));
|
||||
const entrySource = /** @type {HTMLSpanElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source'));
|
||||
const attachBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_attach_block'));
|
||||
|
||||
nameField.value = prompt.name;
|
||||
roleField.value = 'system';
|
||||
|
|
@ -570,6 +581,10 @@ class PromptManager {
|
|||
injectionTriggerField.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
depthBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';
|
||||
orderBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';
|
||||
attachBlock.style.display = prompt.injection_position === INJECTION_POSITION.ATTACH_EXISTING ? 'flex' : 'none';
|
||||
attachRoleField.value = prompt.attach_role ?? 'user';
|
||||
attachIndexField.value = (prompt.attach_index ?? 1).toString();
|
||||
attachSideField.value = prompt.attach_side ?? 'end';
|
||||
forbidOverridesField.checked = prompt.forbid_overrides ?? false;
|
||||
forbidOverridesBlock.style.visibility = this.overridablePrompts.includes(prompt.identifier) ? 'visible' : 'hidden';
|
||||
promptField.disabled = prompt.marker ?? false;
|
||||
|
|
@ -908,6 +923,9 @@ class PromptManager {
|
|||
const injectionOrderField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order'));
|
||||
const injectionTriggerField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_trigger'));
|
||||
const forbidOverridesField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides'));
|
||||
const attachRoleField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_role'));
|
||||
const attachIndexField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_index'));
|
||||
const attachSideField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_side'));
|
||||
|
||||
prompt.name = nameField.value;
|
||||
prompt.role = roleField.value;
|
||||
|
|
@ -917,6 +935,9 @@ class PromptManager {
|
|||
prompt.injection_order = Number(injectionOrderField.value);
|
||||
prompt.injection_trigger = Array.from(injectionTriggerField.selectedOptions).map(option => option.value);
|
||||
prompt.forbid_overrides = forbidOverridesField.checked;
|
||||
prompt.attach_role = attachRoleField.value;
|
||||
prompt.attach_index = Number(attachIndexField.value) || 1;
|
||||
prompt.attach_side = attachSideField.value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1370,6 +1391,10 @@ class PromptManager {
|
|||
const forbidOverridesBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block'));
|
||||
const entrySourceBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block'));
|
||||
const entrySource = /** @type {HTMLSpanElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source'));
|
||||
const attachBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_attach_block'));
|
||||
const attachRoleField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_role'));
|
||||
const attachIndexField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_index'));
|
||||
const attachSideField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_side'));
|
||||
const isPulledPrompt = Object.keys(this.promptSources).includes(prompt.identifier);
|
||||
|
||||
nameField.value = prompt.name ?? '';
|
||||
|
|
@ -1385,6 +1410,10 @@ class PromptManager {
|
|||
injectionTriggerField.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
injectionDepthBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';
|
||||
injectionOrderBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';
|
||||
attachBlock.style.display = prompt.injection_position === INJECTION_POSITION.ATTACH_EXISTING ? 'flex' : 'none';
|
||||
attachRoleField.value = prompt.attach_role ?? 'user';
|
||||
attachIndexField.value = (prompt.attach_index ?? 1).toString();
|
||||
attachSideField.value = prompt.attach_side ?? 'end';
|
||||
injectionPositionField.removeAttribute('disabled');
|
||||
forbidOverridesField.checked = prompt.forbid_overrides ?? false;
|
||||
forbidOverridesBlock.style.visibility = this.overridablePrompts.includes(prompt.identifier) ? 'visible' : 'hidden';
|
||||
|
|
@ -1413,13 +1442,20 @@ class PromptManager {
|
|||
handleInjectionPositionChange(event) {
|
||||
const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block');
|
||||
const injectionOrderBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_order_block');
|
||||
const attachBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_attach_block');
|
||||
const injectionPosition = Number(event.target.value);
|
||||
if (injectionPosition === INJECTION_POSITION.ABSOLUTE) {
|
||||
injectionDepthBlock.style.visibility = 'visible';
|
||||
injectionOrderBlock.style.visibility = 'visible';
|
||||
attachBlock.style.display = 'none';
|
||||
} else if (injectionPosition === INJECTION_POSITION.ATTACH_EXISTING) {
|
||||
injectionDepthBlock.style.visibility = 'hidden';
|
||||
injectionOrderBlock.style.visibility = 'hidden';
|
||||
attachBlock.style.display = 'flex';
|
||||
} else {
|
||||
injectionDepthBlock.style.visibility = 'hidden';
|
||||
injectionOrderBlock.style.visibility = 'hidden';
|
||||
attachBlock.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1483,6 +1519,10 @@ class PromptManager {
|
|||
const forbidOverridesBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block'));
|
||||
const entrySourceBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block'));
|
||||
const entrySource = /** @type {HTMLSpanElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source'));
|
||||
const attachBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_attach_block'));
|
||||
const attachRoleField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_role'));
|
||||
const attachIndexField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_index'));
|
||||
const attachSideField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_attach_side'));
|
||||
|
||||
nameField.value = '';
|
||||
roleField.selectedIndex = 0;
|
||||
|
|
@ -1495,6 +1535,10 @@ class PromptManager {
|
|||
injectionTriggerField.value = '';
|
||||
injectionDepthBlock.style.visibility = 'unset';
|
||||
injectionOrderBlock.style.visibility = 'unset';
|
||||
attachBlock.style.display = 'none';
|
||||
attachRoleField.value = 'user';
|
||||
attachIndexField.value = '1';
|
||||
attachSideField.value = 'end';
|
||||
forbidOverridesBlock.style.visibility = 'unset';
|
||||
forbidOverridesField.checked = false;
|
||||
entrySourceBlock.style.display = 'none';
|
||||
|
|
@ -1721,11 +1765,12 @@ class PromptManager {
|
|||
}
|
||||
|
||||
const encodedName = escapeHtml(prompt.name);
|
||||
const isMarkerPrompt = prompt.marker && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE;
|
||||
const isSystemPrompt = !prompt.marker && prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE && !prompt.forbid_overrides;
|
||||
const isImportantPrompt = !prompt.marker && prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE && prompt.forbid_overrides;
|
||||
const isUserPrompt = !prompt.marker && !prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE;
|
||||
const isMarkerPrompt = prompt.marker && prompt.injection_position === INJECTION_POSITION.RELATIVE;
|
||||
const isSystemPrompt = !prompt.marker && prompt.system_prompt && prompt.injection_position === INJECTION_POSITION.RELATIVE && !prompt.forbid_overrides;
|
||||
const isImportantPrompt = !prompt.marker && prompt.system_prompt && prompt.injection_position === INJECTION_POSITION.RELATIVE && prompt.forbid_overrides;
|
||||
const isUserPrompt = !prompt.marker && !prompt.system_prompt && prompt.injection_position === INJECTION_POSITION.RELATIVE;
|
||||
const isInjectionPrompt = prompt.injection_position === INJECTION_POSITION.ABSOLUTE;
|
||||
const isAttachedPrompt = prompt.injection_position === INJECTION_POSITION.ATTACH_EXISTING;
|
||||
const isOverriddenPrompt = Array.isArray(this.overriddenPrompts) && this.overriddenPrompts.includes(prompt.identifier);
|
||||
const importantClass = isImportantPrompt ? `${prefix}prompt_manager_important` : '';
|
||||
const iconLookup = prompt.role === 'system' && (prompt.marker || prompt.system_prompt) ? '' : prompt.role;
|
||||
|
|
@ -1747,9 +1792,13 @@ class PromptManager {
|
|||
${isImportantPrompt ? '<span class="fa-fw fa-solid fa-star" title="Important Prompt"></span>' : ''}
|
||||
${isUserPrompt ? '<span class="fa-fw fa-solid fa-asterisk" title="Preset Prompt"></span>' : ''}
|
||||
${isInjectionPrompt ? '<span class="fa-fw fa-solid fa-syringe" title="In-Chat Injection"></span>' : ''}
|
||||
${isAttachedPrompt ? '<span class="fa-fw fa-solid fa-paperclip" title="Attached to Existing Message"></span>' : ''}
|
||||
${this.isPromptInspectionAllowed(prompt) ? `<a title="${encodedName}" class="prompt-manager-inspect-action">${encodedName}</a>` : `<span title="${encodedName}">${encodedName}</span>`}
|
||||
${roleIcon ? `<span data-role="${escapeHtml(prompt.role)}" class="fa-xs fa-solid ${roleIcon}" title="${roleTitle}"></span>` : ''}
|
||||
${isInjectionPrompt ? `<small class="prompt-manager-injection-depth">@ ${escapeHtml(prompt.injection_depth.toString())}</small>` : ''}
|
||||
${isAttachedPrompt
|
||||
? `<small class="prompt-manager-injection-depth">${escapeHtml(prompt.attach_side === 'start' ? 'Prepend' : 'Append')} to ${escapeHtml(prompt.attach_role)} ${escapeHtml(String(prompt.attach_index))}</small>`
|
||||
: ''}
|
||||
${isOverriddenPrompt ? '<small class="fa-solid fa-address-card prompt-manager-overridden" title="Pulled from a character card"></small>' : ''}
|
||||
</span>
|
||||
<span>
|
||||
|
|
|
|||
|
|
@ -774,6 +774,56 @@ export function formatWorldInfo(value, { wiFormat = null } = {}) {
|
|||
return stringFormat(format, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply attach-existing prompts directly onto the raw chat history messages.
|
||||
* Why: the feature is defined against existing chat turns, not the final prompt stack.
|
||||
* How: we resolve the target within the original latest-first message array by role and
|
||||
* chronological index, then mutate that message content before any in-chat injection runs.
|
||||
* Purpose: make "first user message end" and the generalized attach semantics match the
|
||||
* original requirement instead of counting synthetic prompt-manager messages.
|
||||
*
|
||||
* @param {Prompt[]} attachedPrompts - Prompts configured for ATTACH_EXISTING.
|
||||
* @param {Object[]} messages - Raw chat history messages in latest-first order.
|
||||
*/
|
||||
function applyAttachedPromptsToMessages(attachedPrompts, messages) {
|
||||
if (!Array.isArray(attachedPrompts) || !attachedPrompts.length || !Array.isArray(messages) || !messages.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const attachPrompt of attachedPrompts) {
|
||||
const attachContent = String(attachPrompt?.content ?? '').trim();
|
||||
const attachRole = String(attachPrompt?.attach_role ?? '').trim();
|
||||
const attachSide = attachPrompt?.attach_side === 'start' ? 'start' : 'end';
|
||||
const requestedIndex = Number(attachPrompt?.attach_index) || 1;
|
||||
|
||||
if (!attachContent || !attachRole) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const chronologicalRoleMatches = messages
|
||||
.filter(message => message && !message.injected && message.role === attachRole)
|
||||
.slice()
|
||||
.reverse();
|
||||
|
||||
if (!chronologicalRoleMatches.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const normalizedIndex = requestedIndex === 0 ? 1 : requestedIndex;
|
||||
const targetIndex = normalizedIndex > 0 ? normalizedIndex - 1 : chronologicalRoleMatches.length + normalizedIndex;
|
||||
const targetMessage = chronologicalRoleMatches[targetIndex];
|
||||
|
||||
if (!targetMessage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const currentContent = String(targetMessage.content ?? '');
|
||||
targetMessage.content = attachSide === 'start'
|
||||
? [attachContent, currentContent].filter(Boolean).join('\n\n')
|
||||
: [currentContent, attachContent].filter(Boolean).join('\n\n');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function populates the injections in the conversation.
|
||||
*
|
||||
|
|
@ -1243,7 +1293,7 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
|
|||
// Add ordered system and user prompts
|
||||
const systemPrompts = ['nsfw', 'jailbreak'];
|
||||
const userRelativePrompts = prompts.collection
|
||||
.filter((prompt) => false === prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE)
|
||||
.filter((prompt) => false === prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE && prompt.injection_position !== INJECTION_POSITION.ATTACH_EXISTING)
|
||||
.reduce((acc, prompt) => {
|
||||
acc.push(prompt.identifier);
|
||||
return acc;
|
||||
|
|
@ -1254,6 +1304,8 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
|
|||
acc.push(prompt);
|
||||
return acc;
|
||||
}, []);
|
||||
const attachedPrompts = prompts.collection
|
||||
.filter((prompt) => prompt.injection_position === INJECTION_POSITION.ATTACH_EXISTING);
|
||||
|
||||
for (const identifier of [...systemPrompts, ...userRelativePrompts]) {
|
||||
await addToChatCompletion(identifier);
|
||||
|
|
@ -1318,6 +1370,12 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
|
|||
chatCompletion.reserveBudget(toolTokens);
|
||||
}
|
||||
|
||||
// Apply attach-existing prompts before later prompt assembly mutates the history.
|
||||
// This keeps indexing anchored to real chat turns instead of synthetic completion rows.
|
||||
if (attachedPrompts.length > 0) {
|
||||
applyAttachedPromptsToMessages(attachedPrompts, messages);
|
||||
}
|
||||
|
||||
// Displace the message to be continued from its original position before performing in-chat injections
|
||||
// In case if it is an assistant message, we want to prepend the users assistant prefill on the message
|
||||
if (type === 'continue' && oai_settings.continue_prefill && messages.length) {
|
||||
|
|
|
|||
19
tests/attach-existing-message-contract.test.mjs
Normal file
19
tests/attach-existing-message-contract.test.mjs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
||||
function readProjectFile(relativePath) {
|
||||
return readFile(path.join(REPO_ROOT, relativePath), 'utf8');
|
||||
}
|
||||
|
||||
test('attach existing message logic runs on raw chat history before in-chat injections', async () => {
|
||||
const openaiSource = await readProjectFile('src/scripts/openai.js');
|
||||
|
||||
assert.match(openaiSource, /function applyAttachedPromptsToMessages\(attachedPrompts, messages\)/);
|
||||
assert.match(openaiSource, /applyAttachedPromptsToMessages\(attachedPrompts, messages\);[\s\S]*messages = await populationInjectionPrompts\(absolutePrompts, messages\);/);
|
||||
assert.doesNotMatch(openaiSource, /chatCompletion\.getMessages\(\)[\s\S]*attach/);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue