Spaces:
Sleeping
Sleeping
| import json | |
| from datetime import datetime | |
| from app.contracts import EngineRequest, EngineResponse, EngineError | |
| from app.hf_client import HFClient | |
| class OBLEngine: | |
| def __init__(self): | |
| self.client = HFClient() | |
| async def process(self, request: EngineRequest) -> EngineResponse: | |
| try: | |
| action = request.action.lower() | |
| if action == "create_outcome_contract": | |
| return await self._create_outcome_contract(request) | |
| elif action == "decompose_outcome": | |
| return await self._decompose_outcome(request) | |
| elif action == "generate_path": | |
| return await self._generate_path(request) | |
| elif action == "update_readiness": | |
| return await self._update_readiness(request) | |
| else: | |
| raise ValueError(f"Unknown action: {action}") | |
| except Exception as e: | |
| return EngineResponse( | |
| request_id=request.request_id, | |
| ok=False, | |
| status="error", | |
| engine="obl-engine", | |
| action=request.action, | |
| error=EngineError(code="ENGINE_EXECUTION_ERROR", detail=str(e)) | |
| ) | |
| async def _create_outcome_contract(self, request: EngineRequest) -> EngineResponse: | |
| outcome_text = request.input.text or "Not specified" | |
| prompt = ( | |
| f"User Goal: {outcome_text}\n" | |
| "Create a structured 'Outcome Contract' for this goal. It represents a verified result (job skill, exam pass, etc.). " | |
| "Output JSON only with fields: " | |
| "'outcome_statement', 'time_horizon', 'proof_criteria' (list of specific evidences), 'engagement_rules', 'pricing_model_type'." | |
| ) | |
| messages = [{"role": "system", "content": prompt}] | |
| response_text = await self.client.generate(messages) | |
| try: | |
| # Simple cleanup for JSON extraction | |
| contract_data = json.loads(response_text.replace("```json", "").replace("```", "").strip()) | |
| except: | |
| contract_data = {"raw_output": response_text, "note": "Failed to parse JSON contract."} | |
| return EngineResponse( | |
| request_id=request.request_id, | |
| ok=True, | |
| status="success", | |
| engine="obl-engine", | |
| action="create_outcome_contract", | |
| result={"contract_draft": contract_data} | |
| ) | |
| async def _decompose_outcome(self, request: EngineRequest) -> EngineResponse: | |
| contract = request.input.refs.get("contract", {}) | |
| prompt = ( | |
| f"Analyze this Outcome Contract: {json.dumps(contract)}\n" | |
| "Decompose it into:\n" | |
| "1. Skills (What they must do)\n" | |
| "2. Proofs (How we verify it - exams, projects, simulations)\n" | |
| "Output JSON with keys: 'skills' (list of objects with id, name), 'proofs' (list of objects with id, type, rubric)." | |
| ) | |
| messages = [{"role": "system", "content": prompt}] | |
| response_text = await self.client.generate(messages) | |
| try: | |
| decomposition = json.loads(response_text.replace("```json", "").replace("```", "").strip()) | |
| except: | |
| decomposition = {"raw_output": response_text} | |
| return EngineResponse( | |
| request_id=request.request_id, | |
| ok=True, | |
| status="success", | |
| engine="obl-engine", | |
| action="decompose_outcome", | |
| result=decomposition | |
| ) | |
| async def _generate_path(self, request: EngineRequest) -> EngineResponse: | |
| skills = request.input.refs.get("skills", []) | |
| proofs = request.input.refs.get("proofs", []) | |
| prompt = ( | |
| f"Skills: {json.dumps(skills)}\nProofs: {json.dumps(proofs)}\n" | |
| "Generate a 'shortest path' learning plan. Prioritize actions that lead directly to proofs. " | |
| "Output JSON key 'plan' as a list of steps. Each step has: 'step_id', 'action' (Lesson/Practice/Sim/Teachback), 'linked_proof_id'." | |
| ) | |
| messages = [{"role": "system", "content": prompt}] | |
| response_text = await self.client.generate(messages) | |
| try: | |
| plan = json.loads(response_text.replace("```json", "").replace("```", "").strip()) | |
| except: | |
| plan = {"raw_output": response_text} | |
| return EngineResponse( | |
| request_id=request.request_id, | |
| ok=True, | |
| status="success", | |
| engine="obl-engine", | |
| action="generate_path", | |
| result=plan | |
| ) | |
| async def _update_readiness(self, request: EngineRequest) -> EngineResponse: | |
| evidence = request.input.text # e.g., "Passed quiz with 90%" | |
| current_readiness = request.input.refs.get("readiness_score", 0.0) | |
| prompt = ( | |
| f"Current Readiness: {current_readiness}\n" | |
| f"New Evidence: {evidence}\n" | |
| "Estimate the new readiness score (0.0 to 1.0) and identify any risk flags. " | |
| "Output JSON: 'new_readiness_score', 'risk_flags' (list)." | |
| ) | |
| messages = [{"role": "system", "content": prompt}] | |
| response_text = await self.client.generate(messages) | |
| try: | |
| update_data = json.loads(response_text.replace("```json", "").replace("```", "").strip()) | |
| except: | |
| update_data = {"new_readiness_score": current_readiness, "note": "Failed to parse update."} | |
| return EngineResponse( | |
| request_id=request.request_id, | |
| ok=True, | |
| status="success", | |
| engine="obl-engine", | |
| action="update_readiness", | |
| result=update_data | |
| ) | |
| engine = OBLEngine() | |