Mayank2027 commited on
Commit
4061c53
·
verified ·
1 Parent(s): 4d3a867

Update backend/main.py

Browse files
Files changed (1) hide show
  1. backend/main.py +65 -227
backend/main.py CHANGED
@@ -1,10 +1,4 @@
1
-
2
- # Fixed main.py
3
- main_py = '''import os
4
- import json
5
- import asyncio
6
- import subprocess
7
- import uuid
8
  from contextlib import asynccontextmanager
9
  from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect, BackgroundTasks
10
  from fastapi.responses import FileResponse, StreamingResponse
@@ -26,13 +20,9 @@ def get_codebase_index():
26
  from codebase_index import CodebaseIndex as CBI
27
  CodebaseIndex = CBI
28
  except ImportError:
29
- raise HTTPException(
30
- status_code=503,
31
- detail="Codebase indexing unavailable – rebuild with updated sentence-transformers"
32
- )
33
  return CodebaseIndex
34
 
35
-
36
  # Auto-index sandbox on startup
37
  async def startup_index():
38
  try:
@@ -43,20 +33,16 @@ async def startup_index():
43
  except Exception as e:
44
  print(f"Startup indexing failed: {e}")
45
 
46
-
47
  @asynccontextmanager
48
  async def lifespan(app: FastAPI):
49
  await startup_index()
50
  yield
51
 
52
-
53
  app = FastAPI(title="Codeki API", lifespan=lifespan)
54
 
55
- indexers: Dict[str, Any] = {}
56
- agents: Dict[str, CodingAgent] = {}
57
  STATIC_DIR = "static"
58
- SANDBOX_ROOT = "/app/sandbox"
59
-
60
 
61
  # ---------- API Models ----------
62
  class ChatRequest(BaseModel):
@@ -65,15 +51,13 @@ class ChatRequest(BaseModel):
65
  context_files: Optional[List[str]] = None
66
  project_path: str = "/app/sandbox"
67
 
68
-
69
  class AgentTaskRequest(BaseModel):
70
  prompt: str
71
  model: str = "openrouter-anthropic/claude-3-sonnet"
72
- project_path: str = "/app/sandbox"
73
  auto_approve: bool = False
74
  background: bool = False
75
 
76
-
77
  class AutocompleteRequest(BaseModel):
78
  prefix: str
79
  suffix: str = ""
@@ -82,101 +66,61 @@ class AutocompleteRequest(BaseModel):
82
  max_tokens: int = 64
83
  model: str = "groq-llama-3.1-8b-instant"
84
 
85
-
86
  class IndexRequest(BaseModel):
87
- project_path: str = "/app/sandbox"
88
-
89
 
90
  class BugFixRequest(BaseModel):
91
  file_path: str
92
  error_output: str
93
- project_path: str = "/app/sandbox"
94
-
95
 
96
  class ReviewRequest(BaseModel):
97
  pr_diff: str
98
- project_path: str = "/app/sandbox"
99
-
100
 
101
  class ApplyRequest(BaseModel):
102
  task_id: str
103
  approved_indices: List[int]
104
 
105
-
106
- class GitRequest(BaseModel):
107
- action: str
108
- project_path: str = "/app/sandbox"
109
- args: Optional[dict] = None
110
-
111
-
112
- def _validate_project_path(project_path: str) -> str:
113
- """Validate and resolve project path to prevent directory traversal."""
114
- real_path = os.path.realpath(project_path)
115
- real_sandbox = os.path.realpath(SANDBOX_ROOT)
116
- if not real_path.startswith(real_sandbox):
117
- raise HTTPException(status_code=403, detail="Access denied: path outside sandbox")
118
- if not os.path.exists(real_path):
119
- raise HTTPException(status_code=404, detail="Project path not found")
120
- return real_path
121
-
122
-
123
  # ---------- API Routes ----------
124
  @app.get("/api/models")
125
  async def get_models():
126
  return get_available_models()
127
 
128
-
129
  @app.post("/api/chat")
130
  async def chat(req: ChatRequest):
131
  context = ""
132
- safe_path = _validate_project_path(req.project_path)
133
-
134
  if req.context_files:
135
  for f in req.context_files:
136
  if f == "__git__":
 
137
  try:
138
- import git
139
- repo = git.Repo(safe_path)
140
  diff = repo.git.diff(repo.head.commit.tree) if repo.head.is_valid() else ""
141
- context += f"\\n--- Git Diff ---\\n{diff}\\n"
142
- except Exception:
143
  pass
144
  else:
145
- p = Path(safe_path) / f
146
- try:
147
- real_p = os.path.realpath(p)
148
- if not real_p.startswith(os.path.realpath(SANDBOX_ROOT)):
149
- continue
150
- if p.exists() and p.is_file():
151
- context += f"\\n--- {f} ---\\n{p.read_text(encoding='utf-8')}\\n"
152
- except Exception:
153
- pass
154
-
155
- messages = req.messages.copy()
156
- if context:
157
- messages.insert(0, {"role": "system", "content": f"Relevant codebase context:\\n{context}"})
158
-
159
  async def event_stream():
160
  try:
161
- async for token in route_chat(req.model, messages):
162
- yield f"data: {json.dumps({'token': token})}\\n\\n"
163
- yield "data: [DONE]\\n\\n"
164
  except Exception as e:
165
- yield f"data: {json.dumps({'token': f'ERROR: {str(e)}'})}\\n\\n"
166
- yield "data: [DONE]\\n\\n"
167
-
168
  return StreamingResponse(event_stream(), media_type="text/event-stream")
169
 
170
-
171
  @app.post("/api/agent/plan")
172
  async def agent_plan(req: AgentTaskRequest):
173
- safe_path = _validate_project_path(req.project_path)
174
- agent = CodingAgent(project_path=safe_path, model=req.model)
175
  changes = await agent.plan_task(req.prompt)
176
- task_id = str(uuid.uuid4())
177
- agents[task_id] = agent
178
- return {"task_id": task_id, "changes": changes}
179
-
180
 
181
  @app.post("/api/agent/apply")
182
  async def agent_apply(req: ApplyRequest):
@@ -186,11 +130,9 @@ async def agent_apply(req: ApplyRequest):
186
  await agent.apply_changes(req.approved_indices)
187
  return {"status": agent.status, "logs": agent.logs}
188
 
189
-
190
  @app.post("/api/agent/run")
191
  async def run_agent(req: AgentTaskRequest, background_tasks: BackgroundTasks):
192
- safe_path = _validate_project_path(req.project_path)
193
- agent = CodingAgent(project_path=safe_path, model=req.model)
194
  if req.background:
195
  task_id = str(uuid.uuid4())
196
  agents[task_id] = agent
@@ -199,226 +141,128 @@ async def run_agent(req: AgentTaskRequest, background_tasks: BackgroundTasks):
199
  else:
200
  async def event_stream():
201
  async for event in agent.execute_task_stream(req.prompt, auto_approve=req.auto_approve):
202
- yield f"data: {json.dumps(event)}\\n\\n"
203
- yield "data: [DONE]\\n\\n"
204
  return StreamingResponse(event_stream(), media_type="text/event-stream")
205
 
206
-
207
  @app.post("/api/autocomplete")
208
  async def autocomplete(req: AutocompleteRequest):
209
  suggestions = await generate_autocomplete(req)
210
  return {"completions": suggestions}
211
 
212
-
213
  @app.post("/api/index")
214
  async def index_project(req: IndexRequest):
215
- safe_path = _validate_project_path(req.project_path)
216
  CBI = get_codebase_index()
217
- idx = CBI(safe_path)
218
  idx.build_index()
219
- indexers[safe_path] = idx
220
  return {"status": "indexed", "files": len(idx.file_list)}
221
 
222
-
223
  @app.post("/api/search")
224
  async def search_codebase(query: str, project_path: str = "/app/sandbox", top_k: int = 5):
225
- safe_path = _validate_project_path(project_path)
226
- if safe_path not in indexers:
227
  CBI = get_codebase_index()
228
- idx = CBI(safe_path)
229
  try:
230
  idx.build_index()
231
  except Exception as e:
232
  raise HTTPException(503, f"Indexing failed: {str(e)}")
233
- indexers[safe_path] = idx
234
-
235
- idx = indexers[safe_path]
236
  results = idx.search(query, top_k)
237
  return {"results": results}
238
 
239
-
240
  @app.post("/api/bugfix")
241
  async def bugfix(req: BugFixRequest):
242
- safe_path = _validate_project_path(req.project_path)
243
- agent = CodingAgent(
244
- project_path=safe_path,
245
- model="openrouter-anthropic/claude-3-sonnet"
246
- )
247
  result = await agent.fix_error(req.file_path, req.error_output)
248
  return result
249
 
250
-
251
  @app.post("/api/review")
252
  async def review_code(req: ReviewRequest):
253
- safe_path = _validate_project_path(req.project_path)
254
- agent = CodingAgent(
255
- project_path=safe_path,
256
- model="openrouter-anthropic/claude-3-sonnet"
257
- )
258
  review = await agent.review_pr(req.pr_diff)
259
  return {"review": review}
260
 
261
-
262
  @app.post("/api/git")
263
- async def git_operation(req: GitRequest):
264
- safe_path = _validate_project_path(req.project_path)
265
  try:
266
- import git
267
- repo = git.Repo(safe_path)
268
- args = req.args or {}
269
-
270
- if req.action == "commit":
271
  repo.git.add(A=True)
272
  repo.git.commit(m=args.get("message", "auto commit"))
273
- return {"status": "committed"}
274
-
275
- elif req.action == "log":
276
  logs = list(repo.iter_commits(max_count=10))
277
- return {
278
- "log": [
279
- {
280
- "message": c.message.strip(),
281
- "hash": c.hexsha,
282
- "author": str(c.author),
283
- "date": str(c.committed_datetime),
284
- }
285
- for c in logs
286
- ]
287
- }
288
-
289
- elif req.action == "branch":
290
  branches = [b.name for b in repo.branches]
291
  return {"branches": branches}
292
-
293
- elif req.action == "diff":
294
- diff_output = ""
295
- if repo.head.is_valid():
296
- diff_output = repo.git.diff(repo.head.commit.tree)
297
  return {"diff": diff_output}
298
-
299
- elif req.action == "status":
300
- return {"status": repo.git.status()}
301
-
302
  else:
303
- raise HTTPException(400, f"Unknown git action: {req.action}")
304
-
305
  except Exception as e:
306
- raise HTTPException(400, detail=str(e))
307
-
308
 
309
  @app.websocket("/ws/terminal")
310
  async def terminal_ws(websocket: WebSocket):
311
  await websocket.accept()
312
  proc = None
313
-
314
- async def read_stream(stream, websocket: WebSocket):
315
- try:
316
- while True:
317
- line = await stream.readline()
318
- if not line:
319
- break
320
- await websocket.send_text(line.decode(errors='replace'))
321
- except Exception:
322
- pass
323
-
324
  try:
325
  while True:
326
  cmd = await websocket.receive_text()
327
-
328
  if cmd.startswith("exec:"):
329
  command = cmd[5:]
330
  proc = await asyncio.create_subprocess_shell(
331
  command,
332
  stdout=asyncio.subprocess.PIPE,
333
  stderr=asyncio.subprocess.PIPE,
334
- cwd=SANDBOX_ROOT,
335
- )
336
- # Stream stdout and stderr concurrently
337
- await asyncio.gather(
338
- read_stream(proc.stdout, websocket),
339
- read_stream(proc.stderr, websocket),
340
  )
341
- await proc.wait()
342
- await websocket.send_text(f"\\n[exit code: {proc.returncode}]\\n")
343
- proc = None
344
-
345
- elif cmd == "ctrl-c" and proc and proc.returncode is None:
346
  proc.terminate()
347
- try:
348
- await asyncio.wait_for(proc.wait(), timeout=2.0)
349
- except asyncio.TimeoutError:
350
- proc.kill()
351
- proc = None
352
-
353
  except WebSocketDisconnect:
354
  pass
355
- finally:
356
- if proc and proc.returncode is None:
357
- proc.kill()
358
- try:
359
- await asyncio.wait_for(proc.wait(), timeout=1.0)
360
- except asyncio.TimeoutError:
361
- pass
362
-
363
 
364
  @app.get("/api/agent/tasks")
365
  async def list_agent_tasks():
366
  return {"tasks": list(agents.keys())}
367
 
368
-
369
- @app.get("/api/agent/task/{task_id}")
370
  async def get_agent_status(task_id: str):
371
  agent = agents.get(task_id)
372
  if not agent:
373
  raise HTTPException(404, "Task not found")
374
  return {"status": agent.status, "logs": agent.logs}
375
 
376
-
377
  @app.get("/api/files")
378
  async def list_files(project_path: str = "/app/sandbox"):
379
- safe_path = _validate_project_path(project_path)
380
-
381
- def build_tree(path: str):
382
  tree = []
383
- try:
384
- for item in sorted(os.listdir(path)):
385
- if item.startswith('.') or item == '__pycache__':
386
- continue
387
- full = os.path.join(path, item)
388
- real_full = os.path.realpath(full)
389
- if not real_full.startswith(os.path.realpath(SANDBOX_ROOT)):
390
- continue
391
- node = {
392
- "name": item,
393
- "path": full.replace(safe_path, "").lstrip("/"),
394
- "type": "folder" if os.path.isdir(full) else "file",
395
- }
396
- if os.path.isdir(full):
397
- node["children"] = build_tree(full)
398
- tree.append(node)
399
- except PermissionError:
400
- pass
401
  return tree
402
-
403
  try:
404
- tree = build_tree(safe_path)
405
  return tree
406
  except Exception as e:
407
  raise HTTPException(400, str(e))
408
 
409
-
410
- # ---------- Serve Frontend ----------
411
- @app.get("/")
412
- async def serve_index():
413
- index_path = os.path.join(STATIC_DIR, "index.html")
414
- if os.path.isfile(index_path):
415
- return FileResponse(index_path)
416
- raise HTTPException(404, detail="Frontend not built")
417
-
418
-
419
  @app.get("/{full_path:path}")
420
  async def serve_frontend(full_path: str):
421
- if full_path.startswith("api/") or full_path.startswith("ws/"):
422
  raise HTTPException(404)
423
  file_path = os.path.join(STATIC_DIR, full_path)
424
  if os.path.isfile(file_path):
@@ -428,12 +272,6 @@ async def serve_frontend(full_path: str):
428
  return FileResponse(index_path)
429
  raise HTTPException(404, detail="Not found")
430
 
431
-
432
  if __name__ == "__main__":
433
  import uvicorn
434
- uvicorn.run(app, host="0.0.0.0", port=7860)
435
- '''
436
-
437
- with open('/mnt/agents/output/main.py', 'w') as f:
438
- f.write(main_py)
439
- print("main.py written successfully")
 
