danielrosehill's picture
updated
ef9649a
"""
Dynamic loader for system prompt components.
Automatically loads all prompt components from their respective directories.
"""
import os
import importlib.util
import re
def load_module_from_file(file_path):
"""Load a Python module from a file path."""
module_name = os.path.splitext(os.path.basename(file_path))[0]
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def load_prompts_from_directory(directory):
"""Load all prompt components from a directory."""
prompts = {}
if not os.path.exists(directory):
return prompts
for file in os.listdir(directory):
if file.endswith('.py') and file != '__init__.py':
file_path = os.path.join(directory, file)
try:
module = load_module_from_file(file_path)
name = os.path.splitext(file)[0]
if hasattr(module, 'PROMPT'):
prompts[name] = module.PROMPT.strip()
except Exception as e:
print(f"Error loading {file}: {str(e)}")
return prompts
# Get the base directory for prompt components
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# Load all prompt components
MODEL_PERSONALITIES = load_prompts_from_directory(
os.path.join(BASE_DIR, 'model_personalities')
)
USER_GEOLOCATION = load_prompts_from_directory(
os.path.join(BASE_DIR, 'user_geolocation')
)
USER_PERSONALITY = load_prompts_from_directory(
os.path.join(BASE_DIR, 'user_personality')
)
USER_WORLDVIEW = load_prompts_from_directory(
os.path.join(BASE_DIR, 'user_worldview')
)
USER_POLITICAL_LEANING = load_prompts_from_directory(
os.path.join(BASE_DIR, 'user_political_leaning')
)
MODEL_FORMALITY = load_prompts_from_directory(
os.path.join(BASE_DIR, 'model_formality')
)
MODEL_RESPONSE_STYLE = load_prompts_from_directory(
os.path.join(BASE_DIR, 'model_response_style')
)
MODEL_EXPERTISE = load_prompts_from_directory(
os.path.join(BASE_DIR, 'model_expertise')
)
MODEL_LANGUAGE_STYLE = load_prompts_from_directory(
os.path.join(BASE_DIR, 'model_language_style')
)
MODEL_IDENTITY = load_prompts_from_directory(
os.path.join(BASE_DIR, 'model_identity')
)
USER_LEARNING_STYLE = load_prompts_from_directory(
os.path.join(BASE_DIR, 'user_learning_style')
)
USER_COMMUNICATION_PACE = load_prompts_from_directory(
os.path.join(BASE_DIR, 'user_communication_pace')
)
USER_OUTPUT_PREFERENCE = load_prompts_from_directory(
os.path.join(BASE_DIR, 'user_output_preference')
)
def truncate_to_word_length(text: str, target_length: int) -> str:
"""
Adjust text to reach target word length by expanding or truncating as needed.
Args:
text: The text to truncate
target_length: Target number of words
Returns:
Truncated text maintaining key elements
"""
if not text or target_length <= 0:
return text
sections = text.split("\n\n")
words = text.split()
current_length = len(words)
# If we're already at target length, return as is
if current_length == target_length:
return text
# If we need to expand
if current_length < target_length:
words_to_add = target_length - current_length
expanded_sections = []
for section in sections:
# Add elaborative phrases to reach target length
expanded = section
if "You are" in section:
expanded += " Your purpose is to assist users effectively and professionally."
if "approach" in section:
expanded += " This approach ensures optimal results and user satisfaction."
expanded_sections.append(expanded)
return "\n\n".join(expanded_sections)
# If we need to reduce
words_per_section = target_length // len(sections)
adjusted_sections = [" ".join(section.split()[:words_per_section]) for section in sections]
return "\n\n".join(adjusted_sections)
def combine_prompts(personality: str, geolocation: str, user_type: str,
worldview: str, political_leaning: str = "neutral",
formality: str = "neutral",
response_style: str = "neutral",
data_format: str = "neutral",
output_preference: str = "neutral",
expertise: str = "neutral",
learning_style: str = "neutral",
communication_pace: str = "neutral",
language_style: str = "neutral",
identity_type: str = "neutral",
ai_name: str = None,
backstory: str = None,
user_name: str = None,
age_group: str = None,
occupation: str = None,
target_length: int = None) -> str:
"""
Combine selected prompts from different categories into a cohesive system prompt.
Args:
personality: Key from MODEL_PERSONALITIES
geolocation: Key from USER_GEOLOCATION
user_type: Key from USER_PERSONALITY
worldview: Key from USER_WORLDVIEW
political_leaning: Key from USER_POLITICAL_LEANING
formality: Key from MODEL_FORMALITY
response_style: Key from MODEL_RESPONSE_STYLE
expertise: Key from MODEL_EXPERTISE
learning_style: Key from USER_LEARNING_STYLE
communication_pace: Key from USER_COMMUNICATION_PACE
target_length: Optional target word length for the final prompt
Returns:
Combined system prompt
"""
# Only include non-neutral components
components = [
f"You are {ai_name}. " if ai_name else "",
backstory + "\n" if backstory else "",
f"The user's name is {user_name}. " if user_name else "",
f"The user is in the age group {age_group}. " if age_group else "",
f"The user's occupation is {occupation}. " if occupation else "",
MODEL_IDENTITY.get(identity_type, "")
if identity_type != "neutral" else "",
MODEL_PERSONALITIES.get(personality, "")
if personality != "neutral" else "",
USER_GEOLOCATION.get(geolocation, "")
if geolocation != "neutral" else "",
USER_PERSONALITY.get(user_type, "")
if user_type != "neutral" else "",
USER_WORLDVIEW.get(worldview, "")
if worldview != "neutral" else "",
USER_POLITICAL_LEANING.get(political_leaning, "")
if political_leaning != "neutral" else "",
MODEL_FORMALITY.get(formality, "")
if formality != "neutral" else "",
MODEL_RESPONSE_STYLE.get(response_style, "")
if response_style != "neutral" else "",
USER_OUTPUT_PREFERENCE.get(output_preference, "")
if output_preference != "neutral" else "",
USER_OUTPUT_PREFERENCE.get(data_format, "")
if data_format != "neutral" else "",
MODEL_EXPERTISE.get(expertise, "")
if expertise != "neutral" else "",
USER_LEARNING_STYLE.get(learning_style, "")
if learning_style != "neutral" else "",
USER_COMMUNICATION_PACE.get(communication_pace, "")
if communication_pace != "neutral" else "",
MODEL_LANGUAGE_STYLE.get(language_style, "")
if language_style != "neutral" else ""
]
# Filter out empty strings
components = [c for c in components if c]
if not components:
return "No prompt components selected."
# Join components
combined = "\n\n".join(components)
# Truncate if target length specified
if target_length and target_length > 0:
return truncate_to_word_length(combined, target_length)
return combined
def get_available_prompts():
"""Get all available prompt options for each category."""
return {
# Model characteristics and preferences
"model_personalities": ["neutral"] + [k for k in MODEL_PERSONALITIES.keys() if k != "neutral"],
"model_formality": ["neutral", "very_informal", "informal", "formal", "extremely_formal"],
"model_response_style": ["neutral", "concise", "balanced", "detailed", "socratic"],
"model_expertise": ["neutral", "generalist", "specialist", "academic", "practical"],
"model_language_style": ["neutral", "shakespearean", "middle_ages", "rhyming"],
"model_identity": ["neutral", "bot", "alien", "sloth"],
# User characteristics
"user_geolocation": ["neutral"] + [k for k in USER_GEOLOCATION.keys() if k != "neutral"],
"user_personality": ["neutral"] + [k for k in USER_PERSONALITY.keys() if k != "neutral"],
"user_worldview": ["neutral"] + [k for k in USER_WORLDVIEW.keys() if k != "neutral"],
"user_political_leaning": ["neutral", "conservative", "progressive"],
"user_learning_style": ["neutral", "visual", "practical", "theoretical", "sequential"],
"user_communication_pace": ["neutral", "methodical", "dynamic", "interactive", "contemplative"],
"user_output_preference": ["neutral"] + [k for k in USER_OUTPUT_PREFERENCE.keys() if k != "neutral"]
}