Files
ChatAIBot/services/personas.py
T
2026-05-28 08:42:46 +03:00

169 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from typing import Optional
import aiosqlite
from database.db import DB_PATH
DEFAULT_PERSONAS = {
"default": {
"name": "AI Ассистент",
"emoji": "🤖",
"description": "Универсальный помощник",
"prompt": "Ты — полезный AI ассистент. Отвечай чётко и по делу.",
"sd_enabled": False,
},
"rpg_master": {
"name": "Мастер RPG",
"emoji": "🧙",
"description": "Ведёт ролевые игры, создаёт атмосферу",
"prompt": """Ты — опытный Мастер ролевых игр.
Создавай живые описания, веди нарратив, реагируй на действия игрока.
Мир детальный, персонажи запоминающиеся.
Отвечай только текстом сюжета — без тегов изображений.""",
"sd_enabled": True,
},
"villain": {
"name": "Злодей",
"emoji": "😈",
"description": "Харизматичный антагонист",
"prompt": """Ты — харизматичный злодей с грандиозными планами.
Говоришь театрально, с сарказмом и превосходством.
Никогда не выходишь из роли. Называешь собеседника 'герой' с иронией.""",
"sd_enabled": False,
},
"scientist": {
"name": "Учёный",
"emoji": "🔬",
"description": "Объясняет сложное простыми словами",
"prompt": """Ты — увлечённый учёный. Объясняешь любые темы
через факты, аналогии и примеры. Любишь уточнять детали.
Иногда уходишь в интересные отступления.""",
"sd_enabled": False,
},
"samurai": {
"name": "Самурай",
"emoji": "⚔️",
"description": "Мудрый воин феодальной Японии",
"prompt": """Ты — самурай феодальной Японии.
Говоришь кратко, мудро, с достоинством.
Используешь метафоры природы и войны.
Чтишь кодекс бусидо.""",
"sd_enabled": True,
"appearance_tags": "samurai armor, katana, feudal japan",
},
}
def _row_to_persona(row: dict) -> dict:
return {
"name": row["name"],
"emoji": row["emoji"],
"description": row["description"],
"prompt": row["prompt"],
"custom": bool(row["custom"]),
"sd_enabled": bool(row["sd_enabled"]),
"lora_name": row["lora_name"] or "",
"lora_weight": row["lora_weight"] if row["lora_weight"] is not None else 0.8,
"appearance_tags": row["appearance_tags"] or "",
}
async def get_all_personas() -> dict:
async with aiosqlite.connect(DB_PATH) as db:
db.row_factory = aiosqlite.Row
async with db.execute("SELECT * FROM personas ORDER BY custom ASC, persona_id ASC") as cur:
rows = await cur.fetchall()
return {r["persona_id"]: _row_to_persona(dict(r)) for r in rows}
async def get_persona(persona_id: str) -> Optional[dict]:
async with aiosqlite.connect(DB_PATH) as db:
db.row_factory = aiosqlite.Row
async with db.execute(
"SELECT * FROM personas WHERE persona_id = ?", (persona_id,)
) as cur:
row = await cur.fetchone()
if not row:
return None
return _row_to_persona(dict(row))
async def create_persona(
persona_id: str,
name: str,
emoji: str,
description: str,
prompt: str,
sd_enabled: bool = False,
lora_name: str = "",
lora_weight: float = 0.8,
appearance_tags: str = "",
) -> dict:
async with aiosqlite.connect(DB_PATH) as db:
await db.execute(
"""INSERT INTO personas
(persona_id, name, emoji, description, prompt, custom,
sd_enabled, lora_name, lora_weight, appearance_tags)
VALUES (?, ?, ?, ?, ?, 1, ?, ?, ?, ?)""",
(
persona_id, name, emoji, description, prompt,
1 if sd_enabled else 0, lora_name, lora_weight, appearance_tags,
),
)
await db.commit()
return {
"name": name,
"emoji": emoji,
"description": description,
"prompt": prompt,
"custom": True,
"sd_enabled": sd_enabled,
"lora_name": lora_name,
"lora_weight": lora_weight,
"appearance_tags": appearance_tags,
}
async def delete_persona(persona_id: str) -> bool:
async with aiosqlite.connect(DB_PATH) as db:
async with db.execute(
"SELECT custom FROM personas WHERE persona_id = ?", (persona_id,)
) as cur:
row = await cur.fetchone()
if not row or not row[0]:
return False
await db.execute("DELETE FROM personas WHERE persona_id = ?", (persona_id,))
await db.commit()
if persona_id.startswith("card_"):
from services.character_card import delete_character
await delete_character(persona_id[5:])
return True
async def update_persona_appearance(persona_id: str, appearance_tags: str):
async with aiosqlite.connect(DB_PATH) as db:
await db.execute(
"UPDATE personas SET appearance_tags = ? WHERE persona_id = ?",
(appearance_tags, persona_id),
)
await db.commit()
async def update_persona_lora(persona_id: str, lora_name: str | None, lora_weight: float | None):
fields, vals = [], []
if lora_name is not None:
fields.append("lora_name = ?"); vals.append(lora_name)
if lora_weight is not None:
fields.append("lora_weight = ?"); vals.append(lora_weight)
if not fields:
return
async with aiosqlite.connect(DB_PATH) as db:
await db.execute(f"UPDATE personas SET {', '.join(fields)} WHERE persona_id = ?", (*vals, persona_id))
await db.commit()
async def update_persona_prompt(persona_id: str, prompt: str):
async with aiosqlite.connect(DB_PATH) as db:
await db.execute("UPDATE personas SET prompt = ? WHERE persona_id = ?", (prompt, persona_id))
await db.commit()