Akoda35 commited on
Commit
868f94f
·
verified ·
1 Parent(s): a63bbe1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -77
app.py CHANGED
@@ -1,50 +1,61 @@
1
  import os
2
  import json
3
  import requests
4
- from fastapi import FastAPI, Request, Header
5
  from fastapi.responses import HTMLResponse, JSONResponse
6
  from fastapi.staticfiles import StaticFiles
7
  from fastapi.templating import Jinja2Templates
8
 
9
  # =========================
10
- # APP INIT
11
  # =========================
12
 
13
- app = FastAPI(title="PyRunner AI Space", version="1.0")
14
 
15
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
16
 
17
- templates = Jinja2Templates(directory=os.path.join(BASE_DIR, "templates"))
 
18
 
19
- static_path = os.path.join(BASE_DIR, "static")
20
- if os.path.exists(static_path):
21
- app.mount("/static", StaticFiles(directory=static_path), name="static")
 
 
22
 
23
 
24
  # =========================
25
- # ENV
26
  # =========================
27
 
28
- OLLAMA_URL = os.getenv("OLLAMA_URL", "http://localhost:11434")
29
- OLLAMA_FALLBACK_URL = os.getenv("OLLAMA_FALLBACK_URL")
 
 
 
 
 
 
 
30
 
31
- OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "qwen2.5:1.5b")
32
- OLLAMA_CODE_MODEL = os.getenv("OLLAMA_CODE_MODEL", "qwen2.5-coder:1.5b")
33
- OLLAMA_FAST_MODEL = os.getenv("OLLAMA_FAST_MODEL", "deepseek-coder:1.3b")
34
- OLLAMA_BACKUP_MODEL = os.getenv("OLLAMA_BACKUP_MODEL", "llama3.2:1b")
35
 
36
- OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
37
- OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL")
38
 
39
- SUPABASE_URL = os.getenv("SUPABASE_URL")
40
- SUPABASE_KEY = os.getenv("SUPABASE_KEY")
 
41
 
42
 
43
  # =========================
44
- # SAFE OLLAMA CALL
45
  # =========================
46
 
47
- def call_ollama(prompt: str, model: str):
48
  try:
49
  r = requests.post(
50
  f"{OLLAMA_URL}/api/generate",
@@ -57,16 +68,35 @@ def call_ollama(prompt: str, model: str):
57
  )
58
  return r.json()
59
  except Exception as e:
60
- return {"error": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
 
63
  # =========================
64
- # OPENROUTER FALLBACK
65
  # =========================
66
 
67
- def call_openrouter(prompt: str):
68
  if not OPENROUTER_API_KEY:
69
- return {"error": "No OpenRouter key"}
70
 
71
  try:
72
  r = requests.post(
@@ -77,43 +107,52 @@ def call_openrouter(prompt: str):
77
  },
78
  json={
79
  "model": OPENROUTER_MODEL,
80
- "messages": [
81
- {"role": "user", "content": prompt}
82
- ]
83
  },
84
  timeout=60
85
  )
86
  return r.json()
87
  except Exception as e:
88
- return {"error": str(e)}
89
 
90
 
91
  # =========================
92
- # ROUTER (1B–2B ONLY LOGIC)
93
  # =========================
94
 
95
- def route_model(task: str):
96
- task = task.lower()
97
 
98
- if "code" in task or "script" in task:
99
  return OLLAMA_CODE_MODEL
100
 
101
- if "fast" in task:
102
  return OLLAMA_FAST_MODEL
103
 
104
- if "fallback" in task:
105
  return OLLAMA_BACKUP_MODEL
106
 
107
  return OLLAMA_MODEL
108
 
109
 
110
  # =========================
111
- # UI ROUTE
112
  # =========================
113
 
114
  @app.get("/", response_class=HTMLResponse)
115
  async def home(request: Request):
116
- return templates.TemplateResponse("index.html", {"request": request})
 
 
 
 
 
 
 
 
 
 
 
117
 
118
 
119
  # =========================
@@ -127,13 +166,15 @@ def health():
127
  "ollama": OLLAMA_URL,
128
  "models": {
129
  "default": OLLAMA_MODEL,
130
- "code": OLLAMA_CODE_MODEL
 
 
131
  }
132
  }
133
 
134
 
135
  # =========================
136
- # CHAT ENDPOINT
137
  # =========================
138
 
139
  @app.post("/api/chat")
@@ -145,82 +186,80 @@ async def chat(request: Request):
145
 
146
  model = route_model(mode)
147
 
 
148
  result = call_ollama(prompt, model)
149
 
150
- # fallback logic
151
- if "error" in result and OLLAMA_FALLBACK_URL:
152
- try:
153
- r = requests.post(
154
- f"{OLLAMA_FALLBACK_URL}/api/generate",
155
- json={
156
- "model": OLLAMA_BACKUP_MODEL,
157
- "prompt": prompt,
158
- "stream": False
159
- },
160
- timeout=60
161
- )
162
- result = r.json()
163
- except Exception as e:
164
- result = {"error": str(e)}
165
 
166
  return JSONResponse(result)
167
 
