Files
lucidia-core/duet/logger.py
Alexa Louise 6afdb4b148 Initial extraction from blackroad-prism-console
Lucidia Core - AI reasoning engines for specialized domains:
- Physicist (867 lines) - energy modeling, force calculations
- Mathematician (760 lines) - symbolic computation, proofs
- Geologist (654 lines) - terrain modeling, stratigraphy
- Engineer (599 lines) - structural analysis, optimization
- Painter (583 lines) - visual generation, graphics
- Chemist (569 lines) - molecular analysis, reactions
- Analyst (505 lines) - pattern recognition, insights
- Plus: architect, researcher, mediator, speaker, poet, navigator

Features:
- FastAPI wrapper with REST endpoints for each agent
- CLI with `lucidia list`, `lucidia run`, `lucidia api`
- Codex YAML configurations for agent personalities
- Quantum engine extensions

12,512 lines of Python across 91 files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 08:00:53 -06:00

77 lines
2.1 KiB
Python

"""JSONL logger for reasoning duet sessions."""
from __future__ import annotations
import json
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Iterable, Optional
from .arbiter import ArbiterDecision
from .generator import Proposal
from .validator import ValidationResult
@dataclass
class TaskDescriptor:
id: str
goal: str
constraints: Iterable[str]
def to_payload(self) -> Dict[str, Any]:
return {
"id": self.id,
"goal": self.goal,
"constraints": list(self.constraints),
}
class DuetLogger:
"""Append-only JSONL logger for duet sessions."""
def __init__(self, log_dir: Path) -> None:
self.log_dir = log_dir
self.log_dir.mkdir(parents=True, exist_ok=True)
def _session_path(self, session_id: str) -> Path:
date_prefix = datetime.utcnow().strftime("%Y-%m-%d")
return self.log_dir / f"{date_prefix}.jsonl"
def append_round(
self,
*,
session_id: str,
task: TaskDescriptor,
round_index: int,
generator_model: str,
proposal: Proposal,
validation: ValidationResult,
arbiter: ArbiterDecision,
final_status: Optional[str] = None,
next_actions: Optional[Iterable[str]] = None,
) -> None:
payload: Dict[str, Any] = {
"session_id": session_id,
"task": task.to_payload(),
"round": round_index,
"generator": {
"model": generator_model,
"proposal": proposal.to_payload(),
},
"validator": validation.to_payload(),
"arbiter": arbiter.to_payload(),
}
if final_status or next_actions:
payload["final"] = {
"status": final_status or "pending",
"next_actions": list(next_actions or []),
}
path = self._session_path(session_id)
with path.open("a", encoding="utf-8") as handle:
handle.write(json.dumps(payload, ensure_ascii=False) + "\n")
__all__ = ["DuetLogger", "TaskDescriptor"]