1+ import os
2+
13from fastapi import APIRouter , Depends , HTTPException
24
35from app .utils .api_key_auth import get_api_key
911
1012@router .post ("/prompt/linkedin" )
1113def linkedin_prompt_success (payload : dict , api_key : str = Depends (get_api_key )) -> dict :
12- """POST /prompt/linkedin: return cached completion for linkedinUrl when available ."""
13- linkedin_url = (payload .get ("linkedinUrl" ) or "" ).strip ()
14+ """POST /prompt/linkedin: return cached completion or create a new Gemini analysis ."""
15+ linkedin_url = (payload .get ("linkedin_url" ) or payload . get ( " linkedinUrl" ) or "" ).strip ()
1416 if not linkedin_url :
15- raise HTTPException (status_code = 400 , detail = "Missing 'linkedinUrl' in request body." )
17+ raise HTTPException (status_code = 400 , detail = "Missing 'linkedin_url' in request body." )
18+
19+ prompt = (payload .get ("prompt" ) or "" ).strip ()
20+ if not prompt :
21+ prompt = (
22+ "Analyse this LinkedIn profile URL and provide a concise summary of the person, "
23+ "their role, company, seniority, likely responsibilities, and notable signals. "
24+ f"LinkedIn URL: { linkedin_url } "
25+ )
26+
27+ gemini_api_key = os .getenv ("GEMINI_API_KEY" )
28+ if not gemini_api_key :
29+ raise HTTPException (status_code = 500 , detail = "Gemini API key not configured." )
1630
1731 conn = None
1832 cur = None
@@ -21,43 +35,164 @@ def linkedin_prompt_success(payload: dict, api_key: str = Depends(get_api_key))
2135 cur = conn .cursor ()
2236 cur .execute (
2337 """
24- SELECT id, completion, time, model, data
25- FROM prompt
26- WHERE (data->>'linkedinUrl' = %s OR prompt ILIKE %s)
27- ORDER BY id DESC
28- LIMIT 1;
29- """ ,
30- (linkedin_url , f"%{ linkedin_url } %" ),
38+ SELECT EXISTS (
39+ SELECT 1
40+ FROM information_schema.columns
41+ WHERE table_schema = 'public'
42+ AND table_name = 'prompt'
43+ AND column_name = 'search_vector'
44+ );
45+ """
3146 )
47+ exists_row = cur .fetchone ()
48+ has_search_vector = bool (exists_row and exists_row [0 ])
49+
50+ if has_search_vector :
51+ cur .execute (
52+ """
53+ SELECT id, prompt, completion, time, model, data
54+ FROM prompt
55+ WHERE (
56+ COALESCE(data->>'linkedin_url', data->>'linkedinUrl') = %s
57+ OR search_vector @@ plainto_tsquery('english', %s)
58+ OR prompt ILIKE %s
59+ )
60+ ORDER BY id DESC
61+ LIMIT 1;
62+ """ ,
63+ (linkedin_url , linkedin_url , f"%{ linkedin_url } %" ),
64+ )
65+ else :
66+ cur .execute (
67+ """
68+ SELECT id, prompt, completion, time, model, data
69+ FROM prompt
70+ WHERE (COALESCE(data->>'linkedin_url', data->>'linkedinUrl') = %s OR prompt ILIKE %s)
71+ ORDER BY id DESC
72+ LIMIT 1;
73+ """ ,
74+ (linkedin_url , f"%{ linkedin_url } %" ),
75+ )
3276 row = cur .fetchone ()
3377
3478 if row :
79+ cur .close ()
80+ conn .close ()
81+ cur = None
82+ conn = None
3583 return {
3684 "meta" : make_meta ("success" , "LinkedIn URL already analysed" ),
3785 "data" : {
3886 "cached" : True ,
3987 "id" : row [0 ],
40- "linkedinUrl" : linkedin_url ,
41- "completion" : row [1 ],
42- "time" : row [2 ].isoformat () if row [2 ] else None ,
43- "model" : row [3 ],
44- "record_data" : row [4 ],
88+ "linkedin_url" : linkedin_url ,
89+ "prompt" : row [1 ],
90+ "completion" : row [2 ],
91+ "time" : row [3 ].isoformat () if row [3 ] else None ,
92+ "model" : row [4 ],
93+ "record_data" : row [5 ],
4594 },
4695 }
4796
97+ cur .close ()
98+ conn .close ()
99+ cur = None
100+ conn = None
101+
102+ import json
103+ import logging
104+ import time as time_mod
105+ from app import __version__
106+ from google import genai
107+
108+ client = genai .Client (api_key = gemini_api_key )
109+ model_names = [
110+ "models/gemini-flash-latest" ,
111+ "models/gemini-1.5-pro" ,
112+ "models/gemini-1.5-flash" ,
113+ "models/gemini-1.0-pro" ,
114+ "models/gemini-pro" ,
115+ "models/gemini-pro-vision" ,
116+ ]
117+ response = None
118+ completion = None
119+ used_model = None
120+ errors = {}
121+ start_time = time_mod .time ()
122+ for model_name in model_names :
123+ try :
124+ response = client .models .generate_content (model = model_name , contents = prompt )
125+ completion = getattr (response , "text" , None )
126+ if completion :
127+ used_model = model_name
128+ break
129+ except Exception as model_exc :
130+ errors [model_name ] = str (model_exc )
131+ continue
132+
133+ duration = time_mod .time () - start_time
134+ if not completion :
135+ error_details = " | " .join ([f"{ name } : { message } " for name , message in errors .items ()])
136+ raise Exception (
137+ "No available Gemini model succeeded for generate_content with your API key. "
138+ f"Details: { error_details } "
139+ )
140+
141+ record_id = None
142+ record_data = {
143+ "version" : __version__ ,
144+ "linkedin_url" : linkedin_url ,
145+ }
146+ try :
147+ conn = get_db_connection_direct ()
148+ cur = conn .cursor ()
149+ data_blob = json .dumps (record_data )
150+ if has_search_vector :
151+ cur .execute (
152+ """
153+ INSERT INTO prompt (prompt, completion, duration, model, data, search_vector)
154+ VALUES (%s, %s, %s, %s, %s, to_tsvector('english', %s || ' ' || %s))
155+ RETURNING id;
156+ """ ,
157+ (prompt , completion , duration , used_model , data_blob , prompt , completion )
158+ )
159+ else :
160+ cur .execute (
161+ """
162+ INSERT INTO prompt (prompt, completion, duration, model, data)
163+ VALUES (%s, %s, %s, %s, %s)
164+ RETURNING id;
165+ """ ,
166+ (prompt , completion , duration , used_model , data_blob )
167+ )
168+ record_id_row = cur .fetchone ()
169+ record_id = record_id_row [0 ] if record_id_row else None
170+ conn .commit ()
171+ cur .close ()
172+ conn .close ()
173+ cur = None
174+ conn = None
175+ except Exception as db_exc :
176+ logging .error (f"Failed to insert prompt record: { db_exc } " )
177+
48178 return {
49- "meta" : make_meta ("warning " , "LinkedIn URL not analysed yet " ),
179+ "meta" : make_meta ("success " , f"Gemini completion received from { used_model } " ),
50180 "data" : {
51181 "cached" : False ,
52- "linkedinUrl" : linkedin_url ,
53- "completion" : None ,
182+ "id" : record_id ,
183+ "linkedin_url" : linkedin_url ,
184+ "prompt" : prompt ,
185+ "completion" : completion ,
186+ "duration" : duration ,
187+ "model" : used_model ,
188+ "record_data" : record_data ,
54189 },
55190 }
56191 except HTTPException :
57192 raise
58193 except Exception as e :
59194 return {
60- "meta" : make_meta ("error" , f"DB error: { str (e )} " ),
195+ "meta" : make_meta ("error" , f"Gemini API error: { str (e )} " ),
61196 "data" : {},
62197 }
63198 finally :
0 commit comments