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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -176
app.py CHANGED
@@ -1,58 +1,35 @@
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):
@@ -68,198 +45,148 @@ def call_ollama(prompt, model):
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(
103
- "https://openrouter.ai/api/v1/chat/completions",
104
- headers={
105
- "Authorization": f"Bearer {OPENROUTER_API_KEY}",
106
- "Content-Type": "application/json"
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
  # =========================
159
- # HEALTH CHECK
160
  # =========================
161
 
162
  @app.get("/health")
163
  def health():
164
  return {
165
  "status": "ok",
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")
181
- async def chat(request: Request):
182
- body = await request.json()
183
 
184
- prompt = body.get("prompt", "")
185
- mode = body.get("mode", "default")
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"}
 
1
  import os
 
2
  import requests
3
  from fastapi import FastAPI, Request
4
+ from fastapi.responses import JSONResponse, HTMLResponse
 
5
  from fastapi.templating import Jinja2Templates
6
 
7
  # =========================
8
+ # APP INIT
9
  # =========================
10
 
11
+ app = FastAPI(title="PyRunner Agent OS", version="3.0")
12
 
13
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
14
+ templates = Jinja2Templates(directory=os.path.join(BASE_DIR, "templates"))
 
 
 
 
 
 
 
 
15
 
16
 
17
  # =========================
18
+ # ENV
19
  # =========================
20
 
21
+ OLLAMA_URL = os.getenv("OLLAMA_URL", "http://localhost:11434")
 
 
 
 
 
 
 
 
22
 
23
+ MODELS = {
24
+ "orchestrator": os.getenv("OLLAMA_MODEL", "qwen2.5:1.5b"),
25
+ "coder": os.getenv("OLLAMA_CODE_MODEL", "qwen2.5-coder:1.5b"),
26
+ "fast": os.getenv("OLLAMA_FAST_MODEL", "deepseek-coder:1.3b"),
27
+ "fallback": os.getenv("OLLAMA_BACKUP_MODEL", "llama3.2:1b")
28
+ }
 
 
 
 
 
29
 
30
 
31
  # =========================
32
+ # OLLAMA CORE CALL
33
  # =========================
34
 
35
  def call_ollama(prompt, model):
 
45
  )
46
  return r.json()
47
  except Exception as e:
48
+ return {"error": str(e)}
49
 
50
 
51
+ # =========================
52
+ # AGENT DEFINITIONS
53
+ # =========================
54
 
55
+ def orchestrator_agent(user_input: str):
56
+ """
57
+ Classifies task type
58
+ """
 
 
 
 
 
 
 
 
 
59
 
60
+ prompt = f"""
61
+ You are an orchestrator AI.
62
 
63
+ Classify this request into one category ONLY:
 
 
64
 
65
+ - code
66
+ - vision
67
+ - fashion
68
+ - hairstyle
69
+ - general
70
+
71
+ User request:
72
+ {user_input}
73
+
74
+ Return ONLY the category name.
75
+ """
76
+
77
+ res = call_ollama(prompt, MODELS["orchestrator"])
78
+
79
+ if "response" in res:
80
+ return res["response"].strip().lower()
81
+
82
+ return "general"
83
+
84
+
85
+ def coder_agent(prompt):
86
+ return call_ollama(prompt, MODELS["coder"])
87
 
88
+
89
+ def fashion_agent(prompt):
90
+ system_prompt = f"""
91
+ You are a fashion expert AI.
92
+ Analyze outfit, styling, colors, trends.
93
+
94
+ User:
95
+ {prompt}
96
+ """
97
+ return call_ollama(system_prompt, MODELS["fast"])
98
+
99
+
100
+ def hairstyle_agent(prompt):
101
+ system_prompt = f"""
102
+ You are a barber & hairstyle expert.
103
+ Describe hairstyle changes, fades, textures, transformations.
104
+
105
+ User:
106
+ {prompt}
107
+ """
108
+ return call_ollama(system_prompt, MODELS["fast"])
109
+
110
+
111
+ def fallback_agent(prompt):
112
+ return call_ollama(prompt, MODELS["fallback"])
113
 
114
 
115
  # =========================
116
+ # ROUTER
117
  # =========================
118
 
119
+ def route_agent(category: str, prompt: str):
 
120
 
121
+ if category == "code":
122
+ return coder_agent(prompt)
123
 
124
+ if category == "fashion":
125
+ return fashion_agent(prompt)
126
 
127
+ if category == "hairstyle":
128
+ return hairstyle_agent(prompt)
129
 
130
+ return fallback_agent(prompt)
131
 
132
 
133
  # =========================
134
+ # UI
135
  # =========================
136
 
137
  @app.get("/", response_class=HTMLResponse)
138
  async def home(request: Request):
139
+ return templates.TemplateResponse("index.html", {"request": request})
 
 
 
 
 
 
 
 
 
 
 
140
 
141
 
142
  # =========================
143
+ # HEALTH
144
  # =========================
145
 
146
  @app.get("/health")
147
  def health():
148
  return {
149
  "status": "ok",
150
+ "agents": list(MODELS.keys())
 
 
 
 
 
 
151
  }
152
 
153
 
154
  # =========================
155
+ # MAIN AGENT ENDPOINT
156
  # =========================
157
 
158
+ @app.post("/api/agent")
159
+ async def agent(request: Request):
 
160
 
161
+ body = await request.json()
162
+ user_input = body.get("prompt", "")
163
 
164
+ # 1. Decide agent
165
+ category = orchestrator_agent(user_input)
166
 
167
+ # 2. Route to agent
168
+ result = route_agent(category, user_input)
169
 
170
+ # 3. Fallback safety
171
  if "error" in result:
172
+ result = fallback_agent(user_input)
173
 
174
+ return JSONResponse({
175
+ "category": category,
176
+ "result": result
177
+ })
 
178
 
179
 
180
  # =========================
181
+ # IMAGE PIPELINE HOOK (future)
182
  # =========================
183
 
184
  @app.post("/api/image")
185
  async def image_pipeline(request: Request):
186
  body = await request.json()
 
187
 
188
  return {
189
  "status": "queued",
190
+ "pipeline": "vision_stub",
191
+ "note": "Attach diffusion model later"
192
+ }