Source code for sim_panel.panelists.io
from __future__ import annotations
import json
from pathlib import Path
from typing import Dict, Iterable, List, Optional
from .records import PersonaRecord
from sim_panel.io.jsonl import read_jsonl_dicts, write_jsonl_dicts
def read_jsonl(path: str | Path) -> List[Dict]:
p = Path(path)
if not p.exists():
return []
return read_jsonl_dicts(str(p))
def write_jsonl_atomic(path: str | Path, rows: Iterable[Dict]) -> None:
p = Path(path)
p.parent.mkdir(parents=True, exist_ok=True)
# Delegate to shared JSONL writer (atomic write with fsync) for robustness.
write_jsonl_dicts(str(p), rows)
[docs]
def load_persona_records(path: str | Path) -> List[PersonaRecord]:
return [PersonaRecord.from_dict(r) for r in read_jsonl(path)]
[docs]
def save_persona_records(path: str | Path, records: Iterable[PersonaRecord]) -> None:
# stable order for reproducibility (persona_id, then variant)
recs = sorted(records, key=lambda r: (r.persona_id, r.persona_text_variant))
write_jsonl_atomic(path, (r.to_dict() for r in recs))
[docs]
def merge_persona_records(
base: List[PersonaRecord],
incoming: List[PersonaRecord],
*,
prefer_incoming_attributes: bool = True,
prefer_incoming_text: bool = True,
) -> List[PersonaRecord]:
"""
Merge by (persona_id, persona_text_variant). Rewrite-friendly.
- attributes: prefer incoming by default
- persona_text: prefer incoming by default
"""
index: Dict[tuple[str, str], PersonaRecord] = {}
for r in base:
index[(r.persona_id, r.persona_text_variant)] = r
for r in incoming:
key = (r.persona_id, r.persona_text_variant)
if key not in index:
index[key] = r
continue
existing = index[key]
if prefer_incoming_attributes and r.attributes is not None:
existing.attributes = r.attributes
existing.spec_key = r.spec_key # may be None; recomputed on to_dict()
if prefer_incoming_text and r.persona_text is not None:
existing.persona_text = r.persona_text
existing.text_key = r.text_key
# Merge provenance shallowly (incoming wins)
if r.provenance:
existing.provenance = {**existing.provenance, **r.provenance}
# Ensure keys computed
out = list(index.values())
for r in out:
r.compute_keys()
return out