v3.6.2: Fix relationship field in context for manually added characters, add empty field placeholders and mobile support

This commit is contained in:
Spicy_Marinara
2026-01-18 19:15:30 +01:00
parent 2a48c30808
commit f78c8a1b78
8 changed files with 338 additions and 83 deletions
+35 -35
View File
@@ -86,8 +86,8 @@ function buildHistoricalContextMap() {
// For user_message_end: start from the last assistant message (we need its context for the preceding user message)
// For assistant_message_end: start from before the last assistant message (it gets current context via setExtensionPrompt)
let processedCount = 0;
const startIndex = position === 'user_message_end'
? lastAssistantIndex
const startIndex = position === 'user_message_end'
? lastAssistantIndex
: (lastAssistantIndex > 0 ? lastAssistantIndex - 1 : chat.length - 2);
for (let i = startIndex; i >= 0 && (messageCount === 0 || processedCount < maxMessages); i--) {
@@ -201,7 +201,7 @@ function prepareHistoricalContextInjection() {
/**
* Finds the best match position for message content in the prompt.
* Tries full content first, then progressively smaller suffixes.
*
*
* @param {string} prompt - The prompt to search in
* @param {string} messageContent - The message content to find
* @returns {{start: number, end: number}|null} - Position info or null if not found
@@ -213,7 +213,7 @@ function findMessageInPrompt(prompt, messageContent) {
// Try to find the full content first
let searchIndex = prompt.lastIndexOf(messageContent);
if (searchIndex !== -1) {
return { start: searchIndex, end: searchIndex + messageContent.length };
}
@@ -221,15 +221,15 @@ function findMessageInPrompt(prompt, messageContent) {
// If full content not found, try last N characters with progressively smaller chunks
// This handles cases where messages are truncated in the prompt
const searchLengths = [500, 300, 200, 100, 50];
for (const len of searchLengths) {
if (messageContent.length <= len) {
continue;
}
const searchContent = messageContent.slice(-len);
searchIndex = prompt.lastIndexOf(searchContent);
if (searchIndex !== -1) {
return { start: searchIndex, end: searchIndex + searchContent.length };
}
@@ -241,7 +241,7 @@ function findMessageInPrompt(prompt, messageContent) {
/**
* Injects historical context into a text completion prompt string.
* Searches for message content in the prompt and appends context after matches.
*
*
* @param {string} prompt - The text completion prompt
* @returns {string} - The modified prompt with injected context
*/
@@ -268,7 +268,7 @@ function injectContextIntoTextPrompt(prompt) {
// Find the message content in the prompt
const position = findMessageInPrompt(modifiedPrompt, message.mes);
if (!position) {
// Message not found in prompt (might be truncated or not included)
console.debug(`[RPG Companion] Could not find message ${msgIdx} in prompt for context injection`);
@@ -290,7 +290,7 @@ function injectContextIntoTextPrompt(prompt) {
/**
* Injects historical context into a chat completion message array.
* Modifies the content of messages in the array directly.
*
*
* @param {Array} chatMessages - The chat completion message array
* @returns {Array} - The modified message array with injected context
*/
@@ -315,7 +315,7 @@ function injectContextIntoChatPrompt(chatMessages) {
// Find this message in the chat completion array by matching content
// Try full content first, then progressively smaller suffixes
let found = false;
for (const promptMsg of chatMessages) {
if (!promptMsg.content || typeof promptMsg.content !== 'string') {
continue;
@@ -335,7 +335,7 @@ function injectContextIntoChatPrompt(chatMessages) {
if (messageContent.length <= len) {
continue;
}
const searchContent = messageContent.slice(-len);
if (promptMsg.content.includes(searchContent)) {
promptMsg.content = promptMsg.content + ctxContent;
@@ -344,12 +344,12 @@ function injectContextIntoChatPrompt(chatMessages) {
break;
}
}
if (found) {
break;
}
}
if (!found) {
console.debug(`[RPG Companion] Could not find message ${msgIdx} in chat prompt for context injection`);
}
@@ -365,7 +365,7 @@ function injectContextIntoChatPrompt(chatMessages) {
/**
* Injects historical context into finalMesSend message array (text completion).
* Iterates through chat and finalMesSend in order, matching by content to skip injected messages.
*
*
* @param {Array} finalMesSend - The array of message objects {message: string, extensionPrompts: []}
* @returns {number} - Number of injections made
*/
@@ -381,20 +381,20 @@ function injectContextIntoFinalMesSend(finalMesSend) {
}
let injectedCount = 0;
// Build a map from chat index to finalMesSend index by matching content in order
// This handles injected messages (author's note, OOC, etc.) that exist in finalMesSend but not in chat
const chatToMesSendMap = new Map();
let mesSendIdx = 0;
for (let chatIdx = 0; chatIdx < chat.length && mesSendIdx < finalMesSend.length; chatIdx++) {
const chatMsg = chat[chatIdx];
if (!chatMsg || chatMsg.is_system) {
continue;
}
const chatContent = chatMsg.mes || '';
// Look for this chat message in finalMesSend starting from current position
// Skip any finalMesSend entries that don't match (they're injected content)
while (mesSendIdx < finalMesSend.length) {
@@ -403,40 +403,40 @@ function injectContextIntoFinalMesSend(finalMesSend) {
mesSendIdx++;
continue;
}
// Check if this finalMesSend message contains the chat content
// Use a substring match since instruct formatting adds prefixes/suffixes
// Match with sufficient content (first 50 chars or full message if shorter)
const matchContent = chatContent.length > 50
? chatContent.substring(0, 50)
const matchContent = chatContent.length > 50
? chatContent.substring(0, 50)
: chatContent;
if (matchContent && mesSendObj.message.includes(matchContent)) {
// Found a match - record the mapping
chatToMesSendMap.set(chatIdx, mesSendIdx);
mesSendIdx++;
break;
}
// This finalMesSend entry doesn't match - it's injected content, skip it
mesSendIdx++;
}
}
// Now inject context using the map
for (const [chatIdx, ctxContent] of pendingContextMap) {
const targetMesSendIdx = chatToMesSendMap.get(chatIdx);
if (targetMesSendIdx === undefined) {
console.debug(`[RPG Companion] Chat message ${chatIdx} not found in finalMesSend mapping`);
continue;
}
const mesSendObj = finalMesSend[targetMesSendIdx];
if (!mesSendObj || !mesSendObj.message) {
continue;
}
// Append context to this message
mesSendObj.message = mesSendObj.message + ctxContent;
injectedCount++;
@@ -450,7 +450,7 @@ function injectContextIntoFinalMesSend(finalMesSend) {
* Event handler for GENERATE_BEFORE_COMBINE_PROMPTS (text completion).
* Injects historical context into the finalMesSend array before prompt combination.
* This is more reliable than post-combine string searching.
*
*
* @param {Object} eventData - Event data with finalMesSend and other properties
*/
function onGenerateBeforeCombinePrompts(eventData) {
@@ -478,7 +478,7 @@ function onGenerateBeforeCombinePrompts(eventData) {
/**
* Event handler for GENERATE_AFTER_COMBINE_PROMPTS (text completion).
* This is now a backup/fallback - primary injection happens in BEFORE_COMBINE.
*
*
* @param {Object} eventData - Event data with prompt property
*/
function onGenerateAfterCombinePrompts(eventData) {
@@ -508,7 +508,7 @@ function onGenerateAfterCombinePrompts(eventData) {
/**
* Event handler for CHAT_COMPLETION_PROMPT_READY.
* Injects historical context into the chat message array.
*
*
* @param {Object} eventData - Event data with chat property
*/
function onChatCompletionPromptReady(eventData) {
@@ -938,16 +938,16 @@ Ensure these details naturally reflect and influence the narrative. Character be
export function initHistoryInjectionListeners() {
// Register persistent listeners for prompt injection
// These check pendingContextMap and only inject if there's data
// Primary: BEFORE_COMBINE for text completion (more reliable - modifies message objects)
eventSource.on(event_types.GENERATE_BEFORE_COMBINE_PROMPTS, onGenerateBeforeCombinePrompts);
// Fallback: AFTER_COMBINE for text completion (string-based injection)
eventSource.on(event_types.GENERATE_AFTER_COMBINE_PROMPTS, onGenerateAfterCombinePrompts);
// Chat completion (OpenAI, etc.)
eventSource.on(event_types.CHAT_COMPLETION_PROMPT_READY, onChatCompletionPromptReady);
console.log('[RPG Companion] History injection listeners initialized');
}
+6 -5
View File
@@ -725,13 +725,14 @@ function formatTrackerDataForContext(jsonData, trackerType, userName) {
}
}
// Relationship
if (char.relationship) {
// Relationship - check both Relationship (new format) and relationship (old format)
const relationshipValue = char.Relationship || char.relationship;
if (relationshipValue) {
let relValue;
if (typeof char.relationship === 'object' && !Array.isArray(char.relationship) && 'status' in char.relationship) {
relValue = getValue(char.relationship.status);
if (typeof relationshipValue === 'object' && !Array.isArray(relationshipValue) && 'status' in relationshipValue) {
relValue = getValue(relationshipValue.status);
} else {
relValue = getValue(char.relationship);
relValue = getValue(relationshipValue);
}
if (relValue) formatted += ` Relationship: ${relValue}\n`;
}