1
+ import os, json, asyncio, subprocess, uuid
 
 
 
 
 
 
2
  from contextlib import asynccontextmanager
3
  from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect, BackgroundTasks
4
  from fastapi.responses import FileResponse, StreamingResponse
 
20
  from codebase_index import CodebaseIndex as CBI
21
  CodebaseIndex = CBI
22
  except ImportError:
23
+ raise HTTPException(status_code=503, detail="Codebase indexing unavailable – rebuild with updated sentence-transformers")
 
 
 
24
  return CodebaseIndex
25
 
 
26
  # Auto-index sandbox on startup
27
  async def startup_index():
28
  try:
 
33
  except Exception as e:
34
  print(f"Startup indexing failed: {e}")
35
 
 
36
  @asynccontextmanager
37
  async def lifespan(app: FastAPI):
38
  await startup_index()
39
  yield
40
 
 
41
  app = FastAPI(title="Codeki API", lifespan=lifespan)
42
 
43
+ indexers = {}
44
+ agents = {}
45
  STATIC_DIR = "static"
 
 
46
 
47
  # ---------- API Models ----------
48
  class ChatRequest(BaseModel):
 
51
  context_files: Optional[List[str]] = None
52
  project_path: str = "/app/sandbox"
53
 
 
54
  class AgentTaskRequest(BaseModel):
