Fayza38 commited on
Commit
a8cac7f
·
verified ·
1 Parent(s): 1bb94e9

Update behavioral and technical id

Browse files
Files changed (1) hide show
  1. main.py +267 -333
main.py CHANGED
@@ -1,333 +1,267 @@
1
- # =========================================
2
- # 1. IMPORTS
3
- # =========================================
4
- import asyncio
5
- import os
6
- import json
7
- import uuid
8
- import cloudinary
9
- import cloudinary.uploader
10
- import firebase_admin
11
- from firebase_admin import credentials, firestore
12
- from fastapi import FastAPI, HTTPException, BackgroundTasks
13
- from pydantic import BaseModel
14
- from gradio_client import Client
15
- from google.cloud.firestore_v1.base_query import FieldFilter
16
- import edge_tts
17
- from dotenv import load_dotenv
18
-
19
- # =========================================
20
- # 2. INITIALIZATIONS
21
- # =========================================
22
- if not firebase_admin._apps:
23
- fb_json = os.getenv("FIREBASE_JSON")
24
-
25
- if fb_json:
26
- cred_dict = json.loads(fb_json)
27
- cred = credentials.Certificate(cred_dict)
28
- else:
29
- cred = credentials.Certificate("serviceAccountKey.json")
30
-
31
- firebase_admin.initialize_app(cred)
32
-
33
- db = firestore.client()
34
-
35
- # Load environment variables
36
- load_dotenv()
37
-
38
- # Cloudinary Configuration
39
- cloudinary.config(
40
- cloud_name=os.getenv("CLOUD_NAME"),
41
- api_key=os.getenv("API_KEY"),
42
- api_secret=os.getenv("API_SECRET"),
43
- secure=True
44
- )
45
-
46
- app = FastAPI(title="AI Question Service")
47
- HF_SPACE = "Fayza38/Question_and_answer_model"
48
- client = None
49
-
50
- # =========================================
51
- # 3. MODELS & CONSTANTS
52
- # =========================================
53
- TECH_CATEGORIES = {0: "Security",
54
- 1: "BackEnd",
55
- 2: "Networking",
56
- 3: "FrontEnd",
57
- 4: "DataEngineering",
58
- 5: "WebDevelopment",
59
- 6: "FullStack",
60
- 7: "VersionControl",
61
- 8: "SystemDesign",
62
- 9: "MachineLearning",
63
- 10: "LanguagesAndFrameworks",
64
- 11: "DatabaseSystems",
65
- 12: "ArtificialIntelligence",
66
- 13: "SoftwareTesting",
67
- 14: "DistributedSystems",
68
- 15: "DevOps",
69
- 16: "LowLevelSystems",
70
- 17: "DatabaseAndSql",
71
- 18: "GeneralProgramming",
72
- 19: "DataStructures",
73
- 20: "Algorithms"}
74
- DIFFICULTY_MAP = {0: "Easy", 1: "Intermediate", 2: "Hard"}
75
- SESSION_TYPE_MAP = {0: "Technical", 1: "Behavioral"}
76
-
77
- class GenerateSessionRequest(BaseModel):
78
- sessionId: str
79
- sessionType: int
80
- difficultyLevel: int = 0
81
- trackName: int
82
-
83
- class CleanupRequest(BaseModel):
84
- audioUrls: list[str]
85
-
86
- # =========================================
87
- # 4. STARTUP EVENT
88
- # =========================================
89
- @app.on_event("startup")
90
- async def startup_event():
91
- global client
92
- max_retries = 5
93
- retry_delay = 10
94
- print("Connecting to Hugging Face Space...")
95
- for i in range(max_retries):
96
- try:
97
- client = Client(HF_SPACE)
98
- print("Connected Successfully!")
99
- break
100
- except Exception as e:
101
- print(f"Connection attempt {i+1} failed. Retrying in {retry_delay}s...")
102
- if i < max_retries - 1: await asyncio.sleep(retry_delay)
103
-
104
- # =========================================
105
- # 5. HELPERS
106
- # =========================================
107
- async def generate_audio(text, filename):
108
- try:
109
- # Rate is set to -10% to make the voice slightly slower and clearer
110
- communicate = edge_tts.Communicate(text, "en-US-GuyNeural", rate="-15%")
111
- await communicate.save(filename)
112
-
113
- # Upload to Cloudinary
114
- upload_result = cloudinary.uploader.upload(
115
- filename,
116
- resource_type="video",
117
- folder="interview_audio"
118
- )
119
-
120
- if os.path.exists(filename): os.remove(filename)
121
- return upload_result["secure_url"]
122
- except Exception as e:
123
- print(f"Audio Generation Error: {e}")
124
- if os.path.exists(filename): os.remove(filename)
125
- return None
126
-
127
- async def safe_generate(prompt, retries=3):
128
- if client is None: raise Exception("Gradio Client not initialized")
129
- for attempt in range(retries):
130
- try:
131
- loop = asyncio.get_running_loop()
132
- return await loop.run_in_executor(None, lambda: client.predict(prompt=prompt, api_name="/generate_questions"))
133
- except Exception as e:
134
- if attempt == retries - 1: raise e
135
- await asyncio.sleep(2)
136
-
137
- def parse_question_output(raw_output: str):
138
- if not raw_output: return None, None
139
- text = raw_output.split("assistant")[-1].strip() if "assistant" in raw_output else raw_output
140
- if "Q:" in text and "A:" in text:
141
- try:
142
- parts = text.split("A:")
143
- q = parts[0].replace("Q:", "").strip()
144
- a = parts[1].split("<|im_end|>")[0].strip()
145
- return q, a
146
- except: return None, None
147
- return None, None
148
-
149
- # =========================================
150
- # 6. REFILL & PREFILL LOGIC
151
- # =========================================
152
- async def refill_specific_pool(track_id: int, difficulty: int, count: int, session_type: int = 0):
153
- global client
154
- while client is None: await asyncio.sleep(5)
155
-
156
- # Technical (0) vs Behavioral (1)
157
- if session_type == 1:
158
- prompt = "Generate ONE unique behavioral interview question (soft skills, situational). Format: Q: [Question] A: [Answer]"
159
- track_text = "Behavioral"
160
- level_text = "General"
161
- else:
162
- track_text = TECH_CATEGORIES.get(track_id)
163
- level_text = DIFFICULTY_MAP.get(difficulty)
164
- prompt = f"Generate ONE unique {track_text} interview question for {level_text} level. Format: Q: [Question] A: [Answer]"
165
-
166
- success_count = 0
167
- while success_count < count:
168
- try:
169
- raw_output = await safe_generate(prompt)
170
- q_text, a_text = parse_question_output(raw_output)
171
-
172
- if q_text and a_text:
173
- filename = f"{uuid.uuid4()}.mp3"
174
- audio_url = await generate_audio(q_text, filename)
175
-
176
- if audio_url:
177
- db.collection("questions_pool").add({
178
- "session_type": session_type,
179
- "track_id": track_id if session_type == 0 else -1,
180
- "difficulty": difficulty if session_type == 0 else 0,
181
- "questionText": q_text,
182
- "questionIdealAnswer": a_text,
183
- "audio_url": audio_url,
184
- "created_at": firestore.SERVER_TIMESTAMP
185
- })
186
- success_count += 1
187
- print(f"[{success_count}/{count}] Refilled: {track_text}")
188
- await asyncio.sleep(2)
189
- except Exception as e:
190
- print(f"Error in refill: {e}")
191
- await asyncio.sleep(5)
192
-
193
- # =========================================
194
- # 6. ENDPOINTS
195
- # =========================================
196
- @app.post("/generate-session")
197
- async def generate_session(request: GenerateSessionRequest, background_tasks: BackgroundTasks):
198
- t_id, diff = request.trackName, request.difficultyLevel
199
- s_type = request.sessionType # 0: Technical, 1: Behavioral
200
-
201
- # Query based on the new session types (0 or 1)
202
- query = db.collection("questions_pool").where(filter=FieldFilter("session_type", "==", s_type))
203
-
204
- if s_type == 0: # Technical
205
- query = query.where(filter=FieldFilter("track_id", "==", t_id)) \
206
- .where(filter=FieldFilter("difficulty", "==", diff))
207
-
208
- docs_query = query.limit(10).get()
209
-
210
- final_questions = []
211
- for index, doc in enumerate(docs_query, start=1):
212
- data = doc.to_dict()
213
- final_questions.append({
214
- "question_id": index,
215
- "text": data["questionText"],
216
- "expected_answer": data["questionIdealAnswer"],
217
- "audio_url": data.get("audio_url", "")
218
- })
219
- # Delete after fetching to ensure questions are unique for next users
220
- db.collection("questions_pool").document(doc.id).delete()
221
-
222
- # Maintenance task to keep the pool full
223
- async def maintain_stock():
224
- agg_query = query.count()
225
- current_count = agg_query.get()[0][0].value
226
-
227
- target = 50
228
- if current_count < target:
229
- await refill_specific_pool(t_id, diff, target - current_count, session_type=s_type)
230
-
231
- background_tasks.add_task(maintain_stock)
232
-
233
- if not final_questions:
234
- raise HTTPException(status_code=503, detail="Pool empty for this type.")
235
-
236
- return {"session_id": request.sessionId, "questions": final_questions}
237
-
238
- @app.get("/system-cleanup")
239
- async def system_cleanup(background_tasks: BackgroundTasks):
240
- """Scan and delete all questions with missing or invalid audio URLs"""
241
- def run_cleanup():
242
- print("Starting System Cleanup...")
243
- # Get all documents in the pool
244
- docs = db.collection("questions_pool").get()
245
- deleted_count = 0
246
-
247
- for doc in docs:
248
- data = doc.to_dict()
249
- # Check if audio_url is missing, None, or empty string
250
- if not data.get("audio_url") or data.get("audio_url") == "":
251
- db.collection("questions_pool").document(doc.id).delete()
252
- deleted_count += 1
253
-
254
- print(f"Cleanup finished! Deleted {deleted_count} broken questions.")
255
-
256
- background_tasks.add_task(run_cleanup)
257
- return {"message": "Cleanup started in background. Check your console/logs."}
258
-
259
-
260
- @app.post("/cleanup-audio")
261
- async def cleanup_audio(request: CleanupRequest, background_tasks: BackgroundTasks):
262
- def delete_job(urls):
263
- for url in urls:
264
- try:
265
- public_id = "interview_audio/" + url.split('/')[-1].split('.')[0]
266
- cloudinary.uploader.destroy(public_id, resource_type="video")
267
- print(f"Deleted: {public_id}")
268
- except Exception: pass
269
-
270
- background_tasks.add_task(delete_job, request.audioUrls)
271
- return {"message": "Cleanup started"}
272
-
273
- # @app.get("/trigger-full-prefill")
274
- # async def trigger_full_prefill(background_tasks: BackgroundTasks):
275
- # """Prefills 30 questions for every track and every difficulty level"""
276
- # async def full_prefill_task():
277
- # for t_id in TECH_CATEGORIES.keys():
278
- # for diff in DIFFICULTY_MAP.keys():
279
- # print(f"Starting full prefill for Track {t_id}, Level {diff}")
280
- # await refill_specific_pool(t_id, diff, 30)
281
-
282
- # background_tasks.add_task(full_prefill_task)
283
- # return {"message": "Full system prefill started in background (30 questions per track/level)"}
284
- #?##############################################################################
285
- # @app.get("/trigger-behavioral-prefill")
286
- # async def trigger_behavioral_prefill(background_tasks: BackgroundTasks):
287
- # """Prefills 30 Behavioral questions (No track or difficulty needed)"""
288
-
289
- # async def run_behavioral_task():
290
- # print("Starting Behavioral questions prefill...")
291
- # await refill_specific_pool(track_id=0, difficulty=0, count=30, session_type=2)
292
- # print("Finished prefilling 30 Behavioral questions!")
293
-
294
- # background_tasks.add_task(run_behavioral_task)
295
- # return {"message": "Behavioral prefill (30 questions) started in background."}
296
-
297
- @app.get("/health")
298
- async def health(): return {"status": "running", "hf_connected": client is not None}
299
-
300
-
301
- #?##########################################################################
302
-
303
- # @app.get("/final-migration-fix")
304
- # async def final_migration_fix(background_tasks: BackgroundTasks):
305
- # def run_fix():
306
- # print("🔄 Starting Final Data Fix...")
307
- # docs = db.collection("questions_pool").get()
308
- # updated_count = 0
309
-
310
- # for doc in docs:
311
- # data = doc.to_dict()
312
- # updates = {}
313
-
314
- # # 1. تصحيح الـ session_type (Technical: 0, Behavioral: 1)
315
- # # لو كان 1 (قديم) خليه 0، ولو كان 2 (قديم) خليه 1
316
- # curr_type = data.get("session_type")
317
- # if curr_type == 1: updates["session_type"] = 0
318
- # elif curr_type == 2: updates["session_type"] = 1
319
-
320
- # # 2. تصحيح الـ difficulty (Easy: 0, Intermediate: 1, Hard: 2)
321
- # # الأسئلة القديمة كانت 1 و 2 و 3، هننقص منها 1
322
- # curr_diff = data.get("difficulty")
323
- # if curr_diff in [1, 2, 3]:
324
- # updates["difficulty"] = curr_diff - 1
325
-
326
- # if updates:
327
- # db.collection("questions_pool").document(doc.id).update(updates)
328
- # updated_count += 1
329
-
330
- # print(f"✅ Final Fix Done! Updated {updated_count} questions.")
331
-
332
- # background_tasks.add_task(run_fix)
333
- # return {"message": "Final migration started. Your pool will be ready in a minute!"}
 
