Dwarakesh-V commited on
Commit
c06e529
·
1 Parent(s): 5b9fb0b

switched mongodb to json to deploy on hf spaces

Browse files
backend/__pycache__/app.cpython-311.pyc CHANGED
Binary files a/backend/__pycache__/app.cpython-311.pyc and b/backend/__pycache__/app.cpython-311.pyc differ
 
backend/app.py CHANGED
@@ -2,47 +2,73 @@ import asyncio
2
  import json
3
  import subprocess
4
  from pathlib import Path
5
- from datetime import datetime, timezone, timedelta
6
-
7
  from fastapi import FastAPI, HTTPException
8
  from fastapi.middleware.cors import CORSMiddleware
9
- from fastapi.staticfiles import StaticFiles
10
  from fastapi.responses import FileResponse
11
-
12
- from odmantic import AIOEngine
13
- from models.message import Message
14
 
15
  # --------------------------------------------------------
16
- # FASTAPI APP
17
  # --------------------------------------------------------
18
 
19
  app = FastAPI()
20
 
21
  app.add_middleware(
22
  CORSMiddleware,
23
- allow_origins=["*"], # change this in production
 
24
  allow_methods=["*"],
25
  allow_headers=["*"],
26
- allow_credentials=True,
27
  )
28
 
29
  # --------------------------------------------------------
30
- # DATABASE (ODMantic)
31
  # --------------------------------------------------------
32
 
33
- engine = AIOEngine(database="semantic_chat")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  # --------------------------------------------------------
36
- # PYTHON ENGINE PROCESS
37
  # --------------------------------------------------------
38
 
39
- python_engine_path = Path(__file__).parent.parent / "python_engine/engine_server.py"
40
 
