Spaces:
Sleeping
Sleeping
Download scripts/lib/chat/session_core.py from defenestrate/asa: direct link, hf CLI and curl.
- Browser
- Download file 10.6 kB
-
https://huggingface.co/spaces/defenestrate/asa/resolve/main/scripts/lib/chat/session_core.py
- Command line
-
hf download hf://spaces/defenestrate/asa/scripts/lib/chat/session_core.py
-
curl -L -o session_core.py https://huggingface.co/spaces/defenestrate/asa/resolve/main/scripts/lib/chat/session_core.py
10.6 kB
| """Shared core curriculum: a byte-stable, nightly-built prompt block of | |
| the highest-savings docs, sized dynamically by a savings floor. | |
| The block sits in Anthropic's `system` field with extended-cache markers | |
| so its first read in any 1-hour window is the only write. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import logging | |
| import os | |
| import statistics | |
| from collections import defaultdict | |
| from dataclasses import dataclass, field | |
| from pathlib import Path | |
| from scripts.lib.chat.pricing import ( | |
| PRICE_SONNET_IN, | |
| PRICE_SONNET_CACHE_READ, | |
| PRICE_SONNET_CACHE_WRITE, | |
| PRICE_SONNET_CACHE_WRITE_1H, | |
| ) | |
| from scripts.lib.chat_eval.turn_log import iter_turns | |
| from scripts.lib.runtime_config import DERIVED_STUDENT_CHAT_DIR | |
| logger = logging.getLogger(__name__) | |
| _DERIVED_DIR = DERIVED_STUDENT_CHAT_DIR | |
| _DEFAULT_JSON_PATH = _DERIVED_DIR / "session_core.json" | |
| _MTIME_CACHE_SECONDS = 60.0 | |
| class SessionCore: | |
| doc_ids: list[str] | |
| frozen_prompt_text: str | |
| build_timestamp: str | |
| concurrent_sessions_estimate: int | |
| projected_savings_usd: float | |
| floor_usd: float | |
| # path -> (loaded_at_monotonic, file_mtime, SessionCore) | |
| _CACHE: dict[Path, tuple[float, float, SessionCore]] = {} | |
| def load_session_core(path: Path = _DEFAULT_JSON_PATH) -> SessionCore | None: | |
| """Load core from disk. Returns None if the file is missing. | |
| Caches in-process for 60s OR until the json file's mtime changes, | |
| whichever comes first. The 60s ceiling means orchestrators pick up | |
| a nightly rebuild within a minute without explicit invalidation. | |
| """ | |
| import time | |
| if not Path(path).is_file(): | |
| return None | |
| stat = Path(path).stat() | |
| cached = _CACHE.get(Path(path)) | |
| now = time.monotonic() | |
| if cached is not None: | |
| loaded_at, cached_mtime, core = cached | |
| if cached_mtime == stat.st_mtime and (now - loaded_at) < _MTIME_CACHE_SECONDS: | |
| return core | |
| try: | |
| data = json.loads(Path(path).read_text(encoding="utf-8")) | |
| except (json.JSONDecodeError, OSError) as e: | |
| logger.warning("session_core.load: failed to read %s (%s)", path, e) | |
| return None | |
| prompt_path = Path(path).parent / data.get("prompt_path", "session_core_prompt.txt") | |
| try: | |
| prompt_text = prompt_path.read_text(encoding="utf-8") | |
| except OSError as e: | |
| logger.warning("session_core.load: failed to read prompt %s (%s)", prompt_path, e) | |
| return None | |
| core = SessionCore( | |
| doc_ids=list(data.get("doc_ids", [])), | |
| frozen_prompt_text=prompt_text, | |
| build_timestamp=data.get("build_timestamp", ""), | |
| concurrent_sessions_estimate=int(data.get("concurrent_sessions_estimate", 1)), | |
| projected_savings_usd=float(data.get("projected_savings_usd", 0.0)), | |
| floor_usd=float(data.get("floor_usd", 0.01)), | |
| ) | |
| _CACHE[Path(path)] = (now, stat.st_mtime, core) | |
| return core | |
| def _doc_savings( | |
| *, body_tokens: int, retrieval_count: int, | |
| concurrent_sessions: int, ttl_hours: float, | |
| write_rate: float, | |
| ) -> float: | |
| """Projected savings (USD) of pinning one doc into the cached prefix. | |
| Each TTL window: write the doc once, read it on every retrieval that | |
| hits during the window. Aggregate retrieval rate scales with | |
| concurrent_sessions because every active session may read the same | |
| cached prefix. | |
| expected_reads_per_TTL = retrieval_rate Γ concurrent_sessions Γ ttl_hours | |
| savings_per_TTL = body_tokens Γ ( | |
| expected_reads_per_TTL Γ (in_rate β read_rate) | |
| β (write_rate β in_rate) | |
| ) | |
| `retrieval_rate` is the per-session-hour retrieval count. For a 7-day | |
| log we approximate it as retrieval_count / (7 days Γ 24 hours). | |
| """ | |
| in_rate = PRICE_SONNET_IN | |
| read_rate = PRICE_SONNET_CACHE_READ | |
| retrieval_rate_per_hour = retrieval_count / (7.0 * 24.0) | |
| expected_reads_per_TTL = ( | |
| retrieval_rate_per_hour * concurrent_sessions * ttl_hours | |
| ) | |
| return body_tokens * ( | |
| expected_reads_per_TTL * (in_rate - read_rate) | |
| - (write_rate - in_rate) | |
| ) | |
| def _render_doc_block(rec: dict) -> str: | |
| """Render one doc as a deterministic XML envelope. | |
| Identical doc_id + kind + topic + concept_profile β identical bytes. | |
| Kept simpler than prompt_builder._format_doc_envelope on purpose: | |
| the core is *static reference material*, not session-specific | |
| retrieval, so we don't inline cited turns or other dynamic content. | |
| """ | |
| doc_id = rec.get("doc_id", "") | |
| kind = rec.get("kind", "") | |
| topic = rec.get("topic", "") | |
| body = rec.get("concept_profile", "") or "" | |
| return ( | |
| f'<doc id="{doc_id}" kind="{kind}" topic="{topic}">\n' | |
| f" <body>{body}</body>\n" | |
| f"</doc>" | |
| ) | |
| def build_session_core( | |
| *, | |
| log_path: Path, | |
| output_dir: Path = _DERIVED_DIR, | |
| concurrent_sessions: int, | |
| savings_floor_usd: float = 0.01, | |
| ttl_hours: float = 1.0, | |
| catalog_records_by_id: dict, | |
| since: str | None = None, | |
| ) -> SessionCore: | |
| """Build the shared core from a turn log + catalog snapshot. | |
| Walks the log to count retrievals + capture body_chars per doc_id, | |
| projects savings under the concurrency model, then includes every | |
| doc whose projection clears `savings_floor_usd`. Output ordering is | |
| by savings descending, with doc_id tiebreaker for determinism. | |
| """ | |
| write_rate = ( | |
| PRICE_SONNET_CACHE_WRITE_1H if ttl_hours >= 1.0 | |
| else PRICE_SONNET_CACHE_WRITE | |
| ) | |
| by_doc: dict[str, dict] = {} | |
| for turn in iter_turns(log_path, since=since): | |
| for r in turn.get("retrieved", []): | |
| doc_id = r.get("doc_id", "") | |
| if not doc_id: | |
| continue | |
| entry = by_doc.setdefault(doc_id, { | |
| "retrieval_count": 0, "body_chars": 0, | |
| }) | |
| entry["retrieval_count"] += 1 | |
| if not entry["body_chars"]: | |
| entry["body_chars"] = r.get("body_chars", 0) or 0 | |
| candidates: list[tuple[float, str, dict]] = [] | |
| total_savings = 0.0 | |
| for doc_id, info in by_doc.items(): | |
| rec = catalog_records_by_id.get(doc_id) | |
| if rec is None: | |
| continue # stale catalog: doc was removed since log was written | |
| body_chars = info["body_chars"] or len(rec.get("concept_profile", "") or "") | |
| if body_chars <= 0 or info["retrieval_count"] < 2: | |
| continue | |
| body_tokens = body_chars // 4 | |
| savings = _doc_savings( | |
| body_tokens=body_tokens, | |
| retrieval_count=info["retrieval_count"], | |
| concurrent_sessions=concurrent_sessions, | |
| ttl_hours=ttl_hours, | |
| write_rate=write_rate, | |
| ) | |
| if savings <= 0 or savings < savings_floor_usd: | |
| continue | |
| candidates.append((savings, doc_id, rec)) | |
| total_savings += savings | |
| # Deterministic order: highest savings first, doc_id ascending tiebreaker. | |
| candidates.sort(key=lambda x: (-x[0], x[1])) | |
| # Always-pin: course_meta records (the syllabus, today). These bypass | |
| # the savings filter β pinning the syllabus is a policy choice, not an | |
| # emergent metric. Without this, the syllabus could fall out of the core | |
| # whenever its retrieval count drops (which is exactly what happens once | |
| # it's pinned, since students no longer need to ask for it explicitly). | |
| existing_ids = {doc_id for _, doc_id, _ in candidates} | |
| must_pin: list[tuple[float, str, dict]] = [] | |
| for doc_id, rec in catalog_records_by_id.items(): | |
| if rec.get("kind") != "course_meta": | |
| continue | |
| if doc_id in existing_ids: | |
| continue | |
| must_pin.append((0.0, doc_id, rec)) | |
| must_pin.sort(key=lambda x: x[1]) | |
| candidates = must_pin + candidates | |
| doc_ids = [doc_id for _, doc_id, _ in candidates] | |
| rendered_blocks = [_render_doc_block(rec) for _, _, rec in candidates] | |
| prompt_text = "\n\n".join(rendered_blocks) | |
| output_dir = Path(output_dir) | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| prompt_path = output_dir / "session_core_prompt.txt" | |
| prompt_staging = prompt_path.with_name(prompt_path.name + ".tmp") | |
| prompt_staging.write_text(prompt_text, encoding="utf-8") | |
| from datetime import datetime, timezone | |
| metadata = { | |
| "doc_ids": doc_ids, | |
| "build_timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), | |
| "concurrent_sessions_estimate": concurrent_sessions, | |
| "projected_savings_usd": round(total_savings, 4), | |
| "floor_usd": savings_floor_usd, | |
| "prompt_path": prompt_path.name, | |
| } | |
| json_path = output_dir / "session_core.json" | |
| json_staging = json_path.with_name(json_path.name + ".tmp") | |
| json_staging.write_text(json.dumps(metadata, indent=2), encoding="utf-8") | |
| # Both staging files written; rename them as close together as possible. | |
| # If the process is killed between the two renames, the loader will read | |
| # the old json (still pointing at the old prompt path) β the new prompt | |
| # is left at session_core_prompt.txt, but the old json keeps consumers on | |
| # the previous build until next nightly run. | |
| os.replace(prompt_staging, prompt_path) | |
| os.replace(json_staging, json_path) | |
| return SessionCore( | |
| doc_ids=doc_ids, | |
| frozen_prompt_text=prompt_text, | |
| build_timestamp=metadata["build_timestamp"], | |
| concurrent_sessions_estimate=concurrent_sessions, | |
| projected_savings_usd=metadata["projected_savings_usd"], | |
| floor_usd=savings_floor_usd, | |
| ) | |
| def estimate_concurrent_sessions( | |
| log_path: Path, | |
| *, | |
| since: str | None = None, | |
| ) -> int: | |
| """Estimate steady-state concurrent-session count via p50 of distinct | |
| sessions per active hour. | |
| 'Active hour' = any UTC hour with at least one logged turn. Hours | |
| with zero turns don't count (they would drag the median to 1 | |
| artificially during off-peak). | |
| Returns max(1, p50). Used by the builder when concurrent_sessions | |
| isn't explicitly passed. | |
| """ | |
| by_hour: dict[str, set[str]] = defaultdict(set) | |
| for turn in iter_turns(log_path, since=since): | |
| ts = turn.get("ts", "") | |
| sid = turn.get("session_id", "") | |
| if not ts or not sid: | |
| continue | |
| # ts format: 2026-05-01T10:05:00Z β hour key: 2026-05-01T10 | |
| hour_key = ts[:13] | |
| by_hour[hour_key].add(sid) | |
| if not by_hour: | |
| return 1 | |
| counts = sorted(len(s) for s in by_hour.values()) | |
| p50 = int(statistics.median(counts)) | |
| return max(1, p50) | |