rairo commited on
Commit
fb7ced5
·
verified ·
1 Parent(s): ebf3a85

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +111 -57
main.py CHANGED
@@ -88,14 +88,44 @@ OPENALEX_MAILTO = os.environ.get("OPENALEX_MAILTO", "rairo@sozofix.tech")
88
  # 2. HELPER FUNCTIONS & AUTHENTICATION
89
  # -----------------------------------------------------------------------------
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  def _strip_json_fences(s: str) -> str:
92
- """Removes markdown code fences and cleans up AI string responses."""
93
  s = (s or "").strip()
94
  if "```" in s:
95
  m = re.search(r"```(?:json)?\s*(.*?)\s*```", s, re.DOTALL)
96
  if m: return m.group(1).strip()
97
  return s
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  def verify_token(auth_header):
100
  """Verifies Firebase ID token from the Authorization header."""
101
  if not auth_header or not auth_header.startswith('Bearer '):
@@ -453,84 +483,108 @@ def validate_odysseus_v6(t: dict) -> (bool, str):
453
  @app.route('/api/trial/generate', methods=['POST'])
454
  def generate_trial():
455
  """
456
- Odysseus v10: The Socratic Instrument.
457
- Architects a bespoke, interactive React Component based on the 'Essential Verb'.
458
  Cost: 1 Spark.
459
  """
460
- logger.info(">>> ODYSSEUS V10: ARCHITECTING SOCRATIC INSTRUMENT")
461
  uid = verify_token(request.headers.get('Authorization'))
462
  if not uid: return jsonify({"error": "Unauthorized"}), 401
463
-
464
- payload = request.get_json()
465
  ep_id, layer_key, subject = payload.get("epiphanyId"), payload.get("layerKey"), payload.get("subject")
466
 
 
 
 
 
467
  trial_path = f"epiphanies/{ep_id}/trials/{layer_key}"
468
  existing = db_ref.child(trial_path).get()
469
  if existing: return jsonify(existing), 200
470
 
 
471
  lock_ref = db_ref.child(f"epiphanies/{ep_id}/trialLocks/{layer_key}")
472
- if lock_ref.get(): return jsonify({"error": "Forging Instrument..."}), 409
473
  lock_ref.set({"uid": uid, "at": datetime.utcnow().isoformat()})
474
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
  try:
476
- layer_data = db_ref.child(f"epiphanies/{ep_id}/layers/{layer_key}").get() or {}
477
- context = layer_data.get('text', '')
478
-
479
- trial_prompt = f"""
480
- You are 'Athena's Master Instrumentalist'.
481
- Subject: {subject}. Layer: {layer_key}. Context: {context}
482
-
483
- TASK: Forge a bespoke, interactive React component named 'Instrument'.
484
- This is a TACTILE EXPERIMENT that communicates with the Odysseus Workbench.
485
-
486
- MANDATORY PROPS INTERFACE:
487
- - The component receives: `{{ onAction, onWin }}`.
488
- - `onAction()`: You MUST call this the instant the user interacts (clicks/drags/keys).
489
- - `onWin()`: You MUST call this when the seeker achieves the scientific goal.
490
-
491
- RULES FOR THE CODE:
492
- 1. DESIGN: Create a high-contrast lab environment (Dark #0B1120, Gold #D4AF37).
493
- 2. PHYSICS: Use Matter.js for weight and tension.
494
- 3. INTERACTION: Design a specific interaction (e.g. 'Stitching the DNA', 'Managing the Fuel flow').
495
- 4. ENTROPY: The system must have a decay factor. If the user doesn't call `onAction()`, show visual failure.
496
- 5. NO WRAPPERS: Return only the component code. Use React hooks.
497
-
498
- JSON SCHEMA:
499
- {{
500
- "engine": "odysseus_v10",
501
- "instrument_name": "Unique Title",
502
- "mission_brief": "Feynman-style objective.",
503
- "component_code": "const Instrument = ({{ onAction, onWin }}) => {{ ... }}; export default Instrument;",
504
- "feynman_truth": "The law proven by this instrument."
505
- }}
506
- """
507
 
508
- res = client.models.generate_content(
509
- model=ATHENA_FLASH,
510
- contents=trial_prompt,
511
- config=types.GenerateContentConfig(response_mime_type="application/json")
512
- )
513
-
514
- trial_data = json.loads(_strip_json_fences(res.text))
515
-
516
  user_ref = db_ref.child(f'users/{uid}')