55
  prompt: str
56
  model: str = "openrouter-anthropic/claude-3-sonnet"
57
+ project_path: str
58
  auto_approve: bool = False
59
  background: bool = False
60
 
 
61
  class AutocompleteRequest(BaseModel):
62
  prefix: str
63
  suffix: str = ""
 
66
  max_tokens: int = 64
67
  model: str = "groq-llama-3.1-8b-instant"
68
 
 
69
  class IndexRequest(BaseModel):
70
+ project_path: str
 
71
 
72
  class BugFixRequest(BaseModel):
73
  file_path: str
74
  error_output: str
75
+ project_path: str
 
76
 
77
  class ReviewRequest(BaseModel):
78
  pr_diff: str
79
+ project_path: str
 
80
 
81
  class ApplyRequest(BaseModel):
82
  task_id: str
83
  approved_indices: List[int]
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  # ---------- API Routes ----------
86
  @app.get("/api/models")
87
  async def get_models():
88
  return get_available_models()
89
 
 
90
  @app.post("/api/chat")
91
  async def chat(req: ChatRequest):
92
  context = ""
 
 
93
  if req.context_files:
94
  for f in req.context_files:
95
  if f == "__git__":
96
+ import git
97
  try:
98
+ repo = git.Repo(req.project_path)
 