41
  python = subprocess.Popen(
42
- [
43
- "/home/dwarakesh/base/bin/python3",
44
- str(python_engine_path)
45
- ],
46
  stdin=subprocess.PIPE,
47
  stdout=subprocess.PIPE,
48
  stderr=subprocess.PIPE,
@@ -53,8 +79,9 @@ python = subprocess.Popen(
53
  ready = False
54
  callbacks = []
55
 
 
56
  # --------------------------------------------------------
57
- # BACKGROUND READERS FOR PYTHON ENGINE
58
  # --------------------------------------------------------
59
 
60
  async def read_stdout():
@@ -78,7 +105,7 @@ async def read_stdout():
78
 
79
 
80
  async def read_stderr():
81
- """Reads stderr for READY signal + logs."""
82
  global ready
83
  loop = asyncio.get_event_loop()
84
 
@@ -101,7 +128,7 @@ async def startup_event():
101
 
102
 
103
  # --------------------------------------------------------
104
- # SEND MESSAGE TO PYTHON ENGINE
105
  # --------------------------------------------------------
106
 
107
  async def ask_python(query: str):
@@ -130,80 +157,57 @@ async def ask_route(payload: dict):
130
  raise HTTPException(400, "Missing 'query' field")
131
 
132
  # Save user message
133
- await engine.save(Message(role="user", text=user_query))
134
 
135
  # Engine response
136
  result = await ask_python(user_query)
137
 
138
- # Save bot messages
139
  if isinstance(result.get("response"), list):
140
  for msg in result["response"]:
141
- await engine.save(Message(role="bot", text=msg))
142
 
143
  return result
144
 
145
 
146
  @app.get("/history")
147
  async def history_route():
148
- msgs = await engine.find(Message, sort=Message.timestamp)
149
- # Make sure timestamps are serialized as ISO strings
150
- return [
151
- {
152
- "role": msg.role,
153
- "text": msg.text,
154
- "src": getattr(msg, 'src', None),
155
- "timestamp": msg.timestamp.isoformat() if isinstance(msg.timestamp, datetime) else msg.timestamp
156
- }
157
- for msg in msgs
158
- ]
159
 
160
  @app.post("/clear")
161
  async def clear_route():
162
- # wipe all docs
163
- await engine.remove(Message, {})
164
-
165
- # insert greeting with current UTC time
166
- greeting = Message(
167
- role="bot",
168
- text="Hi. I am Dwarakesh. Ask me anything about myself.",
169
- timestamp=datetime.now(timezone(timedelta(hours=5, minutes=30))).replace(tzinfo=None)
170
- )
171
- await engine.save(greeting)
172
-
173
- return {
174
- "success": True,
175
- "message": {
176
- "role": greeting.role,
177
- "text": greeting.text,
178
- "timestamp": greeting.timestamp.isoformat()
179
- }
180
  }
 
 
 
 
181
 
182
  # --------------------------------------------------------
183
- # STATIC FILE SERVING (REACT BUILD)
184
  # --------------------------------------------------------
185
 
 
 
 
186
 
187
- DIST_DIR = Path(__file__).parent.parent / "frontend/dist"
 
188
 
189
- # /assets/*
190
- from fastapi.staticfiles import StaticFiles
191
-
192
- app.mount("/static", StaticFiles(directory="../frontend/public"), name="static")
193
- app.mount("/assets", StaticFiles(directory=str(DIST_DIR / "assets")), name="assets")
194
 
195
- # /favicon.png
196
  @app.get("/favicon.png")
197
  async def favicon():
198
- return FileResponse(str(DIST_DIR / "favicon.png"))
199
 
200
- # SPA fallback everything else → index.html
201
  @app.get("/{full_path:path}")
202
  async def serve_spa(full_path: str):
203
- return FileResponse(str(DIST_DIR / "index.html"))
204
-
205
- # --------------------------------------------------------
206
- # Run with:
207
- # cd backend
208
- # uvicorn app:app --reload --port 8000
209
- # --------------------------------------------------------
 
2
  import json
3
  import subprocess
4
  from pathlib import Path
5
+ from datetime import datetime
 
6
  from fastapi import FastAPI, HTTPException
7
  from fastapi.middleware.cors import CORSMiddleware
 
8
  from fastapi.responses import FileResponse
9
+ from fastapi.staticfiles import StaticFiles
 
 
10
 
11
  # --------------------------------------------------------
12
+ # FASTAPI INIT
13
  # --------------------------------------------------------
14
 
15
  app = FastAPI()
16
 
17
  app.add_middleware(
18
  CORSMiddleware,
19
+ allow_origins=["*"], # allow all for HF Spaces
20
+ allow_credentials=True,
21
  allow_methods=["*"],
22
  allow_headers=["*"],
 
23
  )
24
 
25
  # --------------------------------------------------------
26
+ # JSON DATABASE (persistent message storage)
27
  # --------------------------------------------------------
28
 
29
+ ROOT_DIR = Path(__file__).parent.parent
30
+ DATA_DIR = ROOT_DIR / "data"
31
+ DATA_DIR.mkdir(exist_ok=True)
32
+
33
+ DB_FILE = DATA_DIR / "history.json"
34
+
35
+
36
+ def load_messages():
37
+ if not DB_FILE.exists():
38
+ return []
39
+ try:
40
+ with open(DB_FILE, "r") as f:
41
+ return json.load(f)
42
+ except:
43
+ return []
44
+
45
+
46
+ def save_messages(msgs):
47
+ with open(DB_FILE, "w") as f:
48
+ json.dump(msgs, f, indent=2)
49
+
50
+
51
+ def add_message(role, text, src=None):
52
+ msgs = load_messages()
53
+ entry = {
54
+ "role": role,
55
+ "text": text,
56
+ "src": src,
57
+ "timestamp": datetime.utcnow().isoformat()
58
+ }
59
+ msgs.append(entry)
60
+ save_messages(msgs)
61
+ return entry
62
+
63
 
64
  # --------------------------------------------------------
65
+ # PYTHON ENGINE SPAWN
66
  # --------------------------------------------------------
67
 
68
+ python_engine_path = ROOT_DIR / "python_engine" / "engine_server.py"
69
 
70
  python = subprocess.Popen(
71
+ ["python3", str(python_engine_path)], # Docker-safe, HF-safe
 
 
 
72
  stdin=subprocess.PIPE,
73
  stdout=subprocess.PIPE,
74
  stderr=subprocess.PIPE,
 
79
  ready = False
80
  callbacks = []
81
 
82
+
83
  # --------------------------------------------------------
84
+ # ENGINE ASYNC READERS
85
  # --------------------------------------------------------
86
 
87
  async def read_stdout():
 
105
 
106
 
107
  async def read_stderr():
108
+ """Reads stderr for logs and READY signal."""
109
  global ready
110
  loop = asyncio.get_event_loop()
111
 
 
128
 
129
 
130
  # --------------------------------------------------------
131
+ # SEND QUERY TO PYTHON ENGINE
132
  # --------------------------------------------------------
133
 
134
  async def ask_python(query: str):
 
157
  raise HTTPException(400, "Missing 'query' field")
158
 
159
  # Save user message
160
+ add_message("user", user_query)
161
 
162
  # Engine response
163
  result = await ask_python(user_query)
164
 
165
+ # Save bot responses
166
  if isinstance(result.get("response"), list):
167
  for msg in result["response"]:
168
+ add_message("bot", msg)
169
 
170
  return result
171
 
172
 
173
  @app.get("/history")
174
  async def history_route():
175
+ return load_messages()
176
+
 
 
 
 
 
 
 
 
 
177
 
178
  @app.post("/clear")
179
  async def clear_route():
180
+ msgs = []
181
+ greeting = {
182
+ "role": "bot",
183
+ "text": "Hi. I am Dwarakesh. Ask me anything about myself.",
184
+ "timestamp": datetime.utcnow().isoformat()
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  }
186
+ msgs.append(greeting)
187
+ save_messages(msgs)
188
+ return {"success": True, "message": greeting}
189
+
190
 
191
  # --------------------------------------------------------
192
+ # STATIC FRONTEND (Vite build)
193
  # --------------------------------------------------------
194
 
195
+ FRONTEND_DIR = ROOT_DIR / "frontend"
196
+ DIST_DIR = FRONTEND_DIR / "dist"
197
+ PUBLIC_DIR = FRONTEND_DIR / "public"
198
 
199
+ # /static/* public folder
200
+ app.mount("/static", StaticFiles(directory=PUBLIC_DIR), name="static")
201
 
202
+ # /assets/* → dist/assets
203
+ app.mount("/assets", StaticFiles(directory=DIST_DIR / "assets"), name="assets")
 
 
 
204
 
205
+ # favicon
206
  @app.get("/favicon.png")
207
  async def favicon():
208
+ return FileResponse(DIST_DIR / "favicon.png")
209
 
210
+ # Serve frontend for all other routes
211
  @app.get("/{full_path:path}")
212
  async def serve_spa(full_path: str):
213
+ return FileResponse(DIST_DIR / "index.html")
 
 
 
 
 
 
backend/server.py CHANGED
@@ -1,36 +1,74 @@
1
- # main.py
2
  import asyncio
3
  import json
 
 
 
4
  from fastapi import FastAPI, HTTPException
5
  from fastapi.middleware.cors import CORSMiddleware
6
- from odmantic import AIOEngine
7
- from models.message import Message
8
- from pathlib import Path
9
- from datetime import datetime, timezone, timedelta
10
- import subprocess
 
11
 
12
  app = FastAPI()
13
 
14
  app.add_middleware(
15
  CORSMiddleware,
16
- allow_origins=["*"],
17
  allow_credentials=True,
18
  allow_methods=["*"],
19
  allow_headers=["*"],
20
  )
21
 
22
- # ----------------------------------------
23
- # MongoDB (ODMantic)
24
- # ----------------------------------------
25
- engine = AIOEngine(database="semantic_chat")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- # ----------------------------------------
28
- # Spawn Python engine
29
- # ----------------------------------------
30
- python_engine_path = Path(__file__).parent / "../python_engine/engine_server.py"
31
 
32
  python = subprocess.Popen(
33
- ["/home/dwarakesh/base/bin/python3", str(python_engine_path)],
34
  stdin=subprocess.PIPE,
35
  stdout=subprocess.PIPE,
36
  stderr=subprocess.PIPE,
@@ -39,34 +77,43 @@ python = subprocess.Popen(
39
  )
40
 
41
  ready = False
42
- callbacks = [] # simple FIFO queue
43
 
44
 
45
- # ----------------------------------------
46
- # Background task: read stdout + stderr
47
- # ----------------------------------------
48
  async def read_stdout():
 
49
  global callbacks
50
  loop = asyncio.get_event_loop()
 
51
  while True:
52
  line = await loop.run_in_executor(None, python.stdout.readline)
53
  if not line:
54
  continue
 
55
  line = line.strip()
56
  if callbacks:
57
  cb = callbacks.pop(0)
58
- cb(json.loads(line))
 
 
 
 
59
 
60
 
61
  async def read_stderr():
 
62
  global ready
63
  loop = asyncio.get_event_loop()
 
64
  while True:
65
  line = await loop.run_in_executor(None, python.stderr.readline)
66
  if not line:
67
  continue
68
- print("[PY]", line.strip())
69
- if "READY" in line:
 
 
 
70
  ready = True
71
 
72
 
@@ -76,76 +123,83 @@ async def startup_event():
76
  asyncio.create_task(read_stderr())
77
 
78
 
79
- # ----------------------------------------
80
- # Helper to send message to python engine
81
- # ----------------------------------------
82
  async def ask_python(query: str):
83
  if not ready:
84
- raise HTTPException(status_code=500, detail="Python engine not ready")
85
 
86
  loop = asyncio.get_event_loop()
87
- fut = loop.create_future()
 
 
88
 
89
- callbacks.append(fut.set_result)
90
  python.stdin.write(json.dumps({"query": query}) + "\n")
91
  python.stdin.flush()
92
 
93
- return await fut
94
 
95
 
96
- # ----------------------------------------
97
- # /ask
98
- # ----------------------------------------
 
99
  @app.post("/ask")
100
  async def ask(payload: dict):
101
  user_query = payload.get("query")
102
  if not user_query:
103
- raise HTTPException(400, "Missing query field")
104
 
105
- # store user message
106
- await engine.save(Message(role="user", text=user_query))
107
 
 
108
  result = await ask_python(user_query)
109
 
110
- # store bot messages
111
  if isinstance(result.get("response"), list):
112
  for msg in result["response"]:
113
- await engine.save(Message(role="bot", text=msg))
114
 
115
  return result
116
 
117
 
118
- # ----------------------------------------
119
- # /history
120
- # ----------------------------------------
121
  @app.get("/history")
122
  async def history():
123
- messages = await engine.find(Message, sort=Message.timestamp)
124
- return messages
125
-
126
 
127
- # ----------------------------------------
128
- # /clear
129
- # ----------------------------------------
130
 
131
  @app.post("/clear")
132
  async def clear_route():
133
- # wipe all docs
134
- await engine.remove(Message, {})
135
-
136
- # Use local timezone, same as JavaScript
137
- greeting = Message(
138
- role="bot",
139
- text="Hi. I am Dwarakesh. Ask me anything.",
140
- timestamp=datetime.now(timezone(timedelta(hours=5, minutes=30))).replace(tzinfo=None)
141
- )
142
- await engine.save(greeting)
143
-
144
- return {
145
- "success": True,
146
- "message": {
147
- "role": greeting.role,
148
- "text": greeting.text,
149
- "timestamp": greeting.timestamp.isoformat()
150
- }
151
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import asyncio
2
  import json
3
+ import subprocess
4
+ from pathlib import Path
5
+ from datetime import datetime
6
  from fastapi import FastAPI, HTTPException
7
  from fastapi.middleware.cors import CORSMiddleware
8
+ from fastapi.responses import FileResponse
9
+ from fastapi.staticfiles import StaticFiles
10
+
11
+ # --------------------------------------------------------
12
+ # FASTAPI INIT
13
+ # --------------------------------------------------------
14
 
15
  app = FastAPI()
16
 
17
  app.add_middleware(
18
  CORSMiddleware,
19
+ allow_origins=["*"], # allow all for HF Spaces
20
  allow_credentials=True,
21
  allow_methods=["*"],
22
  allow_headers=["*"],
23
  )
24
 
25
+ # --------------------------------------------------------
26
+ # JSON DATABASE (file-based persistence)
27
+ # --------------------------------------------------------
28
+
29
+ ROOT_DIR = Path(__file__).parent.parent
30
+ DATA_DIR = ROOT_DIR / "data"
31
+ DATA_DIR.mkdir(exist_ok=True)
32
+
33
+ DB_FILE = DATA_DIR / "history.json"
34
+
35
+
36
+ def load_messages():
37
+ if not DB_FILE.exists():
38
+ return []
39
+ try:
40
+ with open(DB_FILE, "r") as f:
41
+ return json.load(f)
42
+ except:
43
+ return []
44
+
45
+
46
+ def save_messages(msgs):
47
+ with open(DB_FILE, "w") as f:
48
+ json.dump(msgs, f, indent=2)
49
+
50
+
51
+ def add_message(role, text, src=None):
52
+ msgs = load_messages()
53
+ entry = {
54
+ "role": role,
55
+ "text": text,
56
+ "src": src,
57
+ "timestamp": datetime.utcnow().isoformat()
58
+ }
59
+ msgs.append(entry)
60
+ save_messages(msgs)
61
+ return entry
62
+
63
+
64
+ # --------------------------------------------------------
65
+ # PYTHON ENGINE
66
+ # --------------------------------------------------------
67
 
68
+ python_engine_path = ROOT_DIR / "python_engine" / "engine_server.py"
 
 
 
69
 
70
  python = subprocess.Popen(
71
+ ["python3", str(python_engine_path)], # Docker/HF safe
72
  stdin=subprocess.PIPE,
73
  stdout=subprocess.PIPE,
74
  stderr=subprocess.PIPE,
 
77
  )
78
 
79
  ready = False
80
+ callbacks = []
81
 
82
 
 
 
 
83
  async def read_stdout():
84
+ """Reads JSON responses from python engine."""
85
  global callbacks
86
  loop = asyncio.get_event_loop()
87
+
88
  while True:
89
  line = await loop.run_in_executor(None, python.stdout.readline)
90
  if not line:
91
  continue
92
+
93
  line = line.strip()
94
  if callbacks:
95
  cb = callbacks.pop(0)
96
+ try:
97
+ cb(json.loads(line))
98
+ except Exception as err:
99
+ print("JSON decode error:", err)
100
+ print("Line:", line)
101
 
102
 
103
  async def read_stderr():
104
+ """Reads stderr for READY signal + logs."""
105
  global ready
106
  loop = asyncio.get_event_loop()
107
+
108
  while True:
109
  line = await loop.run_in_executor(None, python.stderr.readline)
110
  if not line:
111
  continue
112
+
113
+ msg = line.strip()
114
+ print("[PY]", msg)
115
+
116
+ if "READY" in msg:
117
  ready = True
118
 
119
 
 
123
  asyncio.create_task(read_stderr())
124
 
125
 
 
 
 
126
  async def ask_python(query: str):
127
  if not ready:
128
+ raise HTTPException(503, "Python engine not ready")
129
 
130
  loop = asyncio.get_event_loop()
131
+ future = loop.create_future()
132
+
133
+ callbacks.append(future.set_result)
134
 
 
135
  python.stdin.write(json.dumps({"query": query}) + "\n")
136
  python.stdin.flush()
137
 
138
+ return await future
139
 
140
 
141
+ # --------------------------------------------------------
142
+ # API ROUTES
143
+ # --------------------------------------------------------
144
+
145
  @app.post("/ask")
146
  async def ask(payload: dict):
147
  user_query = payload.get("query")
148
  if not user_query:
149
+ raise HTTPException(400, "Missing 'query' field")
150
 
151
+ # Save user message
152
+ add_message("user", user_query)
153
 
154
+ # Engine response
155
  result = await ask_python(user_query)
156
 
157
+ # Save bot messages
158
  if isinstance(result.get("response"), list):
159
  for msg in result["response"]:
160
+ add_message("bot", msg)
161
 
162
  return result
163
 
164
 
 
 
 
165
  @app.get("/history")
166
  async def history():
167
+ return load_messages()
 
 
168
 
 
 
 
169
 
170
  @app.post("/clear")
171
  async def clear_route():
172
+ msgs = []
173
+ greeting = {
174
+ "role": "bot",
175
+ "text": "Hi. I am Dwarakesh. Ask me anything.",
176
+ "timestamp": datetime.utcnow().isoformat()
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  }
178
+ msgs.append(greeting)
179
+ save_messages(msgs)
180
+ return {"success": True, "message": greeting}
181
+
182
+
183
+ # --------------------------------------------------------
184
+ # STATIC FRONTEND (Vite build)
185
+ # --------------------------------------------------------
186
+
187
+ FRONTEND_DIR = ROOT_DIR / "frontend"
188
+ DIST_DIR = FRONTEND_DIR / "dist"
189
+ PUBLIC_DIR = FRONTEND_DIR / "public"
190
+
191
+ # Files in /public → /static/*
192
+ app.mount("/static", StaticFiles(directory=PUBLIC_DIR), name="static")
193
+
194
+ # /assets/* → dist/assets
195
+ app.mount("/assets", StaticFiles(directory=DIST_DIR / "assets"), name="assets")
196
+
197
+ # favicon
198
+ @app.get("/favicon.png")
199
+ async def favicon():
200
+ return FileResponse(DIST_DIR / "favicon.png")
201
+
202
+ # Everything else → index.html
203
+ @app.get("/{full_path:path}")
204
+ async def serve_spa(full_path: str):
205
+ return FileResponse(DIST_DIR / "index.html")
data/history.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "role": "user",
4
+ "text": "hello",
5
+ "src": null,
6
+ "timestamp": "2025-12-10T18:54:40.717180"
7
+ },
8
+ {
9
+ "role": "bot",
10
+ "text": "Hey",
11
+ "src": null,
12
+ "timestamp": "2025-12-10T18:54:40.752946"
13
+ }
14
+ ]
tree_data/portfolio_tree_data.pkl CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:4db94b18477126989603121cfd3cbbc1a23cb91fbcb4c5b9a8daac4eaf9d2295
3
  size 707757
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a9c8965301fa68822e3404e23a11b088c663b80f77002e6291318738ce2b9582
3
  size 707757