rairo commited on
Commit
723fe6e
·
verified ·
1 Parent(s): 0c22513

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +198 -286
main.py CHANGED
@@ -60,7 +60,7 @@ except Exception as e:
60
  bucket = storage.bucket()
61
  db_ref = db.reference()
62
 
63
- # --- Google GenAI Client Initialization (Gemini 3.0 Flash) ---
64
  try:
65
  logger.info("Initializing Google GenAI Client...")
66
  api_key = os.environ.get("Gemini")
@@ -74,10 +74,11 @@ except Exception as e:
74
  exit(1)
75
 
76
  # Model Constants
77
- ATHENA_MODEL = "gemini-3-flash-preview"
 
78
  BRIEFING_MODEL = "gemini-3-flash-preview"
79
 
80
- # Grounding API Keys
81
  WOLFRAM_APP_ID = os.environ.get("WOLFRAM_APP_ID")
82
  OPENALEX_MAILTO = os.environ.get("OPENALEX_MAILTO", "rairo@sozofix.tech")
83
 
@@ -99,10 +100,9 @@ def verify_token(auth_header):
99
 
100
  def verify_admin(auth_header):
101
  uid = verify_token(auth_header)
102
- if not uid: raise PermissionError('Invalid or missing token')
103
  user_data = db_ref.child(f'users/{uid}').get()
104
  if not user_data or not user_data.get('is_admin', False):
105
- logger.warning(f"Admin access denied for user: {uid}")
106
  raise PermissionError('Admin access required')
107
  return uid
108
 
@@ -117,388 +117,302 @@ def upload_to_storage(data_bytes, destination_blob_name, content_type):
117
  return None
118
 
119
  def query_wolfram_alpha(query):
120
- logger.info(f"Querying Wolfram Alpha: {query}")
121
- if not WOLFRAM_APP_ID:
122
- logger.warning("WOLFRAM_APP_ID missing.")
123
- return "Wolfram|Alpha grounding unavailable."
124
  try:
125
  url = f"http://api.wolframalpha.com/v1/result?appid={WOLFRAM_APP_ID}&i={query}"
126
  response = requests.get(url, timeout=5)
127
- if response.status_code == 200:
128
- logger.info("Wolfram Alpha check successful.")
129
- return response.text
130
- else:
131
- logger.warning(f"Wolfram Alpha returned status {response.status_code}")
132
- return "Fact-check pending."
133
- except Exception as e:
134
- logger.error(f"Wolfram Alpha query failed: {e}")
135
- return "Grounding timeout."
136
-
137
- def query_openalex(topic):
138
- logger.info(f"Querying OpenAlex for topic: {topic}")
139
- try:
140
- url = f"https://api.openalex.org/works?search={topic}&mailto={OPENALEX_MAILTO}"
141
- resp = requests.get(url, timeout=5).json()
142
- results = resp.get('results', [])
143
- logger.info(f"OpenAlex found {len(results)} results.")
144
- return [{"title": r['title'], "url": r['doi'] or r['id'], "year": r['publication_year']} for r in results[:3]]
145
- except Exception as e:
146
- logger.error(f"OpenAlex query failed: {e}")
147
- return []
148
 
149
  # -----------------------------------------------------------------------------
150
- # 3. ASYNCHRONOUS VOICE ORCHESTRATION (ATHENA VOICE)
151
  # -----------------------------------------------------------------------------
152
 
153
- def generate_single_narration(text, uid, epiphany_id, layer_name):
154
- """Deepgram Aura-Luna generation for a single layer."""
155
  try:
156
- logger.info(f"Generating TTS for layer: {layer_name}")
157
  api_key = os.environ.get("DEEPGRAM_API_KEY")
158
- if not api_key:
159
- logger.error("DEEPGRAM_API_KEY is not set.")
160
- return layer_name, None
161
-
162
  DEEPGRAM_URL = "https://api.deepgram.com/v1/speak?model=aura-luna-en"
163
  headers = {"Authorization": f"Token {api_key}", "Content-Type": "text/plain"}
164
-
165
  response = requests.post(DEEPGRAM_URL, headers=headers, data=text.encode('utf-8'))
166
  response.raise_for_status()
167
-
168
- audio_path = f"users/{uid}/epiphanies/{epiphany_id}/narrations/{layer_name}.mp3"
169
- url = upload_to_storage(response.content, audio_path, 'audio/mpeg')
170
- logger.info(f"TTS Uploaded: {url}")
171
- return layer_name, url
172
  except Exception as e:
173
- logger.error(f"TTS Error [{layer_name}]: {e}")
174
  return layer_name, None
175
 
