178 lines
7.0 KiB
Python
178 lines
7.0 KiB
Python
"""AI persona profiles for prompt injection."""
|
|
|
|
import os
|
|
from dataclasses import dataclass, field
|
|
from typing import Optional, List
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
from .config import get_config_dir
|
|
|
|
|
|
@dataclass
|
|
class Profile:
|
|
"""An AI persona profile with system instructions."""
|
|
name: str
|
|
description: str = ""
|
|
system_prompt: str = ""
|
|
tags: List[str] = field(default_factory=list)
|
|
builtin: bool = False # True for default profiles
|
|
|
|
def to_dict(self) -> dict:
|
|
d = {
|
|
"name": self.name,
|
|
"description": self.description,
|
|
"system_prompt": self.system_prompt,
|
|
}
|
|
if self.tags:
|
|
d["tags"] = self.tags
|
|
return d
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict, builtin: bool = False) -> "Profile":
|
|
return cls(
|
|
name=data.get("name", ""),
|
|
description=data.get("description", ""),
|
|
system_prompt=data.get("system_prompt", ""),
|
|
tags=data.get("tags", []),
|
|
builtin=builtin
|
|
)
|
|
|
|
|
|
# Built-in default profiles
|
|
DEFAULT_PROFILES = [
|
|
Profile(
|
|
name="None",
|
|
description="No profile - use prompt as-is",
|
|
system_prompt="",
|
|
builtin=True
|
|
),
|
|
Profile(
|
|
name="Comedian",
|
|
description="Witty and humorous, but stays informative",
|
|
system_prompt="""You are a witty and humorous assistant who uses humor to make information more engaging and memorable. You're able to be serious when it matters, but you naturally inject levity and clever observations into your responses. You are an expert at explaining things clearly while keeping the reader entertained.""",
|
|
tags=["creative", "engaging"],
|
|
builtin=True
|
|
),
|
|
Profile(
|
|
name="Technical Writer",
|
|
description="Precise, structured, uses proper terminology",
|
|
system_prompt="""You are a professional technical writer who prioritizes clarity, precision, and proper structure. You use correct terminology, organize information logically with headers and bullet points when appropriate, and ensure accuracy in all technical details. You write documentation that is both comprehensive and accessible.""",
|
|
tags=["technical", "documentation"],
|
|
builtin=True
|
|
),
|
|
Profile(
|
|
name="Teacher",
|
|
description="Patient, explains step-by-step, uses analogies",
|
|
system_prompt="""You are a patient and experienced teacher who excels at breaking down complex topics into understandable parts. You explain concepts step-by-step, use relatable analogies and examples, and check for understanding. You adapt your explanations to the learner's level and never make them feel bad for not knowing something.""",
|
|
tags=["educational", "patient"],
|
|
builtin=True
|
|
),
|
|
Profile(
|
|
name="Concise",
|
|
description="Brief responses, bullet points, no fluff",
|
|
system_prompt="""You are a direct and efficient communicator who values brevity. You get straight to the point, use bullet points and short sentences, and avoid unnecessary filler words or lengthy explanations. Every word you write serves a purpose. You provide complete information in the most compact form possible.""",
|
|
tags=["brief", "efficient"],
|
|
builtin=True
|
|
),
|
|
Profile(
|
|
name="Creative",
|
|
description="Imaginative, thinks outside the box",
|
|
system_prompt="""You are a highly creative thinker who approaches problems from unexpected angles. You generate novel ideas, make unique connections between concepts, and aren't afraid to suggest unconventional solutions. You balance creativity with practicality, ensuring your ideas are both innovative and actionable.""",
|
|
tags=["creative", "innovative"],
|
|
builtin=True
|
|
),
|
|
Profile(
|
|
name="Code Reviewer",
|
|
description="Critical eye, suggests improvements, follows best practices",
|
|
system_prompt="""You are an experienced code reviewer with a keen eye for quality. You identify potential bugs, security issues, and performance problems. You suggest improvements based on best practices and design patterns. You explain the reasoning behind your suggestions and prioritize the most impactful changes. You're constructive, not harsh.""",
|
|
tags=["developer", "code"],
|
|
builtin=True
|
|
),
|
|
Profile(
|
|
name="Analyst",
|
|
description="Data-driven, objective, thorough analysis",
|
|
system_prompt="""You are a meticulous analyst who examines information thoroughly and objectively. You consider multiple perspectives, identify patterns and trends, and support your conclusions with evidence. You present findings in a clear, structured manner and acknowledge uncertainties or limitations in your analysis.""",
|
|
tags=["analytical", "objective"],
|
|
builtin=True
|
|
),
|
|
]
|
|
|
|
|
|
def get_profiles_dir() -> Path:
|
|
"""Get the profiles directory path."""
|
|
profiles_dir = Path(get_config_dir()) / "profiles"
|
|
profiles_dir.mkdir(parents=True, exist_ok=True)
|
|
return profiles_dir
|
|
|
|
|
|
def load_profile(name: str) -> Optional[Profile]:
|
|
"""Load a profile by name."""
|
|
# Check built-in profiles first
|
|
for profile in DEFAULT_PROFILES:
|
|
if profile.name.lower() == name.lower():
|
|
return profile
|
|
|
|
# Check user profiles
|
|
profile_path = get_profiles_dir() / f"{name}.yaml"
|
|
if profile_path.exists():
|
|
try:
|
|
with open(profile_path) as f:
|
|
data = yaml.safe_load(f)
|
|
return Profile.from_dict(data)
|
|
except Exception:
|
|
return None
|
|
|
|
return None
|
|
|
|
|
|
def save_profile(profile: Profile) -> None:
|
|
"""Save a user profile."""
|
|
if profile.builtin:
|
|
raise ValueError("Cannot save built-in profiles")
|
|
|
|
profile_path = get_profiles_dir() / f"{profile.name}.yaml"
|
|
with open(profile_path, "w") as f:
|
|
yaml.safe_dump(profile.to_dict(), f, default_flow_style=False)
|
|
|
|
|
|
def delete_profile(name: str) -> bool:
|
|
"""Delete a user profile."""
|
|
# Can't delete built-in profiles
|
|
for profile in DEFAULT_PROFILES:
|
|
if profile.name.lower() == name.lower():
|
|
return False
|
|
|
|
profile_path = get_profiles_dir() / f"{name}.yaml"
|
|
if profile_path.exists():
|
|
profile_path.unlink()
|
|
return True
|
|
return False
|
|
|
|
|
|
def list_profiles() -> List[Profile]:
|
|
"""List all available profiles (built-in + user)."""
|
|
profiles = list(DEFAULT_PROFILES)
|
|
|
|
# Load user profiles
|
|
profiles_dir = get_profiles_dir()
|
|
if profiles_dir.exists():
|
|
for path in profiles_dir.glob("*.yaml"):
|
|
try:
|
|
with open(path) as f:
|
|
data = yaml.safe_load(f)
|
|
profile = Profile.from_dict(data)
|
|
# Don't add duplicates of built-in names
|
|
if not any(p.name.lower() == profile.name.lower() for p in profiles):
|
|
profiles.append(profile)
|
|
except Exception:
|
|
continue
|
|
|
|
return profiles
|
|
|
|
|
|
def get_profile_names() -> List[str]:
|
|
"""Get list of all profile names."""
|
|
return [p.name for p in list_profiles()]
|