DeepLearning101 commited on
Commit
f93e884
·
verified ·
1 Parent(s): ef40020

Create services.py

Browse files
Files changed (1) hide show
  1. services.py +138 -0
services.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from dotenv import load_dotenv
4
+ from google import genai
5
+ from google.genai import types
6
+ from typing import List, Dict, Any, Optional
7
+
8
+ # 載入環境變數
9
+ load_dotenv()
10
+
11
+ class GeminiService:
12
+ def __init__(self):
13
+ # 從環境變數讀取 Key,兼容本地 .env 與 Hugging Face Secrets
14
+ api_key = os.getenv("GEMINI_API_KEY")
15
+ if not api_key:
16
+ # 為了避免佈署時報錯,這裡僅印出警告,讓 UI 層處理
17
+ print("警告:找不到 GEMINI_API_KEY")
18
+
19
+ self.client = genai.Client(api_key=api_key) if api_key else None
20
+ self.model_id = os.getenv("GEMINI_MODEL_ID", "gemini-2.0-flash")
21
+
22
+ def _check_client(self):
23
+ if not self.client:
24
+ raise ValueError("API Key 未設定,請檢查 .env 或 Hugging Face Secrets")
25
+
26
+ def search_professors(self, query: str, exclude_names: List[str] = []) -> List[Dict]:
27
+ self._check_client()
28
+ exclusion_prompt = ""
29
+ if exclude_names:
30
+ exclusion_prompt = f"IMPORTANT: Do not include: {', '.join(exclude_names)}."
31
+
32
+ # Phase 1: Search (Pure Text)
33
+ search_prompt = f"""
34
+ Using Google Search, find 10 prominent professors in universities across Taiwan who are experts in the field of "{query}".
35
+
36
+ CRITICAL:
37
+ 1. FACT CHECK: Verify they are currently faculty.
38
+ 2. RELEVANCE: Their PRIMARY research focus must be "{query}".
39
+ {exclusion_prompt}
40
+
41
+ List them (Name - University - Department) in Traditional Chinese.
42
+ """
43
+
44
+ search_response = self.client.models.generate_content(
45
+ model=self.model_id,
46
+ contents=search_prompt,
47
+ config=types.GenerateContentConfig(
48
+ tools=[types.Tool(google_search=types.GoogleSearch())]
49
+ )
50
+ )
51
+ raw_text = search_response.text
52
+
53
+ # Phase 2: Extract JSON
54
+ extract_prompt = f"""
55
+ From the text below, extract professor names, universities, and departments.
56
+ Calculate a Relevance Score (0-100) based on query: "{query}".
57
+
58
+ Return ONLY a JSON array: [{{"name": "...", "university": "...", "department": "...", "relevanceScore": 85}}]
59
+
60
+ Text:
61
+ ---
62
+ {raw_text}
63
+ ---
64
+ """
65
+
66
+ extract_response = self.client.models.generate_content(
67
+ model=self.model_id,
68
+ contents=extract_prompt,
69
+ config=types.GenerateContentConfig(
70
+ response_mime_type='application/json'
71
+ )
72
+ )
73
+
74
+ try:
75
+ return json.loads(extract_response.text)
76
+ except Exception as e:
77
+ print(f"JSON Parse Error: {e}")
78
+ return []
79
+
80
+ def get_professor_details(self, professor: Dict) -> Dict:
81
+ self._check_client()
82
+ name = professor.get('name')
83
+ uni = professor.get('university')
84
+ dept = professor.get('department')
85
+
86
+ prompt = f"""
87
+ Act as an academic consultant. Investigate Professor {name} from {dept} at {uni}.
88
+
89
+ Find their "Combat Experience" (實戰經驗). Search for:
90
+ 1. **Recent Key Publications (Last 5 Years)**: Find 2-3 top papers. **MUST try to find Citation Counts**.
91
+ 2. **Alumni Directions**: Where do their graduates work? (e.g., TSMC, Google).
92
+ 3. **Industry Collaboration**: Any industry projects?
93
+
94
+ Format output in Markdown (Traditional Chinese).
95
+ """
96
+
97
+ response = self.client.models.generate_content(
98
+ model=self.model_id,
99
+ contents=prompt,
100
+ config=types.GenerateContentConfig(
101
+ tools=[types.Tool(google_search=types.GoogleSearch())]
102
+ )
103
+ )
104
+
105
+ # Extract Sources
106
+ sources = []
107
+ if response.candidates[0].grounding_metadata and response.candidates[0].grounding_metadata.grounding_chunks:
108
+ for chunk in response.candidates[0].grounding_metadata.grounding_chunks:
109
+ if chunk.web and chunk.web.uri and chunk.web.title:
110
+ sources.append({"title": chunk.web.title, "uri": chunk.web.uri})
111
+
112
+ # Deduplicate
113
+ unique_sources = {v['uri']: v for v in sources}.values()
114
+
115
+ return {
116
+ "text": response.text,
117
+ "sources": list(unique_sources)
118
+ }
119
+
120
+ def chat_with_ai(self, history: List[Dict], new_message: str, context: str) -> str:
121
+ self._check_client()
122
+ system_instruction = f"Source of truth:\n{context}"
123
+
124
+ chat_history = []
125
+ for h in history:
126
+ role = "user" if h["role"] == "user" else "model"
127
+ chat_history.append(types.Content(role=role, parts=[types.Part(text=h["content"])]))
128
+
129
+ chat = self.client.chats.create(
130
+ model=self.model_id,
131
+ history=chat_history,
132
+ config=types.GenerateContentConfig(
133
+ system_instruction=system_instruction
134
+ )
135
+ )
136
+
137
+ response = chat.send_message(new_message)
138
+ return response.text