99
  diff = repo.git.diff(repo.head.commit.tree) if repo.head.is_valid() else ""
100
+ context += f"\n--- Git Diff ---\n{diff}\n"
101
+ except:
102
  pass
103
  else:
104
+ p = Path(req.project_path) / f
105
+ if p.exists():
106
+ context += f"\n--- {f} ---\n{p.read_text()}\n"
107
+ req.messages.insert(0, {"role": "system", "content": f"Relevant codebase context:\n{context}"})
 
 
 
 
 
 
 
 
 
 
108
  async def event_stream():
109
  try:
110
+ async for token in route_chat(req.model, req.messages):
111
+ yield f"data: {json.dumps({'token': token})}\n\n"
112
+ yield "data: [DONE]\n\n"
113
  except Exception as e:
114
+ yield f"data: {json.dumps({'token': f'ERROR: {str(e)}'})}\n\n"
115
+ yield "data: [DONE]\n\n"
 
116
  return StreamingResponse(event_stream(), media_type="text/event-stream")
117
 
 
118
  @app.post("/api/agent/plan")
119
  async def agent_plan(req: AgentTaskRequest):
120
+ agent = CodingAgent(project_path=req.project_path, model=req.model)
 
121
  changes = await agent.plan_task(req.prompt)
122
+ agents["default"] = agent
123
+ return {"changes": changes}
 
 
124
 