168
 
169
  # =========================
170
- # IMAGE PIPELINE HOOK (stub)
171
  # =========================
172
 
173
  @app.post("/api/image")
174
  async def image_pipeline(request: Request):
175
  body = await request.json()
176
-
177
- prompt = body.get("prompt", "a modern hairstyle")
178
 
179
  return {
180
  "status": "queued",
181
- "message": "Image pipeline not yet connected",
182
- "prompt": prompt
 
183
  }
184
 
185
 
186
  # =========================
187
- # N8N WEBHOOK TRIGGER
188
  # =========================
189
 
190
  @app.post("/api/webhook/n8n")
191
  async def trigger_n8n(request: Request):
192
- body = await request.json()
193
-
194
- webhook_url = os.getenv("N8N_WEBHOOK_URL")
195
 
196
- if not webhook_url:
197
- return {"error": "No webhook configured"}
198
 
199
  try:
200
- r = requests.post(webhook_url, json=body, timeout=30)
201
- return {"status": "sent", "response": r.text}
 
 
 
202
  except Exception as e:
203
  return {"error": str(e)}
204
 
205
 
206
  # =========================
207
- # DEBUG CONFIG (SAFE)
208
  # =========================
209
 
210
  @app.get("/config")
211
  def config():
212
  return {
213
  "ollama_url": OLLAMA_URL,
 
 
 
214
  "models": {
215
  "default": OLLAMA_MODEL,
216
- "code": OLLAMA_CODE_MODEL,
217
- "fast": OLLAMA_FAST_MODEL,
218
- "backup": OLLAMA_BACKUP_MODEL
219
- },
220
- "openrouter": {
221
- "enabled": bool(OPENROUTER_API_KEY)
222
- },
223
- "supabase": {
224
- "enabled": bool(SUPABASE_URL and SUPABASE_KEY)
225
  }
226
- }
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import json
3
  import requests
4
+ from fastapi import FastAPI, Request
5
  from fastapi.responses import HTMLResponse, JSONResponse
6
  from fastapi.staticfiles import StaticFiles
7
  from fastapi.templating import Jinja2Templates
8
 
9
  # =========================
10
+ # APP INIT (HARDENED)
11
  # =========================
12
 
13
+ app = FastAPI(title="PyRunner AI Space", version="2.0")
14
 
15
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
16
 
17
+ TEMPLATE_DIR = os.path.join(BASE_DIR, "templates")
18
+ STATIC_DIR = os.path.join(BASE_DIR, "static")
19
 
20
+ # SAFE TEMPLATE LOADER (FIXES YOUR ERROR CLASS)
21
+ templates = Jinja2Templates(directory=TEMPLATE_DIR)
22
+
23
+ if os.path.exists(STATIC_DIR):
24
+ app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
25
 
26
 
27
  # =========================
28
+ # ENV LOADER (SAFE DEFAULTS)
29
  # =========================
30
 
31
+ def env(key, default=None):
32
+ value = os.getenv(key, default)
33
+ if value == "None":
34
+ return None
35
+ return value
36
+
37
+
38
+ OLLAMA_URL = env("OLLAMA_URL", "http://localhost:11434")
39
+ OLLAMA_FALLBACK_URL = env("OLLAMA_FALLBACK_URL")
40
 
41
+ OLLAMA_MODEL = env("OLLAMA_MODEL", "qwen2.5:1.5b")
42
+ OLLAMA_CODE_MODEL = env("OLLAMA_CODE_MODEL", "qwen2.5-coder:1.5b")
43
+ OLLAMA_FAST_MODEL = env("OLLAMA_FAST_MODEL", "deepseek-coder:1.3b")
44
+ OLLAMA_BACKUP_MODEL = env("OLLAMA_BACKUP_MODEL", "llama3.2:1b")
45
 
46
+ OPENROUTER_API_KEY = env("OPENROUTER_API_KEY")
47
+ OPENROUTER_MODEL = env("OPENROUTER_MODEL")
48
 
49
+ SUPABASE_URL = env("SUPABASE_URL")
50
+ SUPABASE_KEY = env("SUPABASE_KEY")
51
+ N8N_WEBHOOK_URL = env("N8N_WEBHOOK_URL")
52
 
53
 
54
  # =========================
55
+ # OLLAMA CLIENT (HARDENED)
56
  # =========================
57
 
58
+ def call_ollama(prompt, model):
59
  try:
60
  r = requests.post(
61
  f"{OLLAMA_URL}/api/generate",
 
68
  )
69
  return r.json()
70
  except Exception as e:
71
+ return {"error": str(e), "source": "ollama_primary"}
72
+
73
+
74
+ def call_ollama_fallback(prompt):
75
+ if not OLLAMA_FALLBACK_URL:
76
+ return {"error": "No fallback URL configured"}
77
+
78
+ try:
79
+ r = requests.post(
80
+ f"{OLLAMA_FALLBACK_URL}/api/generate",
81
+ json={
82
+ "model": OLLAMA_BACKUP_MODEL,
83
+ "prompt": prompt,
84
+ "stream": False
85
+ },
86
+ timeout=60
87
+ )
88
+ return r.json()
89
+ except Exception as e:
90
+ return {"error": str(e), "source": "ollama_fallback"}
91
 