176
- def generate_all_narrations_async(data_dict, uid, epiphany_id):
177
- """Uses ThreadPoolExecutor to generate all 4 layers in parallel."""
178
- logger.info(f"Starting parallel TTS generation for epiphany: {epiphany_id}")
179
- layers = ['genesis', 'scientific_core', 'engineering_edge', 'cross_pollination']
180
- results = {}
181
- with ThreadPoolExecutor(max_workers=4) as executor:
182
- futures = [executor.submit(generate_single_narration, data_dict[l], uid, epiphany_id, l) for l in layers]
183
- for f in futures:
184
- layer, url = f.result()
185
- results[layer] = url
186
- return results
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
 
188
  # -----------------------------------------------------------------------------
189
- # 4. EPIPHANY SYNTHESIS (THE DISCOVERY ENDPOINTS)
190
  # -----------------------------------------------------------------------------
 
191
  @app.route('/api/image-proxy', methods=['GET'])
192
  def image_proxy():
193
- """Proxies images from Firebase Storage to bypass CORS/Preview issues."""
194
  image_url = request.args.get('url')
195
  if not image_url: return jsonify({'error': 'No URL'}), 400
196
  try:
197
  resp = requests.get(image_url, timeout=10)
198
  return Response(resp.content, content_type=resp.headers['Content-Type'])
199
- except Exception as e:
200
- return jsonify({'error': str(e)}), 500
201
 
202
  @app.route('/api/epiphany/generate', methods=['POST'])
203
  def generate_epiphany():
204
- logger.info(">>> START generate_epiphany request")
205
  uid = verify_token(request.headers.get('Authorization'))
206
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
207
 
208
- logger.info(f"User validated: {uid}")
209
  user_ref = db_ref.child(f'users/{uid}')
210
  user_data = user_ref.get()
211
-
212
- if not user_data or user_data.get('credits', 0) < 1:
213
- logger.warning(f"User {uid} has insufficient credits.")
214
- return jsonify({'error': 'Insufficient sparks for an Epiphany.'}), 402
215
 
216
- if 'image' not in request.files:
217
- logger.error("No image file provided in request.")
218
- return jsonify({'error': 'Visual input is required.'}), 400
219
-
220
  image_file = request.files['image']
221
  image_bytes = image_file.read()
222
-
 
223
  try:
224
- pil_image = Image.open(io.BytesIO(image_bytes)).convert('RGB')
225
- logger.info("Image bytes successfully converted to PIL.")
226
-
227
- # Step 1: Rapid Identification
228
- logger.info("Step 1: Running Rapid Identification...")
229
- id_prompt = "Identify this precisely. If biological, include the Latin name. If mechanical, include the inventor if known. Reply with ONLY the name."
230
- id_response = client.models.generate_content(model=ATHENA_MODEL, contents=[id_prompt, pil_image])
231
- subject = id_response.text.strip()
232
- logger.info(f"Subject Identified: {subject}")
233
-
234
- # Step 2: Grounding (Parallel Dispatch)
235
- logger.info("Step 2: Grounding Dispatch...")
236
- physics_fact = query_wolfram_alpha(f"constants of {subject}")
237
- papers = query_openalex(subject)
238
-
239
- # Step 3: Synthesis (Feynman Technique)
240
- logger.info("Step 3: Generating Feynman Synthesis JSON...")
241
  synthesis_prompt = f"""
242
- Act as Athena, the Systems Synthesizer. Reveal the first principles of '{subject}'.
243
  Style: Richard Feynman. Simple analogies, profound scientific truths.
244
 
245
- Grounding Context: {physics_fact}
 
 
 
246
 
247
- Output JSON:
248
- {{
249
- "title": "Epic 3-word title",
250
- "genesis": "The origin and the core 'Aha!' moment of this system.",
251
- "scientific_core": "The first principles of physics/biology at play here.",
252
- "engineering_edge": "Detailed analysis of material, stress, or evolutionary advantage.",
253
- "cross_pollination": "How this principle applies to a completely different field."
254
- }}
255
  """
256
 
257
- synth_response = client.models.generate_content(
258
- model=ATHENA_MODEL,
259
  contents=[synthesis_prompt, pil_image],
260
- config=types.GenerateContentConfig(response_mime_type='application/json')
 
 
 
261
  )
262
 
263
- # Log raw response for debugging 500s
264
- raw_text = synth_response.text
265
- logger.info(f"Raw Synthesis Response: {raw_text}")
266
-
267
- # Cleaning up markdown code blocks if present
268
- if raw_text.startswith("```json"):
269
- raw_text = raw_text.replace("```json", "").replace("```", "").strip()
270
-
271
- data = json.loads(raw_text)
272
  epiphany_id = str(uuid.uuid4())
273
- logger.info(f"Epiphany ID generated: {epiphany_id}")
274
-
275
- # Step 4: Parallel Audio Generation
276
- logger.info("Step 4: Running Parallel TTS...")
277
- narration_urls = generate_all_narrations_async(data, uid, epiphany_id)
278
-
279
- # Step 5: Persistence
280
- logger.info("Step 5: Finalizing record and uploading image...")
281
- image_url = upload_to_storage(image_bytes, f"users/{uid}/epiphanies/{epiphany_id}/vision.jpg", 'image/jpeg')
282
-
 
 
 
 
 
 
 
