broadfield-dev commited on
Commit
a0913d6
Β·
verified Β·
1 Parent(s): 2116fdd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -18
app.py CHANGED
@@ -1,12 +1,16 @@
1
  #!/usr/bin/env python3
2
  """
3
- Overthinker v20 β€” Gradio Server Backend
4
 
5
  Dual Prompt Architecture:
6
  - Input nodes -> prompts to generate OPTIONS/CHOICES/DECISIONS
7
  - Outcome nodes -> prompts to generate OUTCOMES/CONSEQUENCES
8
  - Model: nvidia/nemotron-3-nano-30b-a3b
9
  Port: 7860
 
 
 
 
10
  """
11
 
12
  import os
@@ -26,10 +30,27 @@ from gradio import Server
26
  from fastapi import HTTPException
27
  from starlette.responses import HTMLResponse
28
 
 
 
 
 
 
 
 
 
 
 
 
29
  load_dotenv()
30
 
31
  OPENROUTER_API_KEY = os.getenv('OPENROUTER_API_KEY', '')
32
  OPENAI_API_KEY = os.getenv('OPENAI_API_KEY', '')
 
 
 
 
 
 
33
 
34
  app = Server()
35
  PORT = 7860
@@ -132,9 +153,39 @@ class HistoryManager:
132
  history_manager = HistoryManager()
133
 
134
  # ────────────────────────────────────────────────
135
- # LLM API Call
136
  # ────────────────────────────────────────────────
137
  def call_api(prompt: str, system_prompt: str = "You are a helpful assistant that generates decision trees.") -> Optional[str]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  if OPENROUTER_API_KEY:
139
  try:
