feat: implement Phase 2 roleplay context injection
Add full context injection system for World Info, Author's Note, and Persona:
- Add scan_for_world_info() function to detect keywords in last 20 messages
- Add build_roleplay_context() to assemble all roleplay additions
- Inject Persona into system prompt as [{{user}}'s Persona: name - desc]
- Inject activated World Info entries into system prompt by priority
- Inject Author's Note as system message before last 3 messages
- Apply context injection to all 4 chat functions:
- chat() - regular non-streaming
- chat_stream() - streaming
- generate_response_only() - regenerate non-streaming
- generate_response_stream() - regenerate streaming
All roleplay features now fully functional and affecting AI generation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -846,6 +846,99 @@ fn get_api_config() -> Result<ApiConfig, String> {
|
|||||||
load_config().ok_or_else(|| "No config found".to_string())
|
load_config().ok_or_else(|| "No config found".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Roleplay Context Injection Logic
|
||||||
|
|
||||||
|
// Scan messages for World Info keywords and return activated entries
|
||||||
|
fn scan_for_world_info(messages: &[Message], world_info: &[WorldInfoEntry], scan_depth: usize) -> Vec<WorldInfoEntry> {
|
||||||
|
let mut activated_entries = Vec::new();
|
||||||
|
|
||||||
|
// Scan the last N messages (scan_depth)
|
||||||
|
let messages_to_scan: Vec<&Message> = messages.iter()
|
||||||
|
.rev()
|
||||||
|
.take(scan_depth)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for entry in world_info {
|
||||||
|
if !entry.enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any keyword matches in the scanned messages
|
||||||
|
let mut activated = false;
|
||||||
|
for message in &messages_to_scan {
|
||||||
|
let content = message.get_content();
|
||||||
|
|
||||||
|
for keyword in &entry.keys {
|
||||||
|
let matches = if entry.case_sensitive {
|
||||||
|
content.contains(keyword)
|
||||||
|
} else {
|
||||||
|
content.to_lowercase().contains(&keyword.to_lowercase())
|
||||||
|
};
|
||||||
|
|
||||||
|
if matches {
|
||||||
|
activated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if activated {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if activated {
|
||||||
|
activated_entries.push(entry.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by priority (higher first)
|
||||||
|
activated_entries.sort_by(|a, b| b.priority.cmp(&a.priority));
|
||||||
|
|
||||||
|
activated_entries
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build injected context from roleplay settings
|
||||||
|
fn build_roleplay_context(
|
||||||
|
_character: &Character,
|
||||||
|
messages: &[Message],
|
||||||
|
settings: &RoleplaySettings,
|
||||||
|
) -> (String, Option<String>) {
|
||||||
|
let mut system_additions = String::new();
|
||||||
|
let mut authors_note_content = None;
|
||||||
|
|
||||||
|
// 1. Add Persona to system prompt
|
||||||
|
if settings.persona_enabled {
|
||||||
|
if let Some(name) = &settings.persona_name {
|
||||||
|
if let Some(desc) = &settings.persona_description {
|
||||||
|
system_additions.push_str(&format!("\n\n[{{{{user}}}}'s Persona: {} - {}]", name, desc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Scan for World Info and add to system prompt
|
||||||
|
let scan_depth = 20; // Scan last 20 messages
|
||||||
|
let activated_entries = scan_for_world_info(messages, &settings.world_info, scan_depth);
|
||||||
|
|
||||||
|
if !activated_entries.is_empty() {
|
||||||
|
system_additions.push_str("\n\n[Relevant World Information:");
|
||||||
|
for entry in activated_entries {
|
||||||
|
system_additions.push_str(&format!("\n- {}", entry.content));
|
||||||
|
}
|
||||||
|
system_additions.push_str("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Store Author's Note for later injection
|
||||||
|
if settings.authors_note_enabled {
|
||||||
|
if let Some(note) = &settings.authors_note {
|
||||||
|
if !note.is_empty() {
|
||||||
|
authors_note_content = Some(note.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(system_additions, authors_note_content)
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn chat(message: String) -> Result<String, String> {
|
async fn chat(message: String) -> Result<String, String> {
|
||||||
let config = load_config().ok_or_else(|| "API not configured".to_string())?;
|
let config = load_config().ok_or_else(|| "API not configured".to_string())?;
|
||||||
@@ -863,8 +956,13 @@ async fn chat(message: String) -> Result<String, String> {
|
|||||||
format!("{}/v1/chat/completions", base)
|
format!("{}/v1/chat/completions", base)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Load roleplay settings and build context
|
||||||
|
let roleplay_settings = load_roleplay_settings(&character.id);
|
||||||
|
let (system_additions, authors_note) = build_roleplay_context(&character, &history.messages, &roleplay_settings);
|
||||||
|
|
||||||
// Build messages with system prompt first - use simple Message for API
|
// Build messages with system prompt first - use simple Message for API
|
||||||
let mut api_messages = vec![Message::new_user(character.system_prompt.clone())];
|
let enhanced_system_prompt = format!("{}{}", character.system_prompt, system_additions);
|
||||||
|
let mut api_messages = vec![Message::new_user(enhanced_system_prompt)];
|
||||||
api_messages[0].role = "system".to_string();
|
api_messages[0].role = "system".to_string();
|
||||||
|
|
||||||
// Add history messages with current swipe content
|
// Add history messages with current swipe content
|
||||||
@@ -874,6 +972,16 @@ async fn chat(message: String) -> Result<String, String> {
|
|||||||
api_messages.push(api_msg);
|
api_messages.push(api_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert Author's Note before last 3 messages if it exists
|
||||||
|
if let Some(note) = authors_note {
|
||||||
|
if api_messages.len() > 4 { // system + at least 3 messages
|
||||||
|
let insert_pos = api_messages.len().saturating_sub(3);
|
||||||
|
let mut note_msg = Message::new_user(format!("[Author's Note: {}]", note));
|
||||||
|
note_msg.role = "system".to_string();
|
||||||
|
api_messages.insert(insert_pos, note_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let request = ChatRequest {
|
let request = ChatRequest {
|
||||||
model: config.model.clone(),
|
model: config.model.clone(),
|
||||||
max_tokens: 4096,
|
max_tokens: 4096,
|
||||||
@@ -930,8 +1038,13 @@ async fn chat_stream(app_handle: tauri::AppHandle, message: String) -> Result<St
|
|||||||
format!("{}/v1/chat/completions", base)
|
format!("{}/v1/chat/completions", base)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Load roleplay settings and build context
|
||||||
|
let roleplay_settings = load_roleplay_settings(&character.id);
|
||||||
|
let (system_additions, authors_note) = build_roleplay_context(&character, &history.messages, &roleplay_settings);
|
||||||
|
|
||||||
// Build messages with system prompt first - use simple Message for API
|
// Build messages with system prompt first - use simple Message for API
|
||||||
let mut api_messages = vec![Message::new_user(character.system_prompt.clone())];
|
let enhanced_system_prompt = format!("{}{}", character.system_prompt, system_additions);
|
||||||
|
let mut api_messages = vec![Message::new_user(enhanced_system_prompt)];
|
||||||
api_messages[0].role = "system".to_string();
|
api_messages[0].role = "system".to_string();
|
||||||
|
|
||||||
// Add history messages with current swipe content
|
// Add history messages with current swipe content
|
||||||
@@ -941,6 +1054,16 @@ async fn chat_stream(app_handle: tauri::AppHandle, message: String) -> Result<St
|
|||||||
api_messages.push(api_msg);
|
api_messages.push(api_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert Author's Note before last 3 messages if it exists
|
||||||
|
if let Some(note) = authors_note {
|
||||||
|
if api_messages.len() > 4 { // system + at least 3 messages
|
||||||
|
let insert_pos = api_messages.len().saturating_sub(3);
|
||||||
|
let mut note_msg = Message::new_user(format!("[Author's Note: {}]", note));
|
||||||
|
note_msg.role = "system".to_string();
|
||||||
|
api_messages.insert(insert_pos, note_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let request = StreamChatRequest {
|
let request = StreamChatRequest {
|
||||||
model: config.model.clone(),
|
model: config.model.clone(),
|
||||||
max_tokens: 4096,
|
max_tokens: 4096,
|
||||||
@@ -1090,8 +1213,13 @@ async fn generate_response_only() -> Result<String, String> {
|
|||||||
format!("{}/v1/chat/completions", base)
|
format!("{}/v1/chat/completions", base)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build messages with system prompt first
|
// Load roleplay settings and build context
|
||||||
let mut api_messages = vec![Message::new_user(character.system_prompt.clone())];
|
let roleplay_settings = load_roleplay_settings(&character.id);
|
||||||
|
let (system_additions, authors_note) = build_roleplay_context(&character, &history.messages, &roleplay_settings);
|
||||||
|
|
||||||
|
// Build messages with enhanced system prompt first
|
||||||
|
let enhanced_system_prompt = format!("{}{}", character.system_prompt, system_additions);
|
||||||
|
let mut api_messages = vec![Message::new_user(enhanced_system_prompt)];
|
||||||
api_messages[0].role = "system".to_string();
|
api_messages[0].role = "system".to_string();
|
||||||
|
|
||||||
// Add existing history (which already includes the user message)
|
// Add existing history (which already includes the user message)
|
||||||
@@ -1101,6 +1229,16 @@ async fn generate_response_only() -> Result<String, String> {
|
|||||||
api_messages.push(api_msg);
|
api_messages.push(api_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert Author's Note before last 3 messages if it exists
|
||||||
|
if let Some(note) = authors_note {
|
||||||
|
if api_messages.len() > 4 { // system + at least 3 messages
|
||||||
|
let insert_pos = api_messages.len().saturating_sub(3);
|
||||||
|
let mut note_msg = Message::new_user(format!("[Author's Note: {}]", note));
|
||||||
|
note_msg.role = "system".to_string();
|
||||||
|
api_messages.insert(insert_pos, note_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let request = ChatRequest {
|
let request = ChatRequest {
|
||||||
model: config.model.clone(),
|
model: config.model.clone(),
|
||||||
max_tokens: 4096,
|
max_tokens: 4096,
|
||||||
@@ -1148,8 +1286,13 @@ async fn generate_response_stream(app_handle: tauri::AppHandle) -> Result<String
|
|||||||
format!("{}/v1/chat/completions", base)
|
format!("{}/v1/chat/completions", base)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build messages with system prompt first
|
// Load roleplay settings and build context
|
||||||
let mut api_messages = vec![Message::new_user(character.system_prompt.clone())];
|
let roleplay_settings = load_roleplay_settings(&character.id);
|
||||||
|
let (system_additions, authors_note) = build_roleplay_context(&character, &history.messages, &roleplay_settings);
|
||||||
|
|
||||||
|
// Build messages with enhanced system prompt first
|
||||||
|
let enhanced_system_prompt = format!("{}{}", character.system_prompt, system_additions);
|
||||||
|
let mut api_messages = vec![Message::new_user(enhanced_system_prompt)];
|
||||||
api_messages[0].role = "system".to_string();
|
api_messages[0].role = "system".to_string();
|
||||||
|
|
||||||
// Add existing history (which already includes the user message)
|
// Add existing history (which already includes the user message)
|
||||||
@@ -1159,6 +1302,16 @@ async fn generate_response_stream(app_handle: tauri::AppHandle) -> Result<String
|
|||||||
api_messages.push(api_msg);
|
api_messages.push(api_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert Author's Note before last 3 messages if it exists
|
||||||
|
if let Some(note) = authors_note {
|
||||||
|
if api_messages.len() > 4 { // system + at least 3 messages
|
||||||
|
let insert_pos = api_messages.len().saturating_sub(3);
|
||||||
|
let mut note_msg = Message::new_user(format!("[Author's Note: {}]", note));
|
||||||
|
note_msg.role = "system".to_string();
|
||||||
|
api_messages.insert(insert_pos, note_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let request = StreamChatRequest {
|
let request = StreamChatRequest {
|
||||||
model: config.model.clone(),
|
model: config.model.clone(),
|
||||||
max_tokens: 4096,
|
max_tokens: 4096,
|
||||||
|
|||||||
Reference in New Issue
Block a user