283
  epiphany_record = {
284
  "epiphanyId": epiphany_id,
285
  "uid": uid,
286
- "title": data.get('title', 'Unknown Discovery'),
287
  "subject": subject,
288
  "imageURL": image_url,
289
  "layers": {
290
- "genesis": {"text": data.get('genesis', ''), "audio": narration_urls.get('genesis')},
291
- "scientific_core": {"text": data.get('scientific_core', ''), "audio": narration_urls.get('scientific_core')},
292
- "engineering_edge": {"text": data.get('engineering_edge', ''), "audio": narration_urls.get('engineering_edge')},
293
- "cross_pollination": {"text": data.get('cross_pollination', ''), "audio": narration_urls.get('cross_pollination')}
 
294
  },
295
- "grounding": {"physics": physics_fact, "papers": papers},
296
  "createdAt": datetime.utcnow().isoformat()
297
  }
298
 
299
  db_ref.child(f'epiphanies/{epiphany_id}').set(epiphany_record)
300
- user_ref.update({'credits': user_data.get('credits', 0) - 1})
301
- logger.info(f"EP-GEN SUCCESS: {epiphany_id}")
302
-
303
  return jsonify(epiphany_record), 201
304
 
305
- except json.JSONDecodeError as je:
306
- logger.error(f"JSON Parsing Error: {je}. Raw output was: {synth_response.text}")
307
- return jsonify({'error': 'AI generated invalid JSON. Try again.'}), 500
308
  except Exception as e:
309
- logger.error(f"Generate Epiphany Global Error: {e}")
310
- logger.error(traceback.format_exc())
311
  return jsonify({'error': str(e)}), 500
312
 
313
- @app.route('/api/epiphany/deep-dive', methods=['POST'])
314
- def deep_dive():
315
- logger.info(">>> START deep-dive request")
 
 
 
316
  uid = verify_token(request.headers.get('Authorization'))
317
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
318
 
 
 
 
 
 
319
  image_file = request.files['image']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  try:
321
  pil_image = Image.open(io.BytesIO(image_file.read())).convert('RGB')
322
- dive_prompt = "Act as an Engineering Lead. In 50 words, explain the microscopic or mechanical significance of this specific detail."
323
-
324
- res = client.models.generate_content(model=ATHENA_MODEL, contents=[dive_prompt, pil_image])
325
- logger.info("Deep dive reasoning completed.")
 
 
 
 
 
 
 
 
 
 
326
 
 
 
 
 
 
 
 
 
 
 
 
327
  user_ref = db_ref.child(f'users/{uid}')
328
  user_ref.update({'credits': max(0, user_ref.get().get('credits', 0) - 1)})
329
  return jsonify({"analysis": res.text.strip()}), 200
330
- except Exception as e:
331
- logger.error(f"Deep Dive Error: {e}")
332
- return jsonify({'error': str(e)}), 500
333
 
334
  # -----------------------------------------------------------------------------
335
- # 5. THE CHIRON MENTOR (INTERACTION & MEMORY)
336
  # -----------------------------------------------------------------------------
337
 
338
  @app.route('/api/user/call-briefing', methods=['GET'])
339
  def get_chiron_briefing():
340
- logger.info(">>> START get_chiron_briefing request")
341
  uid = verify_token(request.headers.get('Authorization'))
342
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
343
-
344
  try:
345
- last_epiphany = db_ref.child('epiphanies').order_by_child('uid').equal_to(uid).limit_to_last(1).get() or {}
346
-
347
- context_data = ""
348
- if last_epiphany:
349
- eid = list(last_epiphany.keys())[0]
350
- e_data = last_epiphany[eid]
351
- papers = e_data.get('grounding', {}).get('papers', [])
352
- paper_titles = [p['title'] for p in papers]
353
- context_data = f"Current focus: {e_data['subject']}. Recent papers unlocked: {', '.join(paper_titles)}."
354
-
355
- logger.info(f"Generating briefing with context: {context_data}")
356
- brief_prompt = f"""
357
- Prep Chiron, the Socratic Mentor.
358
- User Context: {context_data}
359
- Write a 4-sentence brief for Chiron. He must reference the scientific papers or topics the user just explored.
360
- """
361
-
362
- response = client.models.generate_content(model=BRIEFING_MODEL, contents=[brief_prompt])
363
- return jsonify({"memory_summary": response.text.strip(), "grounding_context": context_data}), 200
364
- except Exception as e:
365
- logger.error(f"Call Briefing Error: {e}")
366
- return jsonify({'error': str(e)}), 500
367
 
