B[Auth: extract org_id from JWT] B --> C[Save to temp file] C --> D[extract_text_from_pdf/docx] D --> E1[extract_structured_data\nAnthropic Claude] D --> E2[extract_name_from_cv\nAnthropic Claude] D --> E3[extract_contact_info\nAnthropic Claude + regex] D --> E4[get_embedding\nOpenAI ada-002\nā 1536-dim vector] E1 & E2 & E3 & E4 --> F[Assemble metadata\nname, level, skills,\nexperience, industries,\nt-shape, contact info] F --> G1[INSERT INTO documents\nid, content, metadata,\norganization_id] F --> G2[INSERT INTO embeddings\nid, vector 1536,\ndocument_id] G1 & G2 --> H[UPDATE users\nSET profile_id = doc_id] H --> I[Invalidate cache\nuser_profile / user_profiles] end subgraph MATCH["š MATCHING ā POST /api/profiles/match"] J[User"> B[Auth: extract org_id from JWT] B --> C[Save to temp file] C --> D[extract_text_from_pdf/docx] D --> E1[extract_structured_data\nAnthropic Claude] D --> E2[extract_name_from_cv\nAnthropic Claude] D --> E3[extract_contact_info\nAnthropic Claude + regex] D --> E4[get_embedding\nOpenAI ada-002\nā 1536-dim vector] E1 & E2 & E3 & E4 --> F[Assemble metadata\nname, level, skills,\nexperience, industries,\nt-shape, contact info] F --> G1[INSERT INTO documents\nid, content, metadata,\norganization_id] F --> G2[INSERT INTO embeddings\nid, vector 1536,\ndocument_id] G1 & G2 --> H[UPDATE users\nSET profile_id = doc_id] H --> I[Invalidate cache\nuser_profile / user_profiles] end subgraph MATCH["š MATCHING ā POST /api/profiles/match"] J[User"> B[Auth: extract org_id from JWT] B --> C[Save to temp file] C --> D[extract_text_from_pdf/docx] D --> E1[extract_structured_data\nAnthropic Claude] D --> E2[extract_name_from_cv\nAnthropic Claude] D --> E3[extract_contact_info\nAnthropic Claude + regex] D --> E4[get_embedding\nOpenAI ada-002\nā 1536-dim vector] E1 & E2 & E3 & E4 --> F[Assemble metadata\nname, level, skills,\nexperience, industries,\nt-shape, contact info] F --> G1[INSERT INTO documents\nid, content, metadata,\norganization_id] F --> G2[INSERT INTO embeddings\nid, vector 1536,\ndocument_id] G1 & G2 --> H[UPDATE users\nSET profile_id = doc_id] H --> I[Invalidate cache\nuser_profile / user_profiles] end subgraph MATCH["š MATCHING ā POST /api/profiles/match"] J[User">
flowchart TD
subgraph UPLOAD["š¤ CV UPLOAD ā POST /api/profiles/upload"]
A[User uploads PDF/DOCX] --> B[Auth: extract org_id from JWT]
B --> C[Save to temp file]
C --> D[extract_text_from_pdf/docx]
D --> E1[extract_structured_data\nAnthropic Claude]
D --> E2[extract_name_from_cv\nAnthropic Claude]
D --> E3[extract_contact_info\nAnthropic Claude + regex]
D --> E4[get_embedding\nOpenAI ada-002\nā 1536-dim vector]
E1 & E2 & E3 & E4 --> F[Assemble metadata\nname, level, skills,\nexperience, industries,\nt-shape, contact info]
F --> G1[INSERT INTO documents\nid, content, metadata,\norganization_id]
F --> G2[INSERT INTO embeddings\nid, vector 1536,\ndocument_id]
G1 & G2 --> H[UPDATE users\nSET profile_id = doc_id]
H --> I[Invalidate cache\nuser_profile / user_profiles]
end
subgraph MATCH["š MATCHING ā POST /api/profiles/match"]
J[User submits project description] --> K[Auth: extract org_id from JWT]
K --> L[Embed project description\nOpenAI ada-002]
L --> M[Vector search pgvector\nSELECT ⦠ORDER BY\nembedding <-> query_vec\nLIMIT 10\nWHERE org_id = ā¦]
M --> N{level filter?}
N -->|yes| O[Keep matching level only]
N -->|no| P
O --> P{start_date?}
P -->|yes| Q[AvailabilityDB lookup\nFilter by ISO week]
P -->|no| R
Q --> R[Multi-dimensional scoring\n30% vector similarity\n25% experience relevance\n20% industry alignment\n15% level match\n10% competency match]
R --> S[Top 4 candidates ā\nLLM deep analysis\nAnthropic claude-haiku\nstrengths, gaps, score]
S --> T[LRU cache result\nsize 1000]
T --> U[Log to match_queries\n+ match_results tables]
U --> V[Return ranked matches\nto client]
end
I -.->|profile now findable| M