125
  @app.post("/api/agent/apply")
126
  async def agent_apply(req: ApplyRequest):
 
130
  await agent.apply_changes(req.approved_indices)
131
  return {"status": agent.status, "logs": agent.logs}
132
 
 
133
  @app.post("/api/agent/run")
134
  async def run_agent(req: AgentTaskRequest, background_tasks: BackgroundTasks):
135
+ agent = CodingAgent(project_path=req.project_path, model=req.model)
 
136
  if req.background:
137
  task_id = str(uuid.uuid4())
138
  agents[task_id] = agent
 
141
  else:
142
  async def event_stream():
143
  async for event in agent.execute_task_stream(req.prompt, auto_approve=req.auto_approve):
144
+ yield f"data: {json.dumps(event)}\n\n"
145
+ yield "data: [DONE]\n\n"
146
  return StreamingResponse(event_stream(), media_type="text/event-stream")
147
 
 
148
  @app.post("/api/autocomplete")
149
  async def autocomplete(req: AutocompleteRequest):
150
  suggestions = await generate_autocomplete(req)
151
  return {"completions": suggestions}
152
 
 
153
  @app.post("/api/index")
154
  async def index_project(req: IndexRequest):
 
155
  CBI = get_codebase_index()
156
+ idx = CBI(req.project_path)
157
  idx.build_index()
158
+ indexers[req.project_path] = idx
159
  return {"status": "indexed", "files": len(idx.file_list)}
160
 
 
161
  @app.post("/api/search")
162
  async def search_codebase(query: str, project_path: str = "/app/sandbox", top_k: int = 5):
163
+ if project_path not in indexers:
 
164
  CBI = get_codebase_index()
165
+ idx = CBI(project_path)
166
  try:
167
  idx.build_index()
168
  except Exception as e:
169
  raise HTTPException(503, f"Indexing failed: {str(e)}")
170
+ indexers[project_path] = idx
171
+ idx = indexers[project_path]
 
172
  results = idx.search(query, top_k)
173
  return {"results": results}
174
 
 
175
  @app.post("/api/bugfix")
176
  async def bugfix(req: BugFixRequest):
177
+ agent = CodingAgent(project_path=req.project_path, model="openrouter-anthropic/claude-3-sonnet")
 
 
 
 
178
  result = await agent.fix_error(req.file_path, req.error_output)
179
  return result
180
 
 
181
  @app.post("/api/review")