140
  headers = {
@@ -276,12 +327,12 @@ def _fallback_outcomes(decision: str, context: str = "") -> dict:
276
  labels = [pos, neu, neg]
277
  desc_map = {
278
  'positive': f'If this path unfolds favorably, {pos.lower()}. This represents a best-case scenario where your decision leads to growth and improvement.',
279
- 'neutral': f'On this path, {neu.lower()}. The outcome is neither clearly good nor bad β€” it requires careful monitoring.',
280
  'negative': f'In a challenging scenario, {neg.lower()}. This represents potential risks and difficulties that may arise.'
281
  }
282
  tip_map = {
283
  'positive': 'Nurture this positive outcome by staying engaged and proactive.',
284
- 'neutral': 'Monitor this neutral path closely β€” small changes can shift the outcome.',
285
  'negative': 'Prepare contingency plans to mitigate this risk if it materializes.'
286
  }
287
  children = []
@@ -300,7 +351,7 @@ def _fallback_outcomes(decision: str, context: str = "") -> dict:
300
  return {'children': children}
301
 
302
  # ────────────────────────────────────────────────
303
- # Prompt Builders β€” Dual prompts
304
  # ────────────────────────��───────────────────────
305
  def build_root_prompt(decision: str) -> str:
306
  return f'''You are an AI that helps people explore decisions by generating decision trees.
@@ -329,11 +380,11 @@ Parent node: "{decision_label}"
329
  Description: "{decision_desc}"
330
  {comment_section}
331
 
332
- Generate EXACTLY {count} child nodes that represent different OPTIONS or CHOICES the person could take. Each child should be a distinct, realistic possibility β€” an actionable decision they could make at this point.
333
 
334
  IMPORTANT: Frame each child as an OPTION or CHOICE, not as an outcome. For example:
335
  - GOOD: "Consult a financial advisor" (describes an action/choice)
336
- - BAD: "Financial situation improves" (describes an outcome β€” DO NOT use here)
337
 
338
  Return ONLY valid JSON with exactly this structure (no markdown, no backticks):
339
  {{
@@ -372,7 +423,7 @@ Generate EXACTLY {count} child nodes that represent a DIVERSE RANGE of possible
372
 
373
  IMPORTANT: Frame each child as an OUTCOME or CONSEQUENCE, not as a choice someone makes. For example:
374
  - GOOD: "Financial stability improves" (describes a result)
375
- - BAD: "Consider financial planning" (describes a choice β€” DO NOT do this)
376
 
377
  Return ONLY valid JSON with exactly this structure (no markdown, no backticks):
378
  {{
@@ -420,7 +471,7 @@ async def index():
420
  if os.path.exists(html_path):
421
  with open(html_path, "r", encoding="utf-8") as f:
422
  return HTMLResponse(content=f.read(), status_code=200)
423
- return HTMLResponse(content="<h1>Overthinker v20</h1><p>index.html not found</p>", status_code=404)
424
 
425
 
426
  @app.post("/create_tree")
@@ -641,15 +692,15 @@ async def export_path_md(request: dict):
641
  if not tree_id or not node_id:
642
  raise HTTPException(status_code=400, detail="Missing tree_id or node_id")
643
  path = node_manager.get_path(tree_id, node_id)
644
- md = '# 🧠 Overthinker β€” Decision Path\n\n'
645
  for i, node in enumerate(path):
646
  indent = ' ' * i
647
- emoji = {'root': '🌳', 'input': '🧠', 'outcome': 'πŸ“Š'}.get(node.get('type', ''), 'πŸ“Œ')
648
  md += f'{indent}{emoji} **{node.get("label", "")}**\n'
649
  if node.get('description'):
650
  md += f'{indent} > {node.get("description", "")}\n'
651
  if node.get('tips') and len(node['tips']) > 0:
652
- md += f'{indent} > πŸ’‘ {node["tips"][0]}\n'
653
  md += '\n'
654
  return md
655
 
@@ -658,12 +709,24 @@ async def export_path_md(request: dict):
658
  # Launch
659
  # ────────────────────────────────────────────────
660
  if __name__ == "__main__":
661
- print(f"🧠 Overthinker v20 β€” Starting on port {PORT}")
662
- print(f"πŸ€– Model: {DEFAULT_MODEL}")
663
- print(f"🌐 Open http://localhost:{PORT} in your browser")
664
- if not OPENROUTER_API_KEY and not OPENAI_API_KEY:
665
- print("⚠️ No API key found. Using local fallback generation (limited).")
666
- print(f"πŸ“š API docs available at http://localhost:{PORT}/docs")
 
 
 
 
 
 
 
 
 
 
 
 
667
  app.launch(
668
  server_port=PORT,
669
  show_error=True,
 
1
  #!/usr/bin/env python3
2
  """
3
+ Overthinker v24 β€” Gradio Server Backend
4
 
5
  Dual Prompt Architecture:
6
  - Input nodes -> prompts to generate OPTIONS/CHOICES/DECISIONS
7
  - Outcome nodes -> prompts to generate OUTCOMES/CONSEQUENCES
8
  - Model: nvidia/nemotron-3-nano-30b-a3b
9
  Port: 7860
10
+
11
+ Toggle:
12
+ - USE_HUGGINGFACE = True -> Uses HuggingFace Inference Client (HF_TOKEN from .env)
13
+ - USE_HUGGINGFACE = False -> Uses OpenRouter/OpenAI fallback (original behavior)
14
  """
15
 
16
  import os
 
30
  from fastapi import HTTPException
31
  from starlette.responses import HTMLResponse
32
 
33
+ # ────────────────────────────────────────────────
34
+ # HuggingFace Inference Client (optional import)
35
+ # ────────────────────────────────────────────────
36
+ HF_AVAILABLE = False
37
+ try:
38
+ from huggingface_hub import InferenceClient
39
+ HF_AVAILABLE = True
40
+ except ImportError:
41
+ InferenceClient = None
42
+ print("[Warning] huggingface_hub not installed. HuggingFace mode disabled.")
43
+
44
  load_dotenv()
45
 
46
  OPENROUTER_API_KEY = os.getenv('OPENROUTER_API_KEY', '')
47
  OPENAI_API_KEY = os.getenv('OPENAI_API_KEY', '')
48
+ HF_TOKEN = os.getenv('HF_TOKEN', '')
49
+
50
+ # ────────────────────────────────────────────────
51
+ # TOGGLE: Set True to use HuggingFace Inference Client
52
+ # ────────────────────────────────────────────────
53
+ USE_HUGGINGFACE = True # <-- Set to False to use OpenRouter/OpenAI instead
54
 
55
  app = Server()
56
  PORT = 7860
 
153
  history_manager = HistoryManager()
154
 
155
  # ────────────────────────────────────────────────
156
+ # LLM API Call β€” with HuggingFace Toggle
157
  # ────────────────────────────────────────────────
158
  def call_api(prompt: str, system_prompt: str = "You are a helpful assistant that generates decision trees.") -> Optional[str]:
159
+
160
+ # ─── HuggingFace Inference Client Mode ───
161
+ if USE_HUGGINGFACE:
162
+ if not HF_TOKEN:
163
+ print("[HF Error] No HF_TOKEN found in environment variables")
164
+ return None
165
+ if not HF_AVAILABLE:
166
+ print("[HF Error] huggingface_hub not installed. Run: pip install huggingface-hub>=0.23.0")
167
+ return None
168
+
169
+ try:
170
+ client = InferenceClient(token=HF_TOKEN)
171
+
172
+ response = client.chat.completions.create(
173
+ model=DEFAULT_MODEL,
174
+ messages=[
175
+ {"role": "system", "content": system_prompt},
176
+ {"role": "user", "content": prompt}
177
+ ],
178
+ temperature=0.8,
179
+ max_tokens=2048
180
+ )
181
+
182
+ return response.choices[0].message.content
183
+
184
+ except Exception as e:
185
+ print(f"[HF Exception] {e}")
186
+ return None
187
+
188
+ # ─── OpenRouter Mode (original fallback) ───
189
  if OPENROUTER_API_KEY:
190
  try:
191
  headers = {
 
327
  labels = [pos, neu, neg]
328
  desc_map = {
329
  'positive': f'If this path unfolds favorably, {pos.lower()}. This represents a best-case scenario where your decision leads to growth and improvement.',
330
+ 'neutral': f'On this path, {neu.lower()}. The outcome is neither clearly good nor bad \u2014 it requires careful monitoring.',
331
  'negative': f'In a challenging scenario, {neg.lower()}. This represents potential risks and difficulties that may arise.'
332
  }
333
  tip_map = {
334
  'positive': 'Nurture this positive outcome by staying engaged and proactive.',
335
+ 'neutral': 'Monitor this neutral path closely \u2014 small changes can shift the outcome.',
336
  'negative': 'Prepare contingency plans to mitigate this risk if it materializes.'
337
  }
338
  children = []
 
351
  return {'children': children}
352
 
353
  # ────────────────────────────────────────────────
354
+ # Prompt Builders \u2014 Dual prompts
355
  # ────────────────────────��───────────────────────
356
  def build_root_prompt(decision: str) -> str:
357
  return f'''You are an AI that helps people explore decisions by generating decision trees.
 
380
  Description: "{decision_desc}"
381
  {comment_section}
382
 
383
+ Generate EXACTLY {count} child nodes that represent different OPTIONS or CHOICES the person could take. Each child should be a distinct, realistic possibility \u2014 an actionable decision they could make at this point.
384
 
385
  IMPORTANT: Frame each child as an OPTION or CHOICE, not as an outcome. For example:
386
  - GOOD: "Consult a financial advisor" (describes an action/choice)
387
+ - BAD: "Financial situation improves" (describes an outcome \u2014 DO NOT use here)
388
 
389
  Return ONLY valid JSON with exactly this structure (no markdown, no backticks):
390
  {{
 
423
 
424
  IMPORTANT: Frame each child as an OUTCOME or CONSEQUENCE, not as a choice someone makes. For example:
425
  - GOOD: "Financial stability improves" (describes a result)
426
+ - BAD: "Consider financial planning" (describes a choice \u2014 DO NOT do this)
427
 
428
  Return ONLY valid JSON with exactly this structure (no markdown, no backticks):
429
  {{
 
471
  if os.path.exists(html_path):
472
  with open(html_path, "r", encoding="utf-8") as f:
473
  return HTMLResponse(content=f.read(), status_code=200)
474
+ return HTMLResponse(content="<h1>Overthinker v24</h1><p>index.html not found</p>", status_code=404)
475
 
476
 
477
  @app.post("/create_tree")
 
692
  if not tree_id or not node_id:
693
  raise HTTPException(status_code=400, detail="Missing tree_id or node_id")
694
  path = node_manager.get_path(tree_id, node_id)
695
+ md = '# \U0001f9e0 Overthinker \u2014 Decision Path\n\n'
696
  for i, node in enumerate(path):
697
  indent = ' ' * i
698
+ emoji = {'root': '\U0001f333', 'input': '\U0001f9e0', 'outcome': '\U0001f4ca'}.get(node.get('type', ''), '\U0001f4cc')
699
  md += f'{indent}{emoji} **{node.get("label", "")}**\n'
700
  if node.get('description'):
701
  md += f'{indent} > {node.get("description", "")}\n'
702
  if node.get('tips') and len(node['tips']) > 0:
703
+ md += f'{indent} > \U0001f4a1 {node["tips"][0]}\n'
704
  md += '\n'
705
  return md
706
 
 
709
  # Launch
710
  # ────────────────────────────────────────────────
711
  if __name__ == "__main__":
712
+ mode_str = "HuggingFace Inference" if USE_HUGGINGFACE else "OpenRouter/OpenAI"
713
+ print(f"\U0001f9e0 Overthinker v24 \u2014 Starting on port {PORT}")
714
+ print(f"\U0001f916 Model: {DEFAULT_MODEL}")
715
+ print(f"\U0001f500 Inference Mode: {mode_str}")
716
+ print(f"\U0001f310 Open http://localhost:{PORT} in your browser")
717
+
718
+ if USE_HUGGINGFACE:
719
+ if not HF_TOKEN:
720
+ print("\u26a0\ufe0f No HF_TOKEN found. Set it in your .env file.")
721
+ elif not HF_AVAILABLE:
722
+ print("\u26a0\ufe0f huggingface_hub not installed. Run: pip install huggingface-hub>=0.23.0")
723
+ else:
724
+ print(f"\u2705 HuggingFace Inference Client ready (token: {HF_TOKEN[:6]}...{HF_TOKEN[-4:]})")
725
+ else:
726
+ if not OPENROUTER_API_KEY and not OPENAI_API_KEY:
727
+ print("\u26a0\ufe0f No API key found. Using local fallback generation (limited).")
728
+
729
+ print(f"\U0001f4da API docs available at http://localhost:{PORT}/docs")
730
  app.launch(
731
  server_port=PORT,
732
  show_error=True,