368
  @app.route('/api/log-call-usage', methods=['POST'])
369
- def log_call_usage():
370
- logger.info(">>> START log_call_usage request")
371
  uid = verify_token(request.headers.get('Authorization'))
372
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
373
-
374
- data = request.get_json()
375
- duration = data.get("durationSeconds", 0)
376
- transcript = data.get("transcript", "")
377
-
378
- cost = math.ceil(duration / 60) * 3
379
- logger.info(f"Call ended for {uid}. Duration: {duration}s, Cost: {cost} sparks.")
380
-
 
 
 
381
  try:
382
- user_ref = db_ref.child(f'users/{uid}')
383
- user_data = user_ref.get()
384
- new_bal = max(0, user_data.get('credits', 0) - cost)
385
- user_ref.update({'credits': new_bal})
386
-
387
- if transcript:
388
- db_ref.child(f'transcripts/{uid}').push({
389
- "text": transcript,
390
- "createdAt": datetime.utcnow().isoformat()
391
- })
392
- logger.info("Transcript saved to database.")
393
-
394
- return jsonify({"success": True, "remainingCredits": new_bal}), 200
395
- except Exception as e:
396
- logger.error(f"Usage Logging Error: {e}")
397
- return jsonify({'error': str(e)}), 500
398
-
399
- # -----------------------------------------------------------------------------
400
- # 6. ADMIN, FEEDBACK & CREDITS
401
- # -----------------------------------------------------------------------------
402
 
403
  @app.route('/api/feedback', methods=['POST'])
404
- def submit_feedback():
405
  uid = verify_token(request.headers.get('Authorization'))
406
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
407
- data = request.get_json()
408
- try:
409
- fb_ref = db_ref.child('feedback').push()
410
- fb_ref.set({
411
- "userId": uid,
412
- "message": data.get('message'),
413
- "type": data.get('type', 'general'),
414
- "status": "open",
415
- "createdAt": datetime.utcnow().isoformat()
416
- })
417
- logger.info(f"Feedback received from user {uid}.")
418
- return jsonify({"success": True}), 201
419
- except Exception as e:
420
- logger.error(f"Feedback Submission Error: {e}")
421
- return jsonify({'error': str(e)}), 500
422
 
423
  @app.route('/api/user/request-credits', methods=['POST'])
424
- def request_credits():
425
  uid = verify_token(request.headers.get('Authorization'))
426
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
427
- data = request.get_json()
428
- try:
429
- req_ref = db_ref.child('credit_requests').push()
430
- req_ref.set({
431
- "userId": uid,
432
- "requested_amount": data.get('amount', 50),
433
- "status": "pending",
434
- "createdAt": datetime.utcnow().isoformat()
435
- })
436
- logger.info(f"Spark request logged for user {uid}.")
437
- return jsonify({"success": True}), 201
438
- except Exception as e:
439
- logger.error(f"Credit Request Error: {e}")
440
- return jsonify({'error': str(e)}), 500
441
-
442
- # -----------------------------------------------------------------------------
443
- # 7. AUTHENTICATION & PROFILE
444
- # -----------------------------------------------------------------------------
445
 
446
  @app.route('/api/auth/signup', methods=['POST'])
447
  def signup():
448
- logger.info(">>> START signup sync")
449
  uid = verify_token(request.headers.get('Authorization'))
450
- if not uid:
451
- return jsonify({'error': 'Invalid or expired token'}), 401
452
-
453
- try:
454
- user_ref = db_ref.child(f'users/{uid}')
455
- user_data = user_ref.get()
456
-
457
- if user_data:
458
- logger.info(f"User {uid} already exists, skipping initialization.")
459
- return jsonify({'uid': uid, **user_data}), 200
460
-
461
- data = request.get_json()
462
- new_user_record = {
463
- 'email': data.get('email'),
464
- 'displayName': data.get('displayName', 'Seeker'),
465
- 'credits': 30,
466
- 'is_admin': False,
467
- 'createdAt': datetime.utcnow().isoformat()
468
- }
469
-
470
- user_ref.set(new_user_record)
471
- logger.info(f"New seeker initialized: {uid} with 30 Sparks.")
472
- return jsonify({'success': True, 'uid': uid, **new_user_record}), 201
473
-
474
- except Exception as e:
475
- logger.error(f"Signup Sync Error: {e}")
476
- return jsonify({'error': str(e)}), 400
477
 
478
  @app.route('/api/auth/social-signin', methods=['POST'])
479
  def social_signin():
480
  uid = verify_token(request.headers.get('Authorization'))
481
- if not uid: return jsonify({'error': 'Invalid token'}), 401
482
-
483
  user_ref = db_ref.child(f'users/{uid}')