517
- credits = (user_ref.get() or {}).get('credits', 0)
518
- if credits < 1:
 
 
 
 
519
  lock_ref.delete()
520
  return jsonify({"error": "Insufficient Sparks"}), 402
521
-
522
- trial_data["createdAt"] = datetime.utcnow().isoformat()
523
- db_ref.child(trial_path).set(trial_data)
524
- user_ref.update({'credits': credits - 1})
525
-
526
  lock_ref.delete()
527
- logger.info(f"Odysseus v10: Instrument forged for {subject}")
528
- return jsonify(trial_data), 201
 
529
 
530
  except Exception as e:
531
  lock_ref.delete()
532
- logger.error(f"v10 Forging Error: {e}")
533
- return jsonify({"error": str(e)}), 500
534
 
535
  # -----------------------------------------------------------------------------
536
  # 5. THE CHIRON MENTOR & SYSTEM UTILS
 
88
  # 2. HELPER FUNCTIONS & AUTHENTICATION
89
  # -----------------------------------------------------------------------------
90
 
91
+ # -----------------------------------------------------------------------------
92
+ # ODYSSEUS V10 CONSTANTS & LINTING
93
+ # -----------------------------------------------------------------------------
94
+ FORBIDDEN_CODE_PATTERNS = [
95
+ r"\bfetch\s*\(", r"\bXMLHttpRequest\b", r"\bWebSocket\b", r"\blocalStorage\b",
96
+ r"\bsessionStorage\b", r"\bdocument\.cookie\b", r"\bwindow\.location\b",
97
+ r"\beval\s*\(", r"\bnew\s+Function\s*\(", r"\bimport\s*\(", r"\brequire\s*\("
98
+ ]
99
+
100
+ REQUIRED_CODE_PATTERNS = [r"\bexport\s+default\s+Instrument\b"]
101
+ ALLOWED_INTERACTIONS = {"drag", "dial", "toggle", "tap", "hold", "keys"}
102
+ ALLOWED_WIN_TYPES = {"zone_dwell", "threshold", "sequence", "match"}
103
+
104
  def _strip_json_fences(s: str) -> str:
 
105
  s = (s or "").strip()
106
  if "```" in s:
107
  m = re.search(r"```(?:json)?\s*(.*?)\s*```", s, re.DOTALL)
108
  if m: return m.group(1).strip()
109
  return s
110
 
111
+ def validate_manifest(m: dict) -> (bool, str):
112
+ if not isinstance(m, dict): return False, "Manifest is not an object"
113
+ for k in ("engine", "interaction", "win_rule"):
114
+ if k not in m: return False, f"Manifest missing '{k}'"
115
+ return True, "ok"
116
+
117
+ def lint_component_code(code: str) -> (bool, list):
118
+ errors = []
119
+ if not isinstance(code, str) or len(code.strip()) < 50:
120
+ return False, ["component_code missing/too short"]
121
+ for pat in FORBIDDEN_CODE_PATTERNS:
122
+ if re.search(pat, code): errors.append(f"forbidden_pattern")
123
+ for pat in REQUIRED_CODE_PATTERNS:
124
+ if not re.search(pat, code): errors.append(f"missing_export_default_Instrument")
125
+ if "onAction" not in code: errors.append("missing_call:onAction")
126
+ if "onWin" not in code: errors.append("missing_call:onWin")
127
+ return len(errors) == 0, errors
128
+
129
  def verify_token(auth_header):
130
  """Verifies Firebase ID token from the Authorization header."""
131
  if not auth_header or not auth_header.startswith('Bearer '):
 
483
  @app.route('/api/trial/generate', methods=['POST'])
484
  def generate_trial():
485
  """
486
+ Odysseus v10: The Self-Repairing Instrumentalist.
487
+ Generates bespoke Vanilla React components with a 3-pass repair loop.
488
  Cost: 1 Spark.
489
  """
490
+ logger.info(">>> ODYSSEUS V10: GENERATING TRIAL")
491
  uid = verify_token(request.headers.get('Authorization'))
492
  if not uid: return jsonify({"error": "Unauthorized"}), 401
493
+
494
+ payload = request.get_json(silent=True) or {}
495
  ep_id, layer_key, subject = payload.get("epiphanyId"), payload.get("layerKey"), payload.get("subject")
496
 
497
+ if not all([ep_id, layer_key, subject]):
498
+ return jsonify({"error": "Missing context parameters."}), 400
499
+
500
+ # 1. Cache Check
501
  trial_path = f"epiphanies/{ep_id}/trials/{layer_key}"
