annotate mrjunejune/inference/public_knowledge.py @ 273:e02e2036ef84 default tip

add Layer 2 JRPG component system Add reusable content and window modals, an isolated component sandbox, shared cyberpunk scroll areas, production-safe cache freshness, and server-rendered JRPG panel state. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Sat, 08 Aug 2026 02:08:08 -0700
parents 056790c4fb0d
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
265
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
1 """
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
2 Stdlib-only deterministic loader and compiler for the assistant knowledge base.
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
3
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
4 Loads prompt files from mrjunejune/assistant/ and compiles them with verified
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
5 public facts into a hashed, deterministic system prompt. Fails closed on any
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
6 malformed input.
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
7
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
8 Public API:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
9 compile_prompt(profile, assistant_dir=None) -> dict
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
10 Returns {"content": str, "version": int, "hash": str}.
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
11 Raises ValueError for unknown profile or any validation failure.
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
12 """
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
13
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
14 import hashlib
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
15 import json
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
16 import pathlib
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
17 import re
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
18 from typing import Optional
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
19 from urllib.parse import urlsplit
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
20
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
21 # ---------------------------------------------------------------------------
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
22 # Constants
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
23 # ---------------------------------------------------------------------------
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
24
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
25 _KNOWN_PROFILES: frozenset = frozenset({"public_visitor", "invited_friend", "june_admin"})
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
26
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
27 _REQUIRED_TOP_KEYS: frozenset = frozenset({"version", "facts"})
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
28 _REQUIRED_FACT_KEYS: frozenset = frozenset(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
29 {"id", "topic", "text", "sourceLabel", "sourceUrl", "visibility", "status"}
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
30 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
31
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
32 _EXPECTED_VERSION: int = 1
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
33 _MAX_PROMPT_FILE_BYTES: int = 8192 # 8 KB per prompt file
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
34 _MAX_COMPILED_BYTES: int = 16384 # 16 KB total compiled ceiling
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
35
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
36 _MAX_ID_LEN: int = 64
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
37 _MAX_TOPIC_LEN: int = 64
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
38 _MAX_TEXT_LEN: int = 500
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
39 _MAX_SOURCE_LABEL_LEN: int = 128
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
40
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
41 _SLUG_RE = re.compile(r"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
42
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
43 # Contact patterns: email and North-American-style phone numbers
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
44 _CONTACT_PATTERNS: list = [
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
45 re.compile(r"[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}"),
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
46 re.compile(r"(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}"),
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
47 ]
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
48 _SECRET_VALUE_PATTERNS: list = [
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
49 re.compile(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
50 r"\b(password|passwd|secret|token|api[_\-]?key|private[_\-]?key)"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
51 r"\s*[:=]\s*\S+",
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
52 re.IGNORECASE,
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
53 ),
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
54 re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"),
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
55 ]
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
56 _CONTROL_PATTERN = re.compile(r"[\x00-\x1f\x7f]")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
57 _PROMPT_CONTROL_PATTERN = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
58 _RESERVED_CORPUS_PATTERNS: tuple[str, ...] = (
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
59 "--- knowledge corpus ---",
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
60 "system:",
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
61 "assistant:",
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
62 "user:",
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
63 "<system",
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
64 "ignore previous instructions",
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
65 "ignore prior instructions",
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
66 "follow these instructions",
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
67 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
68
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
69 # Credential / secret keyword patterns
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
70 _SECRET_PATTERNS: list = [
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
71 re.compile(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
72 r"\b(password|passwd|secret|token|api[_\-]?key|private[_\-]?key)\b",
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
73 re.IGNORECASE,
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
74 ),
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
75 ]
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
76
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
77 _FACTS_DELIMITER: str = "--- KNOWLEDGE CORPUS ---"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
78
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
79
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
80 # ---------------------------------------------------------------------------
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
81 # Internal helpers
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
82 # ---------------------------------------------------------------------------
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
83
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
84
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
85 def _default_assistant_dir() -> pathlib.Path:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
86 return pathlib.Path(__file__).parent.parent / "assistant"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
87
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
88
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
89 def _validate_source_url(url: str, fact_id: str) -> None:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
90 if not isinstance(url, str) or not url:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
91 raise ValueError(f"Fact '{fact_id}': sourceUrl is empty")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
92 if _CONTROL_PATTERN.search(url) or "\\" in url:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
93 raise ValueError(f"Fact '{fact_id}': sourceUrl contains unsafe characters")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
94 if url.startswith("/"):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
95 if url.startswith("//") or any(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
96 segment in (".", "..") for segment in url.split("/")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
97 ):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
98 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
99 f"Fact '{fact_id}': sourceUrl must be a safe local path"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
100 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
101 return
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
102 parsed = urlsplit(url)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
103 if (
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
104 parsed.scheme != "https"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
105 or not parsed.hostname
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
106 or parsed.username is not None
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
107 or parsed.password is not None
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
108 ):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
109 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
110 f"Fact '{fact_id}': sourceUrl must be HTTPS or a safe local path"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
111 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
112 try:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
113 parsed.port
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
114 except ValueError as exc:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
115 raise ValueError(f"Fact '{fact_id}': sourceUrl has an invalid port") from exc
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
116
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
117
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
118 def _check_no_contact_or_secret(text: str, fact_id: str, field: str) -> None:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
119 for pat in _CONTACT_PATTERNS:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
120 if pat.search(text):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
121 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
122 f"Fact '{fact_id}': {field} contains contact data (email or phone number)"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
123 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
124 for pat in _SECRET_PATTERNS:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
125 if pat.search(text):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
126 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
127 f"Fact '{fact_id}': {field} contains a secret or credential pattern"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
128 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
129
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
130 def _validate_corpus_text(text: str, fact_id: str, field: str) -> None:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
131 if _CONTROL_PATTERN.search(text):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
132 raise ValueError(f"Fact '{fact_id}': {field} contains control characters")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
133 lowered = text.lower()
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
134 for reserved in _RESERVED_CORPUS_PATTERNS:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
135 if reserved in lowered:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
136 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
137 f"Fact '{fact_id}': {field} contains reserved prompt instructions"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
138 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
139 _check_no_contact_or_secret(text, fact_id, field)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
140
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
141
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
142 def _load_and_validate_facts(assistant_dir: pathlib.Path) -> list:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
143 facts_path = assistant_dir / "knowledge" / "public_facts.json"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
144 try:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
145 raw = facts_path.read_text(encoding="utf-8")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
146 except OSError as exc:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
147 raise ValueError(f"Cannot read facts file: {exc}") from exc
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
148
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
149 try:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
150 data = json.loads(raw)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
151 except json.JSONDecodeError as exc:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
152 raise ValueError(f"Facts file is not valid JSON: {exc}") from exc
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
153
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
154 if not isinstance(data, dict):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
155 raise ValueError("Facts file top level must be a JSON object")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
156
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
157 extra_keys = set(data.keys()) - _REQUIRED_TOP_KEYS
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
158 missing_keys = _REQUIRED_TOP_KEYS - set(data.keys())
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
159 if extra_keys or missing_keys:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
160 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
161 f"Facts file top-level keys must be exactly {sorted(_REQUIRED_TOP_KEYS)}; "
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
162 f"extra={sorted(extra_keys)}, missing={sorted(missing_keys)}"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
163 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
164
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
165 if (
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
166 not isinstance(data["version"], int)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
167 or isinstance(data["version"], bool)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
168 or data["version"] != _EXPECTED_VERSION
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
169 ):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
170 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
171 f"Facts file version must be {_EXPECTED_VERSION}; got {data['version']!r}"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
172 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
173
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
174 raw_facts = data["facts"]
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
175 if not isinstance(raw_facts, list) or len(raw_facts) == 0:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
176 raise ValueError("Facts file 'facts' must be a non-empty list")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
177
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
178 seen_ids: set = set()
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
179 validated: list = []
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
180
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
181 for i, fact in enumerate(raw_facts):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
182 if not isinstance(fact, dict):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
183 raise ValueError(f"Fact at index {i} must be a JSON object")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
184
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
185 extra = set(fact.keys()) - _REQUIRED_FACT_KEYS
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
186 missing = _REQUIRED_FACT_KEYS - set(fact.keys())
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
187 if extra or missing:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
188 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
189 f"Fact at index {i}: keys must be exactly "
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
190 f"{sorted(_REQUIRED_FACT_KEYS)}; "
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
191 f"extra={sorted(extra)}, missing={sorted(missing)}"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
192 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
193
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
194 fid = fact["id"]
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
195 if not isinstance(fid, str) or not fid:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
196 raise ValueError(f"Fact at index {i}: 'id' must be a non-empty string")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
197 if len(fid) > _MAX_ID_LEN:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
198 raise ValueError(f"Fact '{fid}': id exceeds {_MAX_ID_LEN} characters")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
199 if not _SLUG_RE.match(fid):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
200 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
201 f"Fact '{fid}': id must be lowercase alphanumeric with hyphens"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
202 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
203 if fid in seen_ids:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
204 raise ValueError(f"Duplicate fact id: {fid!r}")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
205 seen_ids.add(fid)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
206
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
207 topic = fact["topic"]
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
208 if not isinstance(topic, str) or not topic:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
209 raise ValueError(f"Fact '{fid}': 'topic' must be a non-empty string")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
210 if len(topic) > _MAX_TOPIC_LEN:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
211 raise ValueError(f"Fact '{fid}': topic exceeds {_MAX_TOPIC_LEN} characters")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
212 if not _SLUG_RE.match(topic):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
213 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
214 f"Fact '{fid}': topic must be lowercase alphanumeric with hyphens"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
215 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
216
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
217 text = fact["text"]
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
218 if not isinstance(text, str) or len(text) < 10:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
219 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
220 f"Fact '{fid}': 'text' must be a string of at least 10 characters"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
221 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
222 if len(text) > _MAX_TEXT_LEN:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
223 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
224 f"Fact '{fid}': text exceeds {_MAX_TEXT_LEN} characters"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
225 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
226 _validate_corpus_text(text, fid, "text")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
227
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
228 source_label = fact["sourceLabel"]
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
229 if not isinstance(source_label, str) or not source_label:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
230 raise ValueError(f"Fact '{fid}': 'sourceLabel' must be a non-empty string")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
231 if len(source_label) > _MAX_SOURCE_LABEL_LEN:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
232 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
233 f"Fact '{fid}': sourceLabel exceeds {_MAX_SOURCE_LABEL_LEN} characters"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
234 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
235 _validate_corpus_text(source_label, fid, "sourceLabel")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
236
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
237 _validate_source_url(fact["sourceUrl"], fid)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
238
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
239 if not isinstance(fact["visibility"], str) or fact["visibility"] != "public":
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
240 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
241 f"Fact '{fid}': visibility must be 'public'; got {fact['visibility']!r}"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
242 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
243 if not isinstance(fact["status"], str) or fact["status"] != "verified":
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
244 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
245 f"Fact '{fid}': status must be 'verified'; got {fact['status']!r}"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
246 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
247
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
248 validated.append(fact)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
249
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
250 validated.sort(key=lambda f: f["id"])
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
251 return validated
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
252
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
253
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
254 def _load_prompt_file(path: pathlib.Path) -> str:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
255 try:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
256 raw_bytes = path.read_bytes()
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
257 except OSError as exc:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
258 raise ValueError(f"Cannot read prompt file {path.name}: {exc}") from exc
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
259 if len(raw_bytes) > _MAX_PROMPT_FILE_BYTES:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
260 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
261 f"Prompt file {path.name} exceeds {_MAX_PROMPT_FILE_BYTES} bytes "
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
262 f"(got {len(raw_bytes)})"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
263 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
264 try:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
265 content = raw_bytes.decode("utf-8")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
266 except UnicodeDecodeError as exc:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
267 raise ValueError(f"Prompt file {path.name} is not valid UTF-8") from exc
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
268 if _PROMPT_CONTROL_PATTERN.search(content):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
269 raise ValueError(f"Prompt file {path.name} contains control characters")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
270 for pattern in _CONTACT_PATTERNS + _SECRET_VALUE_PATTERNS:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
271 if pattern.search(content):
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
272 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
273 f"Prompt file {path.name} contains contact data or a secret value"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
274 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
275 return content
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
276
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
277
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
278 def _format_facts(facts: list) -> str:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
279 lines: list = []
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
280 for fact in facts:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
281 lines.append(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
282 json.dumps(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
283 {
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
284 "fact": fact["text"],
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
285 "id": fact["id"],
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
286 "sourceLabel": fact["sourceLabel"],
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
287 "sourceUrl": fact["sourceUrl"],
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
288 "topic": fact["topic"],
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
289 },
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
290 ensure_ascii=False,
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
291 separators=(",", ":"),
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
292 sort_keys=True,
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
293 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
294 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
295 return "\n".join(lines)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
296
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
297
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
298 # ---------------------------------------------------------------------------
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
299 # Public API
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
300 # ---------------------------------------------------------------------------
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
301
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
302
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
303 def compile_prompt(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
304 profile: str,
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
305 assistant_dir: Optional[pathlib.Path] = None,
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
306 ) -> dict:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
307 """
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
308 Compile a deterministic system prompt for *profile*.
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
309
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
310 Parameters
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
311 ----------
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
312 profile:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
313 One of ``"public_visitor"``, ``"invited_friend"``, or ``"june_admin"``.
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
314 assistant_dir:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
315 Path to the ``mrjunejune/assistant/`` directory. Defaults to the
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
316 sibling of the ``inference/`` package at runtime.
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
317
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
318 Returns
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
319 -------
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
320 dict with keys:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
321 - ``"content"`` (str): compiled prompt text
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
322 - ``"version"`` (int): schema version (always 1)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
323 - ``"hash"`` (str): lowercase hex SHA-256 of ``content`` encoded UTF-8
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
324
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
325 Raises
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
326 ------
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
327 ValueError
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
328 On unknown profile, malformed files, validation failures, or size
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
329 limit exceeded.
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
330 """
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
331 if profile not in _KNOWN_PROFILES:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
332 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
333 f"Unknown profile {profile!r}; "
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
334 f"known profiles: {sorted(_KNOWN_PROFILES)}"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
335 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
336
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
337 base = pathlib.Path(assistant_dir) if assistant_dir is not None else _default_assistant_dir()
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
338
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
339 common = _load_prompt_file(base / "common.md")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
340 profile_section = _load_prompt_file(base / f"{profile}.md")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
341 facts = _load_and_validate_facts(base)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
342 facts_block = _format_facts(facts)
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
343
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
344 content = (
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
345 common.rstrip()
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
346 + "\n\n"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
347 + profile_section.rstrip()
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
348 + "\n\n"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
349 + _FACTS_DELIMITER
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
350 + "\n\n"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
351 + facts_block
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
352 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
353
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
354 encoded = content.encode("utf-8")
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
355 if len(encoded) > _MAX_COMPILED_BYTES:
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
356 raise ValueError(
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
357 f"Compiled prompt exceeds {_MAX_COMPILED_BYTES} bytes "
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
358 f"(got {len(encoded)})"
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
359 )
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
360
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
361 return {
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
362 "content": content,
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
363 "version": _EXPECTED_VERSION,
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
364 "hash": hashlib.sha256(encoded).hexdigest(),
056790c4fb0d add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
365 }