484
- user_data = user_ref.get()
485
-
486
- if not user_data:
487
- logger.info(f"Initializing social user: {uid}")
488
- firebase_user = auth.get_user(uid)
489
- user_data = {
490
- 'email': firebase_user.email,
491
- 'displayName': firebase_user.display_name or 'Seeker',
492
- 'credits': 30,
493
- 'is_admin': False,
494
- 'createdAt': datetime.utcnow().isoformat()
495
- }
496
- user_ref.set(user_data)
497
-
498
- return jsonify({'uid': uid, **user_data}), 200
499
 
500
  @app.route('/api/user/profile', methods=['GET'])
501
- def get_profile():
502
  uid = verify_token(request.headers.get('Authorization'))
503
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
504
  return jsonify(db_ref.child(f'users/{uid}').get())
@@ -507,15 +421,13 @@ def get_profile():
507
  def list_epiphanies():
508
  uid = verify_token(request.headers.get('Authorization'))
509
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
510
- logger.info(f"Listing epiphanies for user {uid}.")
511
- results = db_ref.child('epiphanies').order_by_child('uid').equal_to(uid).get() or {}
512
- return jsonify(list(results.values()))
513
 
514
  # -----------------------------------------------------------------------------
515
  # 8. MAIN EXECUTION
516
  # -----------------------------------------------------------------------------
517
 
518
  if __name__ == '__main__':
519
- # Standard HF Port 7860
520
- logger.info("Starting Sozo Athena Server on port 7860...")
521
- app.run(debug=False, host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))
 
60
  bucket = storage.bucket()
61
  db_ref = db.reference()
62
 
63
+ # --- Google GenAI Client Initialization (Gemini 3.0) ---
64
  try:
65
  logger.info("Initializing Google GenAI Client...")
66
  api_key = os.environ.get("Gemini")
 
74
  exit(1)
75
 
76
  # Model Constants
77
+ ATHENA_FLASH = "gemini-3-flash-preview"
78
+ ATHENA_PRO = "gemini-3-pro-image-preview"
79
  BRIEFING_MODEL = "gemini-3-flash-preview"
80
 
81
+ # Grounding / External API
82
  WOLFRAM_APP_ID = os.environ.get("WOLFRAM_APP_ID")
83
  OPENALEX_MAILTO = os.environ.get("OPENALEX_MAILTO", "rairo@sozofix.tech")
84
 
 
100
 
101
  def verify_admin(auth_header):
102
  uid = verify_token(auth_header)
103
+ if not uid: raise PermissionError('Invalid token')
104
  user_data = db_ref.child(f'users/{uid}').get()
105
  if not user_data or not user_data.get('is_admin', False):
 
106
  raise PermissionError('Admin access required')
107
  return uid
108
 
 
117
  return None
118
 
119
  def query_wolfram_alpha(query):
120
+ if not WOLFRAM_APP_ID: return "Grounded in theoretical physics."
 
 
 
121
  try:
122
  url = f"http://api.wolframalpha.com/v1/result?appid={WOLFRAM_APP_ID}&i={query}"
123
  response = requests.get(url, timeout=5)
124
+ return response.text if response.status_code == 200 else "Fact-checking..."
125
+ except: return "Grounding context pending."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
  # -----------------------------------------------------------------------------
128
+ # 3. TITANESS MEDIA ENGINE (PARALLEL BLUEPRINTS & NARRATION)
129
  # -----------------------------------------------------------------------------
130
 
131
+ def generate_narration_task(text, uid, epiphany_id, layer_name):
 
132
  try:
 
133
  api_key = os.environ.get("DEEPGRAM_API_KEY")
134
+ if not api_key: return layer_name, None
 
 
 
135
  DEEPGRAM_URL = "https://api.deepgram.com/v1/speak?model=aura-luna-en"
136
  headers = {"Authorization": f"Token {api_key}", "Content-Type": "text/plain"}
 
137
  response = requests.post(DEEPGRAM_URL, headers=headers, data=text.encode('utf-8'))
138
  response.raise_for_status()
139
+ path = f"users/{uid}/epiphanies/{epiphany_id}/narrations/{layer_name}.mp3"
140
+ return layer_name, upload_to_storage(response.content, path, 'audio/mpeg')
 
 
 
141
  except Exception as e:
142
+ logger.error(f"TTS Task Error [{layer_name}]: {e}")
143
  return layer_name, None
144
 
