Each role can now disable the current date/time header injected into the system prompt. Default is true (all existing roles unchanged). Useful for pure processing roles (summarizer, classifier, translator) where temporal context is irrelevant or could cause unexpected model behavior. Changes: - model_registry: set_role_config/get_role_config gain inject_datetime field - context_loader: load_context gains inject_datetime param (default True) - orchestrator router: passes inject_datetime from role_cfg to load_context - local_llm router: reads inject_datetime from POST body, passes to registry; role_config_data_js includes the field - local_llm.html: checkbox in role config panel; populate on open, save on submit Session logs still timestamp every turn (HH:MM header in YYYY-MM-DD.md files) regardless of this setting — the toggle only affects the system prompt header. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
130 lines
5.3 KiB
Python
130 lines
5.3 KiB
Python
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from persona import persona_path
|
|
from tools.reminders import load_due_reminders
|
|
|
|
_STATIC_DIR = Path(__file__).parent / "static"
|
|
|
|
|
|
# Core identity files — always loaded regardless of tier
|
|
_CORE = ["SOUL.md", "IDENTITY.md"]
|
|
|
|
# Lines of USER.md to include at Tier 1 (identity + what he cares about)
|
|
_TIER_1_USER_LINES = 30
|
|
|
|
|
|
def load_context(
|
|
tier: int = 2,
|
|
include_long: bool = True,
|
|
include_mid: bool = True,
|
|
include_short: bool = True,
|
|
role_append: str = "",
|
|
inject_datetime: bool = True,
|
|
) -> str:
|
|
"""
|
|
Build the system-prompt context block for a given tier and memory toggles.
|
|
|
|
Load order (long → mid → short) keeps the most recent memory closest
|
|
to the conversation turn, which improves LLM recall.
|
|
|
|
Tier 1 — SOUL + IDENTITY + USER summary (~1,500 tokens)
|
|
Tier 2 — + USER full + PROTOCOLS + memory (~5,000 tokens)
|
|
Tier 3 — + last 2 raw session logs (~15,000 tokens)
|
|
Tier 4 — + last 7 raw session logs (~50,000 tokens)
|
|
|
|
role_append — optional text injected last (closest to the turn),
|
|
sourced from the active role's system_append config.
|
|
"""
|
|
inara_dir = persona_path()
|
|
parts = []
|
|
|
|
# ── 0. Current date and time (per-role toggle — injected first so it's prominent) ──
|
|
if inject_datetime:
|
|
now = datetime.now().astimezone()
|
|
parts.append(f"--- System ---\nCurrent date and time: {now.strftime('%A, %Y-%m-%d at %I:%M %p %Z')}")
|
|
|
|
# ── 1. Core identity (always) ──────────────────────────────────
|
|
for filename in _CORE:
|
|
path = inara_dir / filename
|
|
if path.exists():
|
|
parts.append(f"--- {filename} ---\n{path.read_text()}")
|
|
|
|
# ── 2. USER.md ─────────────────────────────────────────────────
|
|
user_path = inara_dir / "USER.md"
|
|
if user_path.exists():
|
|
if tier == 1:
|
|
lines = user_path.read_text().splitlines()[:_TIER_1_USER_LINES]
|
|
content = "\n".join(lines)
|
|
else:
|
|
content = user_path.read_text()
|
|
parts.append(f"--- USER.md ---\n{content}")
|
|
|
|
if tier < 2:
|
|
return "\n\n".join(parts)
|
|
|
|
# ── 3. Protocols + Help reference (tier 2+) ───────────────────
|
|
proto_path = inara_dir / "PROTOCOLS.md"
|
|
if proto_path.exists():
|
|
parts.append(f"--- PROTOCOLS.md ---\n{proto_path.read_text()}")
|
|
|
|
ops_path = inara_dir / "OPERATIONS.md"
|
|
if ops_path.exists():
|
|
parts.append(f"--- OPERATIONS.md ---\n{ops_path.read_text()}")
|
|
|
|
# Global tool reference (same for all personas)
|
|
tools_path = _STATIC_DIR / "TOOLS.md"
|
|
if tools_path.exists():
|
|
parts.append(f"--- TOOLS.md ---\n{tools_path.read_text()}")
|
|
|
|
# Persona-specific help additions (optional)
|
|
help_path = inara_dir / "HELP.md"
|
|
if help_path.exists() and help_path.stat().st_size > 10:
|
|
parts.append(f"--- HELP.md ---\n{help_path.read_text()}")
|
|
|
|
# ── 4. Pending reminders (tier 2+) ────────────────────────────
|
|
# Only due and undated reminders are surfaced — future-dated ones
|
|
# are stored in REMINDERS.md but suppressed until their date arrives.
|
|
content = load_due_reminders()
|
|
if content:
|
|
parts.append(f"--- REMINDERS.md ---\n{content}")
|
|
|
|
# ── 5. Tiered memory — long → mid → short ─────────────────────
|
|
# Short is last so it sits closest to the conversation turn.
|
|
if include_long:
|
|
# Fall back to legacy MEMORY.md during/after migration
|
|
long_path = inara_dir / "MEMORY_LONG.md"
|
|
if not long_path.exists():
|
|
long_path = inara_dir / "MEMORY.md"
|
|
if long_path.exists():
|
|
parts.append(f"--- {long_path.name} ---\n{long_path.read_text()}")
|
|
|
|
if include_mid:
|
|
mid_path = inara_dir / "MEMORY_MID.md"
|
|
if mid_path.exists() and mid_path.stat().st_size > 100:
|
|
content = mid_path.read_text()
|
|
if "Not yet populated" not in content:
|
|
parts.append(f"--- MEMORY_MID.md ---\n{content}")
|
|
|
|
if include_short:
|
|
short_path = inara_dir / "MEMORY_SHORT.md"
|
|
if short_path.exists() and short_path.stat().st_size > 100:
|
|
content = short_path.read_text()
|
|
if "Not yet populated" not in content:
|
|
parts.append(f"--- MEMORY_SHORT.md ---\n{content}")
|
|
|
|
# ── 6. Raw session logs (tier 3+) ──────────────────────────────
|
|
if tier >= 3:
|
|
sessions_dir = inara_dir / "sessions"
|
|
if sessions_dir.exists():
|
|
count = 2 if tier == 3 else 7
|
|
session_files = sorted(sessions_dir.glob("*.md"), reverse=True)[:count]
|
|
for sf in session_files:
|
|
parts.append(f"--- Session: {sf.name} ---\n{sf.read_text()}")
|
|
|
|
# ── 7. Role-specific instructions (always last — closest to the turn) ──
|
|
if role_append and role_append.strip():
|
|
parts.append(f"--- Role Context ---\n{role_append.strip()}")
|
|
|
|
return "\n\n".join(parts)
|