Gaykar commited on
Commit
ebc54c7
·
1 Parent(s): 1c2ebb7
Notebooks/CodeForge.ipynb CHANGED
The diff for this file is too large to render. See raw diff
 
app/core/config.py CHANGED
@@ -8,6 +8,7 @@ class Settings(BaseSettings):
8
 
9
  GROQ_API_KEY: str
10
  PINECONE_API_KEY: str
 
11
  # CLOUDINARY_CLOUD_NAME: str
12
  # CLOUDINARY_API_KEY: str
13
  # CLOUDINARY_API_SECRET: str
 
8
 
9
  GROQ_API_KEY: str
10
  PINECONE_API_KEY: str
11
+ WEB_BASE_URL: str
12
  # CLOUDINARY_CLOUD_NAME: str
13
  # CLOUDINARY_API_KEY: str
14
  # CLOUDINARY_API_SECRET: str
app/main.py CHANGED
@@ -5,9 +5,12 @@ from pathlib import Path
5
  from fastapi import FastAPI, UploadFile, File, Form, HTTPException
6
  from fastapi.middleware.cors import CORSMiddleware
7
  from langgraph.checkpoint.memory import MemorySaver
8
-
 
9
  from app.graph import graph
10
 
 
 
11
  app = FastAPI(title="Adaptive Onboarding Engine")
12
 
13
  app.add_middleware(
@@ -19,31 +22,6 @@ app.add_middleware(
19
 
20
  checkpointer = MemorySaver()
21
 
22
- # -----------------------------
23
- # Payload Builder
24
- # (inline from your export_ui_payload logic)
25
- # -----------------------------
26
-
27
- REQUIRED_KEYS = ["candidate_name", "skill_gap_analysis_data", "mermaid_code", "final_roadmap"]
28
-
29
- def build_ui_payload(state: dict) -> dict:
30
- ui_data = {}
31
- for key in REQUIRED_KEYS:
32
- val = state.get(key)
33
- if val is None:
34
- continue
35
- if hasattr(val, "model_dump"):
36
- ui_data[key] = val.model_dump()
37
- else:
38
- ui_data[key] = val
39
- return ui_data
40
-
41
-
42
- # -----------------------------
43
- # POST /analyze
44
- # Accepts: resume PDF (file upload) + job description (form field)
45
- # Returns: UI payload JSON
46
- # -----------------------------
47
 
48
  @app.post("/analyze")
49
  async def analyze(
 
5
  from fastapi import FastAPI, UploadFile, File, Form, HTTPException
6
  from fastapi.middleware.cors import CORSMiddleware
7
  from langgraph.checkpoint.memory import MemorySaver
8
+ from app.utils.ui_payload_constructor import UIPayload
9
+ from app.core.config import settings
10
  from app.graph import graph
11
 
12
+ WEB_URL=settings.WEB_BASE_URL
13
+
14
  app = FastAPI(title="Adaptive Onboarding Engine")
15
 
16
  app.add_middleware(
 
22
 
23
  checkpointer = MemorySaver()
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  @app.post("/analyze")
27
  async def analyze(
app/utils/cloudinary.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cloudinary
2
+ from cloudinary import Search
3
+ from app.core.config import settings
4
+
5
+ # Configure
6
+ cloudinary.config(
7
+ cloud_name=settings.CLOUDINARY_CLOUD_NAME,
8
+ api_key=settings.CLOUDINARY_API_KEY,
9
+ api_secret=settings.CLOUDINARY_API_SECRET,
10
+ secure=True
11
+ )
12
+
13
+ def get_resume_url(thread_id: str) -> str:
14
+ """
15
+ Searches Cloudinary for the resume PDF in the thread's folder
16
+ and returns the secure URL.
17
+ """
18
+ result = Search() \
19
+ .expression(f'folder:"threads/{thread_id}/*"') \
20
+ .sort_by('public_id', 'desc') \
21
+ .max_results(1) \
22
+ .execute()
23
+
24
+ resources = result.get("resources", [])
25
+
26
+ if not resources:
27
+ raise FileNotFoundError(f"No resume found for thread_id: {thread_id}")
28
+
29
+ pdf_url = resources[0]["secure_url"]
30
+ print(f"Found resume: {pdf_url}")
31
+ return pdf_url
app/utils/ui_payload_constructor.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Dict, Any
2
+
3
+ class UIPayload:
4
+ def __init__(
5
+ self,
6
+ candidate_name: Optional[str],
7
+ skill_gap_analysis_data: Optional[Dict[str, Any]],
8
+ mermaid_code: Optional[str],
9
+ final_roadmap: Optional[Dict[str, Any]],
10
+ ):
11
+ self.candidate_name = candidate_name
12
+ self.skill_gap_analysis_data = skill_gap_analysis_data
13
+ self.mermaid_code = mermaid_code
14
+ self.final_roadmap = final_roadmap
15
+
16
+ @classmethod
17
+ def from_state(cls, state: dict) -> "UIPayload":
18
+ return cls(
19
+ candidate_name=state.get("candidate_name"),
20
+ skill_gap_analysis_data=(
21
+ state["skill_gap_analysis_data"].model_dump()
22
+ if state.get("skill_gap_analysis_data") else None
23
+ ),
24
+ mermaid_code=state.get("mermaid_code"),
25
+ final_roadmap=state.get("final_roadmap"),
26
+ )
27
+
28
+ def to_dict(self) -> dict:
29
+ return {
30
+ k: v for k, v in self.__dict__.items()
31
+ if v is not None # exclude None values
32
+ }
requirements.txt CHANGED
@@ -9,4 +9,5 @@ uvicorn
9
  pinecone-text
10
  sentence-transformers
11
  python-multipart
12
- pymupdf
 
 
9
  pinecone-text
10
  sentence-transformers
11
  python-multipart
12
+ pymupdf
13
+ cloudinary