145
+ def generate_blueprint_task(subject, text, uid, epiphany_id, layer_name):
146
+ """Nano Banana Pro: High-fidelity 4K blueprints."""
147
+ try:
148
+ prompt = (
149
+ f"Generate a technical 4K blueprint infographic of a {subject}'s {layer_name}. "
150
+ f"Context: {text}. Style: Leonardo Da Vinci sketch meets modern 4K schematic. "
151
+ f"Midnight navy background, white-line art. Use Google Search to ensure physical accuracy."
152
+ )
153
+ response = client.models.generate_content(
154
+ model=ATHENA_PRO,
155
+ contents=prompt,
156
+ config=types.GenerateContentConfig(
157
+ tools=[{"google_search": {}}],
158
+ image_config=types.ImageConfig(aspect_ratio="16:9", image_size="4K")
159
+ )
160
+ )
161
+ image_parts = [part for part in response.parts if part.inline_data]
162
+ if image_parts:
163
+ img_bytes = image_parts[0].inline_data.data
164
+ path = f"users/{uid}/epiphanies/{epiphany_id}/blueprints/{layer_name}.png"
165
+ return layer_name, upload_to_storage(img_bytes, path, 'image/png')
166
+ return layer_name, None
167
+ except Exception as e:
168
+ logger.error(f"Nano Banana Pro Task Error [{layer_name}]: {e}")
169
+ return layer_name, None
170
 
171
  # -----------------------------------------------------------------------------
172
+ # 4. EPIPHANY & THEIA ENDPOINTS
173
  # -----------------------------------------------------------------------------
174
+
175
  @app.route('/api/image-proxy', methods=['GET'])
176
  def image_proxy():
 
177
  image_url = request.args.get('url')
178
  if not image_url: return jsonify({'error': 'No URL'}), 400
179
  try:
180
  resp = requests.get(image_url, timeout=10)
181
  return Response(resp.content, content_type=resp.headers['Content-Type'])
182
+ except Exception as e: return jsonify({'error': str(e)}), 500
 
183
 
184
  @app.route('/api/epiphany/generate', methods=['POST'])
185
  def generate_epiphany():
186
+ """Core Epiphany + Feynman Scholar Search."""
187
  uid = verify_token(request.headers.get('Authorization'))
188
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
189
 
 
190
  user_ref = db_ref.child(f'users/{uid}')
191
  user_data = user_ref.get()
192
+ if not user_data or user_data.get('credits', 0) < 4:
193
+ return jsonify({'error': 'Need 4 Sparks for Synthesis.'}), 402
 
 
194
 
 
 
 
 
195
  image_file = request.files['image']
196
  image_bytes = image_file.read()
197
+ pil_image = Image.open(io.BytesIO(image_bytes)).convert('RGB')
198
+
199
  try:
200
+ # Step 1: ID
201
+ id_prompt = "Identify this object/system precisely. Reply with ONLY the name."
202
+ subject = client.models.generate_content(model=ATHENA_FLASH, contents=[id_prompt, pil_image]).text.strip()
203
+ logger.info(f"Subject: {subject}")
204
+
205
+ # Step 2: Feynman Synthesis + Universal Research Search
206
+ physics_fact = query_wolfram_alpha(f"physics laws of {subject}")
 
 
 
 
 
 
 
 
 
 
207
  synthesis_prompt = f"""
208
+ Act as Athena. Analyze '{subject}' grounded in this: {physics_fact}.
209
  Style: Richard Feynman. Simple analogies, profound scientific truths.
210
 
211
+ Tasks:
212
+ 1. Search the web (ArXiv, Patents, Journals) for 3 diverse sources about {subject} using Google Search.
213
+ 2. Create 4 Discovery Layers (JSON keys: title, genesis, scientific_core, engineering_edge, cross_pollination).
214
+ 3. For each of the 3 research sources, provide its URL, Title, and a 2-sentence Feynman Summary.
215
 
216
+ Return JSON ONLY.
 
 
 
 
 
 
 
217
  """
218
 
219
+ res = client.models.generate_content(
220
+ model=ATHENA_FLASH,
221
  contents=[synthesis_prompt, pil_image],
222
+ config=types.GenerateContentConfig(
223
+ tools=[{"google_search": {}}],
224
+ response_mime_type='application/json'
225
+ )
226
  )
227
 
228
+ data = json.loads(res.text)
 
 
 
 
 
 
 
 
229
  epiphany_id = str(uuid.uuid4())
230
+ layers = ['genesis', 'scientific_core', 'engineering_edge', 'cross_pollination']
231
+
232
+ # Step 3: Parallel Media (Titaness Execution)
233
+ final_audios = {}
234
+ final_blueprints = {}
235
+ with ThreadPoolExecutor(max_workers=8) as executor:
236
+ aud_futures = [executor.submit(generate_narration_task, data[l], uid, epiphany_id, l) for l in layers]
237
+ blu_futures = [executor.submit(generate_blueprint_task, subject, data[l], uid, epiphany_id, l) for l in layers]
238
+ for f in aud_futures:
239
+ k, v = f.result()
240
+ final_audios[k] = v
241
+ for f in blu_futures:
242
+ k, v = f.result()
243
+ final_blueprints[k] = v
244
+
245
+ # Step 4: Storage
246
+ image_url = upload_to_storage(image_bytes, f"users/{uid}/epiphanies/{epiphany_id}/original.jpg", 'image/jpeg')
247
  epiphany_record = {
248
  "epiphanyId": epiphany_id,
249
  "uid": uid,
250
+ "title": data.get('title', 'System Epiphany'),
251
  "subject": subject,
252
  "imageURL": image_url,
253
  "layers": {
254
+ l: {
255
+ "text": data[l],
256
+ "audio": final_audios.get(l),
257
+ "blueprint": final_blueprints.get(l)
258
+ } for l in layers
259
  },
260
+ "scholar": data.get('scholar_sources', []),
261
  "createdAt": datetime.utcnow().isoformat()
262
  }