502
  existing = db_ref.child(trial_path).get()
503
  if existing: return jsonify(existing), 200
504
 
505
+ # 2. Concurrency Lock
506
  lock_ref = db_ref.child(f"epiphanies/{ep_id}/trialLocks/{layer_key}")
507
+ if lock_ref.get(): return jsonify({"status": "locked"}), 409
508
  lock_ref.set({"uid": uid, "at": datetime.utcnow().isoformat()})
509
 
510
+ # 3. Context Retrieval
511
+ layer_obj = db_ref.child(f"epiphanies/{ep_id}/layers/{layer_key}").get() or {}
512
+ ctx_text = layer_obj.get("text", "General scientific principles.")
513
+
514
+ # 4. Forge & Repair Loop
515
+ attempts, forged, last_raw = 0, None, ""
516
+
517
+ base_prompt = f"""
518
+ You are Athena's Master Instrumentalist. Forge a tactile scientific Instrument for {subject}.
519
+ Layer Context: {ctx_text}
520
+
521
+ TASK: Write a React component named 'Instrument'.
522
+ REQUIREMENTS:
523
+ 1. Vanilla React ONLY. No external libraries (No Matter.js). Use SVG/Canvas for visuals.
524
+ 2. Tactile: The system must fail/decay if user does nothing.
525
+ 3. Handshake: Must accept and call `props.onAction()` (on touch) and `props.onWin()` (on success).
526
+ 4. Design: Midnight Navy #0B1120, Gold #D4AF37.
527
+
528
+ JSON SCHEMA:
529
+ {{
530
+ "engine": "odysseus_v10",
531
+ "instrument_name": "Title",
532
+ "mission_brief": "Goal",
533
+ "instrument_manifest": {{ "engine": "odysseus_v10", "interaction": "drag|tap|hold", "win_rule": {{ "type": "zone_dwell" }} }},
534
+ "component_code": "const Instrument = (props) => {{ ... }}; export default Instrument;",
535
+ "feynman_truth": "The law proven."
536
+ }}
537
+ """
538
+
539
  try:
540
+ while attempts < 3 and not forged:
541
+ attempts += 1
542
+ current_prompt = base_prompt if attempts == 1 else f"REPAIR THIS CODE: {last_raw}\nERRORS: {last_errs}"
543
+
544
+ res = client.models.generate_content(
545
+ model=ATHENA_FLASH,
546
+ contents=current_prompt,
547
+ config=types.GenerateContentConfig(response_mime_type="application/json")
548
+ )
549
+
550
+ last_raw = _strip_json_fences(res.text)
551
+ candidate = json.loads(last_raw)
552
+
553
+ # Validation logic
554
+ ok_m, _ = validate_manifest(candidate.get("instrument_manifest"))
555
+ ok_c, last_errs = lint_component_code(candidate.get("component_code"))
556
+
557
+ if ok_m and ok_c:
558
+ forged = candidate
559
+ break
 
 
 
 
 
 
 
 
 
 
 
560
 
561
+ if not forged:
562
+ lock_ref.delete()
563
+ return jsonify({"status": "failed", "reason": "Compilation failure"}), 422
564
+
565
+ # 5. Atomic Credit Deduction (1 Spark)
 
 
 
566
  user_ref = db_ref.child(f'users/{uid}')
567
+ def credits_txn(cur):
568
+ cur = cur or 0
569
+ return cur - 1 if cur >= 1 else cur
570
+
571
+ result = user_ref.child('credits').transaction(credits_txn)
572
+ if result < 0:
573
  lock_ref.delete()
574
  return jsonify({"error": "Insufficient Sparks"}), 402
575
+
576
+ # 6. Finalize
577
+ forged["createdAt"] = datetime.utcnow().isoformat()
578
+ db_ref.child(trial_path).set(forged)
 
579
  lock_ref.delete()
580
+
581
+ logger.info(f"Odysseus v10 forged for {subject} in {attempts} attempts.")
582
+ return jsonify(forged), 201
583
 
584
  except Exception as e:
585
  lock_ref.delete()
586
+ logger.error(f"Forging Global Error: {e}")
587
+ return jsonify({"error": "The laws of physics failed to compile."}), 500
588
 
589
  # -----------------------------------------------------------------------------
590
  # 5. THE CHIRON MENTOR & SYSTEM UTILS