92
 
93
  # =========================
94
+ # OPENROUTER (OPTIONAL)
95
  # =========================
96
 
97
+ def call_openrouter(prompt):
98
  if not OPENROUTER_API_KEY:
99
+ return {"error": "OpenRouter not configured"}
100
 
101
  try:
102
  r = requests.post(
 
107
  },
108
  json={
109
  "model": OPENROUTER_MODEL,
110
+ "messages": [{"role": "user", "content": prompt}]
 
 
111
  },
112
  timeout=60
113
  )
114
  return r.json()
115
  except Exception as e:
116
+ return {"error": str(e), "source": "openrouter"}
117
 
118
 
119
  # =========================
120
+ # SMART ROUTER (1B–2B ONLY SAFE)
121
  # =========================
122
 
123
+ def route_model(mode: str):
124
+ mode = (mode or "").lower()
125
 
126
+ if "code" in mode:
127
  return OLLAMA_CODE_MODEL
128
 
129
+ if "fast" in mode:
130
  return OLLAMA_FAST_MODEL
131
 
132
+ if "backup" in mode or "fallback" in mode:
133
  return OLLAMA_BACKUP_MODEL
134
 
135
  return OLLAMA_MODEL
136
 
137
 
138
  # =========================
139
+ # UI ROUTE (SAFE)
140
  # =========================
141
 
142
  @app.get("/", response_class=HTMLResponse)
143
  async def home(request: Request):
144
+ try:
145
+ return templates.TemplateResponse(
146
+ "index.html",
147
+ {"request": request}
148
+ )
149
+ except Exception as e:
150
+ # HARD FALLBACK (prevents total crash)
151
+ return HTMLResponse(f"""
152
+ <h1>PyRunner Running</h1>
153
+ <p>Template error fallback active</p>
154
+ <pre>{str(e)}</pre>
155
+ """)
156
 
157
 
158
  # =========================
 
166
  "ollama": OLLAMA_URL,
167
  "models": {
168
  "default": OLLAMA_MODEL,
169
+ "code": OLLAMA_CODE_MODEL,
170
+ "fast": OLLAMA_FAST_MODEL,
171
+ "backup": OLLAMA_BACKUP_MODEL
172
  }
173
  }
174
 
175
 
176
  # =========================
177
+ # CHAT ENDPOINT (IMPROVED FLOW)
178
  # =========================
179
 
180
  @app.post("/api/chat")
 
186
 
187
  model = route_model(mode)
188
 
189
+ # 1. Try primary Ollama
190
  result = call_ollama(prompt, model)
191
 
192
+ # 2. Fallback to local backup
193
+ if "error" in result:
194
+ result = call_ollama_fallback(prompt)
195
+
196
+ # 3. Optional OpenRouter fallback (last resort)
197
+ if "error" in result:
198
+ result = call_openrouter(prompt)
 
 
 
 
 
 
 
 
199
 
200
  return JSONResponse(result)
201
 
202
 
203
  # =========================
204
+ # IMAGE PIPELINE (STUB READY)
205
  # =========================
206
 
207
  @app.post("/api/image")
208
  async def image_pipeline(request: Request):
209
  body = await request.json()
210
+ prompt = body.get("prompt", "")
 
211
 
212
  return {
213
  "status": "queued",
214
+ "pipeline": "not_connected",
215
+ "prompt": prompt,
216
+ "note": "Connect to diffusion/vision service next"
217
  }
218
 
219
 
220
  # =========================
221
+ # N8N WEBHOOK
222
  # =========================
223
 
224
  @app.post("/api/webhook/n8n")
225
  async def trigger_n8n(request: Request):
226
+ if not N8N_WEBHOOK_URL:
227
+ return {"error": "N8N webhook not set"}
 
228
 
229
+ body = await request.json()
 
230
 
231
  try:
232
+ r = requests.post(N8N_WEBHOOK_URL, json=body, timeout=30)
233
+ return {
234
+ "status": "sent",
235
+ "response": r.text
236
+ }
237
  except Exception as e:
238
  return {"error": str(e)}
239
 
240
 
241
  # =========================
242
+ # DEBUG CONFIG
243
  # =========================
244
 
245
  @app.get("/config")
246
  def config():
247
  return {
248
  "ollama_url": OLLAMA_URL,
249
+ "supabase": bool(SUPABASE_URL and SUPABASE_KEY),
250
+ "openrouter": bool(OPENROUTER_API_KEY),
251
+ "n8n": bool(N8N_WEBHOOK_URL),
252
  "models": {
253
  "default": OLLAMA_MODEL,
254
+ "code": OLLAMA_CODE_MODEL
 
 
 
 
 
 
 
 
255
  }
256
+ }
257
+
258
+
259
+ # =========================
260
+ # SIMPLE TEST ROUTE (IMPORTANT FOR DEBUGGING)
261
+ # =========================
262
+
263
+ @app.get("/ping")
264
+ def ping():
265
+ return {"ping": "ok"}