263
 
264
  db_ref.child(f'epiphanies/{epiphany_id}').set(epiphany_record)
265
+ user_ref.update({'credits': user_data.get('credits', 0) - 4})
 
 
266
  return jsonify(epiphany_record), 201
267
 
 
 
 
268
  except Exception as e:
269
+ logger.error(f"Epiphany Gen Error: {e}")
 
270
  return jsonify({'error': str(e)}), 500
271
 
272
+ @app.route('/api/epiphany/theia', methods=['POST'])
273
+ def theia_sweep():
274
+ """
275
+ Separate Feature: Theia Mode.
276
+ Takes an epiphany context and performs an annotated 'Universal Sight Sweep'.
277
+ """
278
  uid = verify_token(request.headers.get('Authorization'))
279
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
280
 
281
+ user_ref = db_ref.child(f'users/{uid}')
282
+ if user_ref.get().get('credits', 0) < 4:
283
+ return jsonify({'error': 'Need 4 Sparks for Theia Mode.'}), 402
284
+
285
+ epiphany_id = request.form.get('epiphanyId')
286
  image_file = request.files['image']
287
+
288
+ # Fetch context from existing epiphany
289
+ context_data = db_ref.child(f'epiphanies/{epiphany_id}').get() or {}
290
+ subject = context_data.get('subject', 'Unknown System')
291
+
292
+ sweep_prompt = f"""
293
+ Activate Theia Mode for: {subject}.
294
+ Task: Use Python Code Execution to perform a surgical spatial deconstruction of this image.
295
+ Identify every functional component.
296
+ For each component return:
297
+ 1. Label.
298
+ 2. Bounding Box [ymin, xmin, ymax, xmax] (0-1000).
299
+ 3. A 20-word Feynman-style 'Micro-Epiphany' about its role.
300
+
301
+ Return JSON list ONLY.
302
+ """
303
+
304
  try:
305
  pil_image = Image.open(io.BytesIO(image_file.read())).convert('RGB')
306
+ res = client.models.generate_content(
307
+ model=ATHENA_FLASH,
308
+ contents=[sweep_prompt, pil_image],
309
+ config=types.GenerateContentConfig(
310
+ tools=[types.Tool(code_execution=types.ToolCodeExecution)],
311
+ response_mime_type='application/json'
312
+ )
313
+ )
314
+ annotations = json.loads(res.text)
315
+ user_ref.update({'credits': user_ref.get().get('credits', 0) - 4})
316
+ return jsonify({"annotations": annotations}), 200
317
+ except Exception as e:
318
+ logger.error(f"Theia Mode Error: {e}")
319
+ return jsonify({'error': str(e)}), 500
320
 
321
+ @app.route('/api/epiphany/deep-dive', methods=['POST'])
322
+ def deep_dive():
323
+ uid = verify_token(request.headers.get('Authorization'))
324
+ if not uid: return jsonify({'error': 'Unauthorized'}), 401
325
+ image_file = request.files['image']
326
+ try:
327
+ pil_image = Image.open(io.BytesIO(image_file.read())).convert('RGB')
328
+ res = client.models.generate_content(
329
+ model=ATHENA_FLASH,
330
+ contents=["Feynman style: Explain this zoomed-in detail in 50 words.", pil_image]
331
+ )
332
  user_ref = db_ref.child(f'users/{uid}')
333
  user_ref.update({'credits': max(0, user_ref.get().get('credits', 0) - 1)})
334
  return jsonify({"analysis": res.text.strip()}), 200
335
+ except Exception as e: return jsonify({'error': str(e)}), 500
 
 
336
 
337
  # -----------------------------------------------------------------------------
338
+ # 5. CHIRON, ADMIN & AUTH (NO REGRESSION)
339
  # -----------------------------------------------------------------------------
340
 
341
  @app.route('/api/user/call-briefing', methods=['GET'])
342
  def get_chiron_briefing():
 
343
  uid = verify_token(request.headers.get('Authorization'))
344
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
 
345
  try:
