llm generated image gen prompts
This commit is contained in:
@@ -12,10 +12,15 @@ import {
|
||||
isGenerating,
|
||||
lastActionWasSwipe,
|
||||
setIsGenerating,
|
||||
setLastActionWasSwipe
|
||||
setLastActionWasSwipe,
|
||||
sessionAvatarPrompts
|
||||
} from '../../core/state.js';
|
||||
import { saveChatData } from '../../core/persistence.js';
|
||||
import { generateSeparateUpdatePrompt } from './promptBuilder.js';
|
||||
import {
|
||||
generateSeparateUpdatePrompt,
|
||||
generateAvatarPromptGenerationPrompt,
|
||||
parseAvatarPromptsResponse
|
||||
} from './promptBuilder.js';
|
||||
import { parseResponse, parseUserStats } from './parser.js';
|
||||
import { renderUserStats } from '../rendering/userStats.js';
|
||||
import { renderInfoBox } from '../rendering/infoBox.js';
|
||||
@@ -158,6 +163,15 @@ export async function updateRPGData(renderUserStats, renderInfoBox, renderThough
|
||||
lastGeneratedData.characterThoughts = parsedData.characterThoughts;
|
||||
}
|
||||
|
||||
// Generate avatar prompts if auto-generate is enabled and characters need avatars
|
||||
if (extensionSettings.autoGenerateAvatars) {
|
||||
const charactersNeedingPrompts = parseCharactersWithoutAvatars(parsedData.characterThoughts);
|
||||
if (charactersNeedingPrompts.length > 0) {
|
||||
console.log('[RPG Companion] Generating LLM avatar prompts for:', charactersNeedingPrompts);
|
||||
await generateAvatarPrompts(charactersNeedingPrompts);
|
||||
}
|
||||
}
|
||||
|
||||
// When saveTrackerHistory is enabled, store tracker data on the user's message too
|
||||
// This allows scrolling through history and seeing trackers at each point
|
||||
if (extensionSettings.saveTrackerHistory && lastMessage && lastMessage.is_user) {
|
||||
@@ -253,3 +267,74 @@ export async function updateRPGData(renderUserStats, renderInfoBox, renderThough
|
||||
setLastActionWasSwipe(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses character thoughts to find characters that need avatar prompts
|
||||
* @param {string} characterThoughtsData - Raw character thoughts data
|
||||
* @returns {Array<string>} Array of character names needing prompts
|
||||
*/
|
||||
function parseCharactersWithoutAvatars(characterThoughtsData) {
|
||||
if (!characterThoughtsData) return [];
|
||||
|
||||
const lines = characterThoughtsData.split('\n');
|
||||
const characters = [];
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.trim().startsWith('- ')) {
|
||||
const name = line.trim().substring(2).trim();
|
||||
if (name && name.toLowerCase() !== 'unavailable') {
|
||||
// Skip if already has custom avatar
|
||||
if (extensionSettings.npcAvatars && extensionSettings.npcAvatars[name]) {
|
||||
continue;
|
||||
}
|
||||
// Skip if already has session prompt
|
||||
if (sessionAvatarPrompts[name]) {
|
||||
continue;
|
||||
}
|
||||
characters.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return characters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates LLM-based avatar prompts for specified characters
|
||||
* Called during batch RPG data refresh when avatar generation is enabled
|
||||
*
|
||||
* @param {Array<string>} characterNames - Array of character names needing prompts
|
||||
* @returns {Promise<Object>} Map of character name to generated prompt
|
||||
*/
|
||||
export async function generateAvatarPrompts(characterNames) {
|
||||
if (!characterNames || characterNames.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('[RPG Avatar] Generating LLM prompts for characters:', characterNames);
|
||||
|
||||
const prompt = await generateAvatarPromptGenerationPrompt(characterNames);
|
||||
|
||||
// Generate using raw prompt
|
||||
const response = await generateRaw({
|
||||
prompt: prompt,
|
||||
quietToLoud: false
|
||||
});
|
||||
|
||||
if (response) {
|
||||
const prompts = parseAvatarPromptsResponse(response);
|
||||
console.log('[RPG Avatar] Generated prompts:', prompts);
|
||||
|
||||
// Store in session-only storage
|
||||
for (const [name, prompt] of Object.entries(prompts)) {
|
||||
sessionAvatarPrompts[name] = prompt;
|
||||
}
|
||||
|
||||
return prompts;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[RPG Avatar] LLM prompt generation failed:', error);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -597,3 +597,85 @@ export async function generateSeparateUpdatePrompt() {
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default custom instruction for avatar prompt generation
|
||||
*/
|
||||
const DEFAULT_AVATAR_CUSTOM_INSTRUCTION = `Create a detailed portrait prompt focusing on the character's appearance, clothing, and mood. Include appropriate artistic style keywords.`;
|
||||
|
||||
/**
|
||||
* Generates the prompt for LLM-based avatar prompt generation
|
||||
* Uses the same context as RPG generation (character cards, tracker data, chat history)
|
||||
*
|
||||
* @param {Array<string>} characterNames - Array of character names to generate prompts for
|
||||
* @returns {Promise<Array<{role: string, content: string}>>} Message array for generateRaw API
|
||||
*/
|
||||
export async function generateAvatarPromptGenerationPrompt(characterNames) {
|
||||
const depth = extensionSettings.updateDepth;
|
||||
const messages = [];
|
||||
|
||||
// Build system message with character context
|
||||
let systemMessage = `You are an AI assistant specializing in creating detailed image generation prompts for character avatars.\n\n`;
|
||||
|
||||
// Add character card information (reusing existing function)
|
||||
const characterInfo = await getCharacterCardsInfo();
|
||||
if (characterInfo) {
|
||||
systemMessage += `Character Information:\n${characterInfo}\n\n`;
|
||||
}
|
||||
|
||||
// Add tracker context if available
|
||||
if (committedTrackerData.characterThoughts) {
|
||||
systemMessage += `Current Scene Context:\n${committedTrackerData.characterThoughts}\n\n`;
|
||||
}
|
||||
|
||||
systemMessage += `Recent conversation context:\n<history>`;
|
||||
messages.push({ role: 'system', content: systemMessage });
|
||||
|
||||
// Add chat history
|
||||
const recentMessages = chat.slice(-depth);
|
||||
for (const message of recentMessages) {
|
||||
messages.push({
|
||||
role: message.is_user ? 'user' : 'assistant',
|
||||
content: message.mes
|
||||
});
|
||||
}
|
||||
|
||||
// Build instruction message
|
||||
let instructionMessage = `</history>\n\n`;
|
||||
const customInstruction = extensionSettings.avatarLLMCustomInstruction || DEFAULT_AVATAR_CUSTOM_INSTRUCTION;
|
||||
|
||||
instructionMessage += `Task: Generate detailed image prompts for the following characters.\n\n`;
|
||||
instructionMessage += `Instructions: ${customInstruction}\n\n`;
|
||||
instructionMessage += `Characters:\n`;
|
||||
characterNames.forEach((name, index) => {
|
||||
instructionMessage += `${index + 1}. ${name}\n`;
|
||||
});
|
||||
|
||||
instructionMessage += `\nOutput Format (one per line):\n`;
|
||||
instructionMessage += `CHARACTER_NAME: [detailed prompt]\n\n`;
|
||||
instructionMessage += `Example:\n`;
|
||||
instructionMessage += `Gandalf: portrait, elderly wizard with long white beard, wearing grey robes, holding wooden staff, intense blue eyes, wise expression, fantasy art style\n\n`;
|
||||
instructionMessage += `Provide ONLY the formatted prompts, no other text.`;
|
||||
|
||||
messages.push({ role: 'user', content: instructionMessage });
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses LLM response to extract character prompts
|
||||
* @param {string} response - Raw LLM response
|
||||
* @returns {Object} Map of character name to prompt
|
||||
*/
|
||||
export function parseAvatarPromptsResponse(response) {
|
||||
const prompts = {};
|
||||
const lines = response.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
const match = trimmed.match(/^([^:]+):\s*(.+)$/);
|
||||
if (match) {
|
||||
prompts[match[1].trim()] = match[2].trim();
|
||||
}
|
||||
}
|
||||
return prompts;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user