Added RPG
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from services.llm import send_message_with_model
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
NARRATOR_MODEL = os.getenv("RPG_NARRATOR_MODEL", "").strip() or "deepseek/deepseek-chat-v3"
|
||||
|
||||
NARRATOR_PRE_SYSTEM = """You are the System/Narrator of an RPG chat.
|
||||
You do NOT roleplay as the character. You update the status quo and enforce mechanics.
|
||||
Return ONLY valid JSON (no markdown):
|
||||
{
|
||||
"directives": ["short imperative rules for the next character reply"],
|
||||
"resolution_text": "what actually happens as the result of the user's described action (1-2 sentences)",
|
||||
"status_quo_update": "optional short update about the world's state after applying outcome",
|
||||
"choices": [{"id":"a","label":"..."}, ...]
|
||||
}
|
||||
Rules:
|
||||
- directives must be actionable constraints (tone, consequences, new obstacles).
|
||||
- resolution_text must be consistent with roll/outcome and should not contradict established facts.
|
||||
- If outcome is failure/critical failure, impose meaningful complication.
|
||||
- If outcome is success/critical success, allow progress and reward.
|
||||
- Keep it short."""
|
||||
|
||||
NARRATOR_POST_SYSTEM = """You are the System/Narrator of an RPG chat.
|
||||
After the character replied, update persistent status quo and facts.
|
||||
Return ONLY valid JSON (no markdown):
|
||||
{
|
||||
"status_quo_update": "what changed in the world/state (short)",
|
||||
"facts": ["durable facts only"],
|
||||
"choices": [{"id":"a","label":"..."}, ...]
|
||||
}
|
||||
Rules:
|
||||
- status_quo_update should be 1-3 sentences.
|
||||
- facts must be stable and non-duplicative.
|
||||
- choices optional (0-4)."""
|
||||
|
||||
|
||||
async def narrator_pre(
|
||||
persona_name: str,
|
||||
context: str,
|
||||
global_plot: str,
|
||||
facts_block: str,
|
||||
roll: int,
|
||||
outcome: str,
|
||||
) -> dict:
|
||||
user = (
|
||||
f"Persona: {persona_name}\n"
|
||||
f"Roll d20={roll}\nOutcome={outcome}\n\n"
|
||||
f"Global plot:\n{global_plot}\n\n"
|
||||
f"Facts:\n{facts_block}\n\n"
|
||||
f"Recent context:\n{context}\n"
|
||||
)
|
||||
raw = await send_message_with_model(
|
||||
[{"role": "system", "content": NARRATOR_PRE_SYSTEM}, {"role": "user", "content": user}],
|
||||
NARRATOR_MODEL,
|
||||
)
|
||||
cleaned = raw.strip()
|
||||
if cleaned.startswith("```"):
|
||||
cleaned = cleaned.split("\n", 1)[1] if "\n" in cleaned else cleaned
|
||||
if cleaned.endswith("```"):
|
||||
cleaned = cleaned.rsplit("```", 1)[0]
|
||||
cleaned = cleaned.strip()
|
||||
try:
|
||||
data = json.loads(cleaned)
|
||||
if isinstance(data, dict):
|
||||
return data
|
||||
except Exception:
|
||||
logger.warning("Narrator-pre JSON parse failed. Raw=%.500s", raw)
|
||||
return {"directives": [], "status_quo_update": "", "choices": []}
|
||||
|
||||
|
||||
async def narrator_post(
|
||||
persona_name: str,
|
||||
context: str,
|
||||
global_plot: str,
|
||||
facts_block: str,
|
||||
) -> dict:
|
||||
user = (
|
||||
f"Persona: {persona_name}\n\n"
|
||||
f"Global plot:\n{global_plot}\n\n"
|
||||
f"Facts:\n{facts_block}\n\n"
|
||||
f"Recent context:\n{context}\n"
|
||||
)
|
||||
raw = await send_message_with_model(
|
||||
[{"role": "system", "content": NARRATOR_POST_SYSTEM}, {"role": "user", "content": user}],
|
||||
NARRATOR_MODEL,
|
||||
)
|
||||
cleaned = raw.strip()
|
||||
if cleaned.startswith("```"):
|
||||
cleaned = cleaned.split("\n", 1)[1] if "\n" in cleaned else cleaned
|
||||
if cleaned.endswith("```"):
|
||||
cleaned = cleaned.rsplit("```", 1)[0]
|
||||
cleaned = cleaned.strip()
|
||||
try:
|
||||
data = json.loads(cleaned)
|
||||
if isinstance(data, dict):
|
||||
return data
|
||||
except Exception:
|
||||
logger.warning("Narrator-post JSON parse failed. Raw=%.500s", raw)
|
||||
return {"status_quo_update": "", "facts": [], "choices": []}
|
||||
|
||||
Reference in New Issue
Block a user