346
+ last = db_ref.child('epiphanies').order_by_child('uid').equal_to(uid).limit_to_last(1).get() or {}
347
+ ctx = "New Seeker."
348
+ if last:
349
+ e = list(last.values())[0]
350
+ ctx = f"Subject: {e['subject']}. Recent Research: {e.get('scholar', [])[:1]}"
351
+ prompt = f"Prep Chiron (Socratic Mentor). Context: {ctx}. 4-sentence brief."
352
+ res = client.models.generate_content(model=BRIEFING_MODEL, contents=[prompt])
353
+ return jsonify({"memory_summary": res.text.strip()}), 200
354
+ except Exception as e: return jsonify({'error': str(e)}), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
355
 
356
  @app.route('/api/log-call-usage', methods=['POST'])
357
+ def log_usage():
 
358
  uid = verify_token(request.headers.get('Authorization'))
359
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
360
+ data = request.json
361
+ cost = math.ceil(data.get("durationSeconds", 0) / 60) * 3
362
+ user_ref = db_ref.child(f'users/{uid}')
363
+ new_bal = max(0, user_ref.get().get('credits', 0) - cost)
364
+ user_ref.update({'credits': new_bal})
365
+ if data.get("transcript"):
366
+ db_ref.child(f'transcripts/{uid}').push({"text": data["transcript"], "at": datetime.utcnow().isoformat()})
367
+ return jsonify({"success": True, "remainingCredits": new_bal}), 200
368
+
369
+ @app.route('/api/admin/dashboard', methods=['GET'])
370
+ def admin_dashboard():
371
  try:
372
+ verify_admin(request.headers.get('Authorization'))
373
+ return jsonify({
374
+ "users": len(db_ref.child('users').get() or {}),
375
+ "epiphanies": len(db_ref.child('epiphanies').get() or {}),
376
+ "requests": len([r for r in (db_ref.child('credit_requests').get() or {}).values() if r.get('status') == 'pending'])
377
+ })
378
+ except Exception as e: return jsonify({'error': str(e)}), 403
 
 
 
 
 
 
 
 
 
 
 
 
 
379
 
380
  @app.route('/api/feedback', methods=['POST'])
381
+ def feedback():
382
  uid = verify_token(request.headers.get('Authorization'))
383
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
384
+ db_ref.child('feedback').push({"uid": uid, "msg": request.json.get('message'), "at": datetime.utcnow().isoformat()})
385
+ return jsonify({"success": True})
 
 
 
 
 
 
 
 
 
 
 
 
 
386
 
387
  @app.route('/api/user/request-credits', methods=['POST'])
388
+ def request_sparks():
389
  uid = verify_token(request.headers.get('Authorization'))
390
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
391
+ db_ref.child('credit_requests').push({"uid": uid, "at": datetime.utcnow().isoformat(), "status": "pending"})
392
+ return jsonify({"success": True})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
 
394
  @app.route('/api/auth/signup', methods=['POST'])
395
  def signup():
 
396
  uid = verify_token(request.headers.get('Authorization'))
397
+ if not uid: return jsonify({'error': 'Unauthorized'}), 401
398
+ user_ref = db_ref.child(f'users/{uid}')
399
+ if not user_ref.get():
400
+ data = request.json
401
+ user_ref.set({'email': data.get('email'), 'displayName': data.get('displayName', 'Seeker'), 'credits': 30, 'is_admin': False, 'createdAt': datetime.utcnow().isoformat()})
402
+ return jsonify({'uid': uid, **user_ref.get()}), 201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
 
404
  @app.route('/api/auth/social-signin', methods=['POST'])
405
  def social_signin():
406
  uid = verify_token(request.headers.get('Authorization'))
407
+ if not uid: return jsonify({'error': 'Unauthorized'}), 401
 
408
  user_ref = db_ref.child(f'users/{uid}')
409
+ if not user_ref.get():
410
+ fu = auth.get_user(uid)
411
+ user_ref.set({'email': fu.email, 'displayName': fu.display_name or 'Seeker', 'credits': 30, 'is_admin': False, 'createdAt': datetime.utcnow().isoformat()})
412
+ return jsonify({'uid': uid, **user_ref.get()}), 200
 
 
 
 
 
 
 
 
 
 
 
413
 
414
  @app.route('/api/user/profile', methods=['GET'])
415
+ def profile():
416
  uid = verify_token(request.headers.get('Authorization'))
417
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
418
  return jsonify(db_ref.child(f'users/{uid}').get())
 
421
  def list_epiphanies():
422
  uid = verify_token(request.headers.get('Authorization'))
423
  if not uid: return jsonify({'error': 'Unauthorized'}), 401
424
+ res = db_ref.child('epiphanies').order_by_child('uid').equal_to(uid).get() or {}
425
+ return jsonify(list(res.values()))
 
426
 
427
  # -----------------------------------------------------------------------------
428
  # 8. MAIN EXECUTION
429
  # -----------------------------------------------------------------------------
430
 
431
  if __name__ == '__main__':
432
+ logger.info("Titaness Final Backbone Active on 7860...")
433
+ app.run(debug=False, host="0.0.0.0", port=7860)