182
  async def review_code(req: ReviewRequest):
183
+ agent = CodingAgent(project_path=req.project_path, model="openrouter-anthropic/claude-3-sonnet")
 
 
 
 
184
  review = await agent.review_pr(req.pr_diff)
185
  return {"review": review}
186
 
 
187
  @app.post("/api/git")
188
+ async def git_operation(action: str, project_path: str, args: Optional[dict] = None):
189
+ import git
190
  try:
191
+ repo = git.Repo(project_path)
192
+ if action == "commit":
 
 
 
193
  repo.git.add(A=True)
194
  repo.git.commit(m=args.get("message", "auto commit"))
195
+ elif action == "log":
 
 
196
  logs = list(repo.iter_commits(max_count=10))
197
+ return {"log": [{"message": c.message, "hash": c.hexsha} for c in logs]}
198
+ elif action == "branch":
 
 
 
 
 
 
 
 
 
 
 
199
  branches = [b.name for b in repo.branches]
200
  return {"branches": branches}
201
+ elif action == "diff":
202
+ diff_output = repo.git.diff(repo.head.commit.tree) if repo.head.is_valid() else ""
 
 
 
203
  return {"diff": diff_output}
 
 
 
 
204
  else:
205
+ raise HTTPException(400, "Unknown action")
 
206
  except Exception as e:
207
+ raise HTTPException(400, str(e))
 
208
 
209
  @app.websocket("/ws/terminal")
210
  async def terminal_ws(websocket: WebSocket):
211
  await websocket.accept()
212
  proc = None
 
 
 
 
 
 
 
 
 
 
 
213
  try:
214
  while True:
215
  cmd = await websocket.receive_text()
 
216
  if cmd.startswith("exec:"):
217
  command = cmd[5:]
218
  proc = await asyncio.create_subprocess_shell(
219
  command,
220
  stdout=asyncio.subprocess.PIPE,
221
  stderr=asyncio.subprocess.PIPE,
222
+ cwd="/app/sandbox"
 
 
 
 
 
223
  )
224
+ stdout, stderr = await proc.communicate()
225
+ output = stdout.decode() + stderr.decode()
226
+ await websocket.send_text(output)
227
+ elif cmd == "ctrl-c" and proc:
 
228
  proc.terminate()
 
 
 
 
 
 
229
  except WebSocketDisconnect:
230
  pass
 
 
 
 
 
 
 
 
231
 
232
  @app.get("/api/agent/tasks")
233
  async def list_agent_tasks():
234
  return {"tasks": list(agents.keys())}
235
 
236
+ @app.post("/api/agent/task/{task_id}")
 
237
  async def get_agent_status(task_id: str):
238
  agent = agents.get(task_id)
239
  if not agent:
240
  raise HTTPException(404, "Task not found")
241
  return {"status": agent.status, "logs": agent.logs}
242
 
 
243
  @app.get("/api/files")
244
  async def list_files(project_path: str = "/app/sandbox"):
245
+ def build_tree(path):
 
 
246
  tree = []
247
+ for item in sorted(os.listdir(path)):
248
+ if item.startswith('.') or item == '__pycache__':
249
+ continue
250
+ full = os.path.join(path, item)
251
+ node = {"name": item, "path": full.replace(project_path, "").lstrip("/"), "type": "folder" if os.path.isdir(full) else "file"}
252
+ if os.path.isdir(full):
253
+ node["children"] = build_tree(full)
254
+ tree.append(node)
 
 
 
 
 
 
 
 
 
 
255
  return tree
 
256
  try:
257
+ tree = build_tree(project_path)
258
  return tree
259
  except Exception as e:
260
  raise HTTPException(400, str(e))
261
 
262
+ # ---------- Serve Frontend (catch-all, must be last) ----------
 
 
 
 
 
 
 
 
 
263
  @app.get("/{full_path:path}")
264
  async def serve_frontend(full_path: str):
265
+ if full_path.startswith("api"):
266
  raise HTTPException(404)
267
  file_path = os.path.join(STATIC_DIR, full_path)
268
  if os.path.isfile(file_path):
 
272
  return FileResponse(index_path)
273
  raise HTTPException(404, detail="Not found")
274
 
 
275
  if __name__ == "__main__":
276
  import uvicorn
277
+ uvicorn.run(app, host="0.0.0.0", port=7860)