whassan's picture
Deploy Sensorflow Studio
578518d verified
Raw History Blame Contribute Delete
6.9 kB
"""Data model + JSON persistence for the Hill Climbing EM platform.
Everything persists as JSON documents under runs/hillclimb/ (one file per
collection). The store is intentionally simple: this is a single-user,
low-write-volume product surface, and file-backed JSON keeps the whole thing
inspectable and dependency-free.
"""
from __future__ import annotations
import json
import os
import threading
import uuid
from datetime import datetime, timezone
from enum import Enum
from pathlib import Path
from typing import Dict, List, Optional
from pydantic import BaseModel, Field
def now_iso() -> str:
return datetime.now(timezone.utc).isoformat()
def new_id(prefix: str) -> str:
return f"{prefix}-{uuid.uuid4().hex[:10]}"
# --------------------------------------------------------------------- enums
class Dimension(str, Enum):
KNOWLEDGE = "Knowledge"
TECHNICAL_REASONING = "Technical Reasoning"
LEADERSHIP = "Leadership"
EXECUTION = "Execution"
class ReadinessState(str, Enum):
NOT_STARTED = "NOT_STARTED"
LEARNING = "LEARNING"
PRACTICING = "PRACTICING"
NEEDS_REVIEW = "NEEDS_REVIEW"
COMPETENT = "COMPETENT"
STRONG = "STRONG"
INTERVIEW_READY = "INTERVIEW_READY"
class JourneyState(str, Enum):
NOT_STARTED = "NOT_STARTED"
DIAGNOSTIC = "DIAGNOSTIC"
LEARNING = "LEARNING"
PRACTICE = "PRACTICE"
ASSESSMENT = "ASSESSMENT"
REMEDIATION = "REMEDIATION"
REASSESS = "REASSESS"
# --------------------------------------------------------------------- models
class UserProfile(BaseModel):
user_id: str = "default"
name: str = "You"
target_role: str = "Engineering Manager, ML/Perception"
experience_years: float = 8.0
focus_note: str = ""
created_at: str = Field(default_factory=now_iso)
updated_at: str = Field(default_factory=now_iso)
class EvaluationResult(BaseModel):
"""Structured AI-evaluation contract. Free-form LLM text NEVER mutates
state directly — it must parse into this schema or the rule-based
evaluator's result is used instead."""
competency: str
score: int = Field(ge=1, le=5, description="1=Novice .. 5=Expert")
confidence: float = Field(ge=0.0, le=1.0)
evidence: List[str] = Field(default_factory=list, description="Quotes from the user's own statements")
strengths: List[str] = Field(default_factory=list)
weaknesses: List[str] = Field(default_factory=list)
missing_evidence: List[str] = Field(default_factory=list)
misconceptions: List[str] = Field(default_factory=list)
recommended_action: str = ""
follow_up_question: str = ""
evaluator: str = "rule_based" # rule_based | llm
class Evidence(BaseModel):
evidence_id: str = Field(default_factory=lambda: new_id("ev"))
user_id: str = "default"
competency_ids: List[str] = Field(default_factory=list)
artifact_type: str = "exercise_attempt" # exercise_attempt | star_story | design_submission | simulation_debrief | interview_transcript | diagnostic
source: str = ""
summary: str = ""
quotes: List[str] = Field(default_factory=list)
score: float = 0.0
confidence: float = 0.0
timestamp: str = Field(default_factory=now_iso)
payload: Dict = Field(default_factory=dict)
class Attempt(BaseModel):
attempt_id: str = Field(default_factory=lambda: new_id("att"))
user_id: str = "default"
exercise_id: str = ""
competency_id: str = ""
kind: str = "exercise" # exercise | diagnostic | interview | design | simulation
responses: Dict = Field(default_factory=dict)
evaluation: Optional[EvaluationResult] = None
timestamp: str = Field(default_factory=now_iso)
class CompetencyReadiness(BaseModel):
competency_id: str
knowledge_score: float = 0.0
application_score: float = 0.0
evidence_score: float = 0.0
readiness_state: ReadinessState = ReadinessState.NOT_STARTED
evidence_ids: List[str] = Field(default_factory=list)
last_updated: str = Field(default_factory=now_iso)
class Journey(BaseModel):
user_id: str = "default"
state: JourneyState = JourneyState.NOT_STARTED
current_phase: int = 1
current_competency: Optional[str] = None
remediation_target: Optional[str] = None
history: List[Dict] = Field(default_factory=list)
updated_at: str = Field(default_factory=now_iso)
# --------------------------------------------------------------------- store
class Store:
"""JSON-file collection store rooted at runs/hillclimb/ by default."""
def __init__(self, root: Optional[Path] = None):
self.root = Path(root or os.environ.get("HILLCLIMB_DATA_DIR", "runs/hillclimb"))
self.root.mkdir(parents=True, exist_ok=True)
self._lock = threading.Lock()
self._cache: Dict[str, Dict[str, Dict]] = {}
def _path(self, collection: str) -> Path:
return self.root / f"{collection}.json"
def _load(self, collection: str) -> Dict[str, Dict]:
if collection in self._cache:
return self._cache[collection]
path = self._path(collection)
data: Dict[str, Dict] = {}
if path.exists():
try:
data = json.loads(path.read_text())
except Exception:
data = {}
self._cache[collection] = data
return data
def _flush(self, collection: str) -> None:
path = self._path(collection)
tmp = path.with_suffix(".tmp")
tmp.write_text(json.dumps(self._cache.get(collection, {}), indent=2, default=str))
tmp.replace(path)
def put(self, collection: str, key: str, value: Dict | BaseModel) -> None:
if isinstance(value, BaseModel):
value = value.model_dump(mode="json")
with self._lock:
data = self._load(collection)
data[key] = value
self._flush(collection)
def get(self, collection: str, key: str) -> Optional[Dict]:
with self._lock:
return self._load(collection).get(key)
def delete(self, collection: str, key: str) -> bool:
with self._lock:
data = self._load(collection)
if key in data:
del data[key]
self._flush(collection)
return True
return False
def all(self, collection: str) -> List[Dict]:
with self._lock:
return list(self._load(collection).values())
def where(self, collection: str, **eq) -> List[Dict]:
return [r for r in self.all(collection) if all(r.get(k) == v for k, v in eq.items())]
_store: Optional[Store] = None
def get_store() -> Store:
global _store
if _store is None:
_store = Store()
return _store
def reset_store(root: Optional[Path] = None) -> Store:
"""Replace the singleton (used by tests to isolate state)."""
global _store
_store = Store(root)
return _store