1
+ # =========================================
2
+ # 1. IMPORTS
3
+ # =========================================
4
+ import asyncio
5
+ import os
6
+ import json
7
+ import uuid
8
+ import cloudinary
9
+ import cloudinary.uploader
10
+ import firebase_admin
11
+ from firebase_admin import credentials, firestore
12
+ from fastapi import FastAPI, HTTPException, BackgroundTasks
13
+ from pydantic import BaseModel
14
+ from gradio_client import Client
15
+ from google.cloud.firestore_v1.base_query import FieldFilter
16
+ import edge_tts
17
+ from typing import Optional
18
+ from dotenv import load_dotenv
19
+ from contextlib import asynccontextmanager
20
+
21
+ # =========================================
22
+ # 2. INITIALIZATIONS & CONFIG
23
+ # =========================================
24
+ load_dotenv()
25
+
26
+ if not firebase_admin._apps:
27
+ fb_json = os.getenv("FIREBASE_JSON")
28
+ if fb_json:
29
+ cred_dict = json.loads(fb_json)
30
+ cred = credentials.Certificate(cred_dict)
31
+ else:
32
+ cred = credentials.Certificate("serviceAccountKey.json")
33
+ firebase_admin.initialize_app(cred)
34
+
35
+ db = firestore.client()
36
+
37
+ cloudinary.config(
38
+ cloud_name=os.getenv("CLOUD_NAME"),
39
+ api_key=os.getenv("API_KEY"),
40
+ api_secret=os.getenv("API_SECRET"),
41
+ secure=True
42
+ )
43
+
44
+ HF_SPACE = "Fayza38/Question_and_answer_model"
45
+ client = None
46
+
47
+ # =========================================
48
+ # 3. MODELS & CONSTANTS
49
+ # =========================================
50
+ TECH_CATEGORIES = {
51
+ 0: "Security", 1: "BackEnd", 2: "Networking", 3: "FrontEnd",
52
+ 4: "DataEngineering", 5: "WebDevelopment", 6: "FullStack",
53
+ 7: "VersionControl", 8: "SystemDesign", 9: "MachineLearning",
54
+ 10: "LanguagesAndFrameworks", 11: "DatabaseSystems",
55
+ 12: "ArtificialIntelligence", 13: "SoftwareTesting",
56
+ 14: "DistributedSystems", 15: "DevOps", 16: "LowLevelSystems",
57
+ 17: "DatabaseAndSql", 18: "GeneralProgramming",
58
+ 19: "DataStructures", 20: "Algorithms"
59
+ }
60
+
61
+ DIFFICULTY_MAP = {0: "Easy", 1: "Intermediate", 2: "Hard"}
62
+
63
+ class GenerateSessionRequest(BaseModel):
64
+ sessionId: str
65
+ sessionType: int # 0: Behavioral, 1: Technical
66
+ difficultyLevel: int = 0
67
+ trackName: Optional[int] = None
68
+
69
+ class CleanupRequest(BaseModel):
70
+ audioUrls: list[str]
71
+
72
+ # =========================================
73
+ # 4. BACKGROUND TASKS (Auto-Cleaner)
74
+ # =========================================
75
+ async def auto_clean_invalid_questions():
76
+ """Background loop to remove questions with missing or broken audio."""
77
+ while True:
78
+ try:
79
+ print("[Auto-Cleaner] Scanning for broken questions...")
80
+ docs = db.collection("questions_pool").get()
81
+ deleted_count = 0
82
+ for doc in docs:
83
+ data = doc.to_dict()
84
+ if not data.get("audio_url"):
85
+ db.collection("questions_pool").document(doc.id).delete()
86
+ deleted_count += 1
87
+ if deleted_count > 0:
88
+ print(f"[Auto-Cleaner] Removed {deleted_count} broken questions.")
89
+ except Exception as e:
90
+ print(f"[Auto-Cleaner] Error: {e}")
91
+ await asyncio.sleep(600) # Scan every 10 minutes
92
+
93
+ # =========================================
94
+ # 5. LIFESPAN MANAGEMENT
95
+ # =========================================
96
+ @asynccontextmanager
97
+ async def lifespan(app: FastAPI):
98
+ global client
99
+ print("Connecting to Hugging Face Space...")
100
+ try:
101
+ loop = asyncio.get_event_loop()
102
+ client = await loop.run_in_executor(None, lambda: Client(HF_SPACE))
103
+ print("Connected Successfully!")
104
+ # Start the background cleaner
105
+ asyncio.create_task(auto_clean_invalid_questions())
106
+ except Exception as e:
107
+ print(f"Startup Connection failed: {e}")
108
+
109
+ yield
110
+ print("Shutting down Intervision Service...")
111
+
112
+ app = FastAPI(title="Intervision AI Question Service", lifespan=lifespan)
113
+
114
+ # =========================================
115
+ # 6. HELPERS
116
+ # =========================================
117
+ async def generate_audio(text, filename):
118
+ try:
119
+ communicate = edge_tts.Communicate(text, "en-US-GuyNeural", rate="-15%")
120
+ await communicate.save(filename)
121
+ upload_result = cloudinary.uploader.upload(
122
+ filename, resource_type="video", folder="interview_audio"
123
+ )
124
+ if os.path.exists(filename): os.remove(filename)
125
+ return upload_result["secure_url"]
126
+ except Exception as e:
127
+ print(f"Audio Error: {e}")
128
+ if os.path.exists(filename): os.remove(filename)
129
+ return None
130
+
131
+ async def safe_generate(prompt, retries=3):
132
+ if client is None: raise Exception("Gradio Client not initialized")
133
+ for attempt in range(retries):
134
+ try:
135
+ loop = asyncio.get_running_loop()
136
+ return await loop.run_in_executor(None, lambda: client.predict(prompt=prompt, api_name="/generate_questions"))
137
+ except Exception as e:
138
+ if attempt == retries - 1: raise e
139
+ await asyncio.sleep(2)
140
+
141
+ def parse_question_output(raw_output: str):
142
+ if not raw_output: return None, None
143
+ text = raw_output.split("assistant")[-1].strip() if "assistant" in raw_output else raw_output
144
+ if "Q:" in text and "A:" in text:
145
+ try:
146
+ parts = text.split("A:")
147
+ q = parts[0].replace("Q:", "").strip()
148
+ a = parts[1].split("<|im_end|>")[0].strip()
149
+ return q, a
150
+ except: return None, None
151
+ return None, None
152
+
153
+ async def refill_specific_pool(track_id: int, difficulty: int, count: int, session_type: int = 1):
154
+ while client is None: await asyncio.sleep(5)
155
+
156
+ if session_type == 0:
157
+ prompt = ("Generate ONE simple Behavioral interview question for a fresh graduate. "
158
+ "Focus on soft skills like teamwork or leadership. Strictly NO technical questions. "
159
+ "Format: Q: [Question] A: [Answer]")
160
+ track_text = "Behavioral"
161
+ else:
162
+ track_text = TECH_CATEGORIES.get(track_id)
163
+ level_text = DIFFICULTY_MAP.get(difficulty)
164
+ prompt = f"Generate ONE unique {track_text} interview question for {level_text} level. Format: Q: [Question] A: [Answer]"
165
+
166
+ success_count = 0
167
+ while success_count < count:
168
+ try:
169
+ raw_output = await safe_generate(prompt)
170
+ q_text, a_text = parse_question_output(raw_output)
171
+ if q_text and a_text:
172
+ filename = f"{uuid.uuid4()}.mp3"
173
+ audio_url = await generate_audio(q_text, filename)
174
+ if audio_url:
175
+ db.collection("questions_pool").add({
176
+ "session_type": session_type,
177
+ "track_id": track_id if session_type == 1 else -1,
178
+ "difficulty": difficulty if session_type == 1 else 0,
179
+ "questionText": q_text,
180
+ "questionIdealAnswer": a_text,
181
+ "audio_url": audio_url,
182
+ "created_at": firestore.SERVER_TIMESTAMP
183
+ })
184
+ success_count += 1
185
+ print(f"Refilled {success_count}/{count} for {track_text}")
186
+ await asyncio.sleep(3)
187
+ except Exception as e:
188
+ print(f"Refill error: {e}")
189
+ await asyncio.sleep(5)
190
+
191
+ # =========================================
192
+ # 7. MAIN ENDPOINTS
193
+ # =========================================
194
+ @app.post("/generate-session")
195
+ async def generate_session(request: GenerateSessionRequest, background_tasks: BackgroundTasks):
196
+ s_type, diff, t_id = request.sessionType, request.difficultyLevel, request.trackName
197
+ query = db.collection("questions_pool").where(filter=FieldFilter("session_type", "==", s_type))
198
+
199
+ if s_type == 1: # Technical
200
+ if t_id is None: raise HTTPException(status_code=400, detail="trackName required for technical.")
201
+ query = query.where(filter=FieldFilter("track_id", "==", t_id)).where(filter=FieldFilter("difficulty", "==", diff))
202
+
203
+ docs_query = query.limit(10).get()
204
+ final_questions = []
205
+ for index, doc in enumerate(docs_query, start=1):
206
+ data = doc.to_dict()
207
+ final_questions.append({
208
+ "question_id": index, "text": data["questionText"],
209
+ "expected_answer": data["questionIdealAnswer"], "audio_url": data.get("audio_url", "")
210
+ })
211
+ db.collection("questions_pool").document(doc.id).delete()
212
+
213
+ async def maintain_stock():
214
+ snap = query.count().get()
215
+ current = snap[0][0].value
216
+ if current < 50:
217
+ await refill_specific_pool(t_id if s_type == 1 else -1, diff, 50 - current, session_type=s_type)
218
+
219
+ background_tasks.add_task(maintain_stock)
220
+ if not final_questions:
221
+ raise HTTPException(status_code=503, detail="Question pool is currently empty.")
222
+ return {"session_id": request.sessionId, "questions": final_questions}
223
+
224
+ @app.post("/cleanup-audio")
225
+ async def cleanup_audio(request: CleanupRequest, background_tasks: BackgroundTasks):
226
+ def delete_job(urls):
227
+ for url in urls:
228
+ try:
229
+ public_id = "interview_audio/" + url.split('/')[-1].split('.')[0]
230
+ cloudinary.uploader.destroy(public_id, resource_type="video")
231
+ except: pass
232
+ background_tasks.add_task(delete_job, request.audioUrls)
233
+ return {"message": "Cleanup started"}
234
+
235
+ @app.get("/system-cleanup")
236
+ async def system_cleanup(background_tasks: BackgroundTasks):
237
+ """Scan and delete all questions with missing or invalid audio URLs"""
238
+ def run_cleanup():
239
+ print("Starting System Cleanup...")
240
+ # Get all documents in the pool
241
+ docs = db.collection("questions_pool").get()
242
+ deleted_count = 0
243
+
244
+ for doc in docs:
245
+ data = doc.to_dict()
246
+ # Check if audio_url is missing, None, or empty string
247
+ if not data.get("audio_url") or data.get("audio_url") == "":
248
+ db.collection("questions_pool").document(doc.id).delete()
249
+ deleted_count += 1
250
+
251
+ print(f"Cleanup finished! Deleted {deleted_count} broken questions.")
252
+
253
+ background_tasks.add_task(run_cleanup)
254
+ return {"message": "Cleanup started in background. Check your console/logs."}
255
+
256
+
257
+ @app.get("/health")
258
+ async def health():
259
+ return {"status": "active", "hf_connected": client is not None}
260
+
261
+ @app.get("/")
262
+ async def root():
263
+ return {"app": "Intervision AI Service","Status": "Running.."}
264
+
265
+ if __name__ == "__main__":
266
+ import uvicorn
267
+ uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)