daniielyan commited on
Commit
4a5b92f
Β·
1 Parent(s): 305420b

πŸ”§ Update main.py to change MCP server flag, add Hugging Face dependencies in pyproject.toml, and enhance LLM service with Hugging Face integration. Add new job listings and user profiles in JSON data files.

Browse files
data/embeddings_metadata.json CHANGED
The diff for this file is too large to render. See raw diff
 
data/jobs_cache.json CHANGED
The diff for this file is too large to render. See raw diff
 
data/profiles.json CHANGED
@@ -17,5 +17,59 @@
17
  "certifications": null,
18
  "created_at": "2025-06-07 15:09:40.290202",
19
  "updated_at": "2025-06-07 15:09:40.290541"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
  }
 
17
  "certifications": null,
18
  "created_at": "2025-06-07 15:09:40.290202",
19
  "updated_at": "2025-06-07 15:09:40.290541"
20
+ },
21
+ "demo_user1": {
22
+ "user_id": "demo_user1",
23
+ "resume": "AI engineer with experience in NLP, computer vision, and legal AI systems. Skilled in building RAG-based assistants, deploying FastAPI services, and optimizing ML models.",
24
+ "skills": [
25
+ "Python",
26
+ "FastAPI",
27
+ "Hugging Face Transformers",
28
+ "OpenPose",
29
+ "Deep SORT",
30
+ "Docker",
31
+ "PostgreSQL",
32
+ "Bash",
33
+ "Claude",
34
+ "Gemini",
35
+ "YOLO",
36
+ "Pandas",
37
+ "NumPy"
38
+ ],
39
+ "salary_wish": "$30,000 - $50,000 annually",
40
+ "career_goals": "Grow as an AI engineer by working on real-world AI applications, especially legal and vision-based systems. Open to research or applied ML roles that help improve model reliability and deployment pipelines.",
41
+ "experience_level": "Entry-level",
42
+ "location": "Yerevan / Remote",
43
+ "education": "BS in Artificial Intelligence (in progress, 2023\u20132027), National Polytechnic University of Armenia",
44
+ "certifications": null,
45
+ "created_at": "2025-06-08 00:48:18.790480",
46
+ "updated_at": "2025-06-08 00:48:18.790482"
47
+ },
48
+ "Tatul's_profile": {
49
+ "user_id": "Tatul's_profile",
50
+ "resume": "AI engineer with experience in NLP, computer vision, and legal AI systems. Skilled in building RAG-based assistants, deploying FastAPI services, and optimizing ML models.",
51
+ "skills": [
52
+ "Python",
53
+ "FastAPI",
54
+ "Hugging Face Transformers",
55
+ "OpenPose",
56
+ "Deep SORT",
57
+ "Docker",
58
+ "PostgreSQL",
59
+ "Bash",
60
+ "Claude",
61
+ "Gemini",
62
+ "YOLO",
63
+ "Pandas",
64
+ "NumPy"
65
+ ],
66
+ "salary_wish": "$30,000 - $50,000 annually",
67
+ "career_goals": "Grow as an AI engineer by working on real-world AI applications, especially legal and vision-based systems. Open to research or applied ML roles that help improve model reliability and deployment pipelines.",
68
+ "experience_level": "Entry-level",
69
+ "location": "Yerevan / Remote",
70
+ "education": "BS in Artificial Intelligence (in progress, 2023\u20132027), National Polytechnic University of Armenia",
71
+ "certifications": null,
72
+ "created_at": "2025-06-08 00:48:36.150851",
73
+ "updated_at": "2025-06-08 00:48:36.150854"
74
  }
75
  }
main.py CHANGED
@@ -420,10 +420,10 @@ def main():
420
  demo.launch(
421
  server_name=mcp_server.settings.host,
422
  server_port=mcp_server.settings.port,
423
- enable_mcp=True,
424
  share=False,
425
- show_error=True,
426
- )
427
 
428
 
429
  if __name__ == "__main__":
 
420
  demo.launch(
421
  server_name=mcp_server.settings.host,
422
  server_port=mcp_server.settings.port,
423
+ mcp_server=True,
424
  share=False,
425
+ show_error=True
426
+ )
427
 
428
 
429
  if __name__ == "__main__":
pyproject.toml CHANGED
@@ -26,6 +26,7 @@ dependencies = [
26
  "aiohttp>=3.8.0",
27
  "typing-extensions>=4.5.0",
28
  "pydantic-settings>=2.9.1",
 
29
  ]
30
 
31
  [project.optional-dependencies]
 
26
  "aiohttp>=3.8.0",
27
  "typing-extensions>=4.5.0",
28
  "pydantic-settings>=2.9.1",
29
+ "huggingface-hub>=0.32.4",
30
  ]
31
 
32
  [project.optional-dependencies]
src/config/settings.py CHANGED
@@ -3,10 +3,14 @@
3
  import os
4
  from functools import lru_cache
5
  from typing import Optional
 
6
 
7
  from pydantic import Field
8
  from pydantic_settings import BaseSettings
9
 
 
 
 
10
 
11
  class Settings(BaseSettings):
12
  """Application settings and configuration."""
@@ -14,6 +18,7 @@ class Settings(BaseSettings):
14
  # API Keys
15
  openai_api_key: Optional[str] = Field(default=None, env="OPENAI_API_KEY")
16
  anthropic_api_key: Optional[str] = Field(default=None, env="ANTHROPIC_API_KEY")
 
17
 
18
  # Job Search APIs
19
  linkedin_api_key: Optional[str] = Field(default=None, env="LINKEDIN_API_KEY")
@@ -30,8 +35,13 @@ class Settings(BaseSettings):
30
  embedding_dimension: int = Field(default=384, env="EMBEDDING_DIMENSION")
31
 
32
  # LLM Settings
33
- llm_provider: str = Field(default="openai", env="LLM_PROVIDER") # openai, anthropic
34
- llm_model: str = Field(default="gpt-3.5-turbo", env="LLM_MODEL")
 
 
 
 
 
35
  max_tokens: int = Field(default=300, env="MAX_TOKENS")
36
  temperature: float = Field(default=0.7, env="TEMPERATURE")
37
 
@@ -66,4 +76,14 @@ class Settings(BaseSettings):
66
  @lru_cache()
67
  def get_settings() -> Settings:
68
  """Get cached settings instance."""
69
- return Settings()
 
 
 
 
 
 
 
 
 
 
 
3
  import os
4
  from functools import lru_cache
5
  from typing import Optional
6
+ from dotenv import load_dotenv
7
 
8
  from pydantic import Field
9
  from pydantic_settings import BaseSettings
10
 
11
+ # Load environment variables from .env file
12
+ load_dotenv()
13
+
14
 
15
  class Settings(BaseSettings):
16
  """Application settings and configuration."""
 
18
  # API Keys
19
  openai_api_key: Optional[str] = Field(default=None, env="OPENAI_API_KEY")
20
  anthropic_api_key: Optional[str] = Field(default=None, env="ANTHROPIC_API_KEY")
21
+ hf_access_token: Optional[str] = Field(default=None, env="HF_ACCESS_TOKEN")
22
 
23
  # Job Search APIs
24
  linkedin_api_key: Optional[str] = Field(default=None, env="LINKEDIN_API_KEY")
 
35
  embedding_dimension: int = Field(default=384, env="EMBEDDING_DIMENSION")
36
 
37
  # LLM Settings
38
+ llm_provider: str = Field(
39
+ default="huggingface", env="LLM_PROVIDER"
40
+ ) # openai, anthropic, huggingface
41
+ llm_model: str = Field(default="deepseek/deepseek-v3-turbo", env="LLM_MODEL")
42
+ hf_inference_provider: str = Field(
43
+ default="novita", env="HF_INFERENCE_PROVIDER"
44
+ ) # novita, together, fireworks, etc.
45
  max_tokens: int = Field(default=300, env="MAX_TOKENS")
46
  temperature: float = Field(default=0.7, env="TEMPERATURE")
47
 
 
76
  @lru_cache()
77
  def get_settings() -> Settings:
78
  """Get cached settings instance."""
79
+ settings = Settings()
80
+
81
+ # Debug print the HF token
82
+ if settings.hf_access_token:
83
+ print(
84
+ f"πŸ”‘ HF Access Token loaded: {settings.hf_access_token[:20]}...{settings.hf_access_token[-10:]}"
85
+ )
86
+ else:
87
+ print("❌ No HF Access Token found in environment variables")
88
+
89
+ return settings
src/services/llm_service.py CHANGED
@@ -3,6 +3,8 @@
3
  from typing import Dict, Any, Optional
4
  import openai
5
  from anthropic import Anthropic
 
 
6
 
7
  from ..config import get_settings
8
  from .profile_service import UserProfile
@@ -15,6 +17,7 @@ class LLMService:
15
  self.settings = get_settings()
16
  self.openai_client = None
17
  self.anthropic_client = None
 
18
  self._initialize_clients()
19
 
20
  def _initialize_clients(self):
@@ -26,12 +29,29 @@ class LLMService:
26
  if self.settings.anthropic_api_key:
27
  self.anthropic_client = Anthropic(api_key=self.settings.anthropic_api_key)
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  def _get_client(self):
30
  """Get the appropriate LLM client based on provider setting."""
31
- if self.settings.llm_provider == "openai" and self.openai_client:
 
 
32
  return self.openai_client, "openai"
33
  elif self.settings.llm_provider == "anthropic" and self.anthropic_client:
34
  return self.anthropic_client, "anthropic"
 
 
35
  elif self.openai_client:
36
  return self.openai_client, "openai"
37
  elif self.anthropic_client:
@@ -81,13 +101,81 @@ class LLMService:
81
  print(f"Anthropic API error: {e}")
82
  return None
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  def generate_text(self, messages: list, max_tokens: int = None) -> Optional[str]:
85
  """Generate text using the configured LLM."""
86
  client, provider = self._get_client()
87
  if not client:
 
88
  return None
89
 
90
- if provider == "openai":
 
 
 
 
91
  return self._call_openai(messages, max_tokens)
92
  elif provider == "anthropic":
93
  return self._call_anthropic(messages, max_tokens)
@@ -130,10 +218,10 @@ JOB DESCRIPTION:
130
 
131
  CANDIDATE PROFILE:
132
  - Skills: {skills_text}
133
- - Experience: {profile.experience_level or 'Not specified'}
134
  - Career Goals: {profile.career_goals}
135
- - Location: {profile.location or 'Not specified'}
136
- - Education: {profile.education or 'Not specified'}
137
 
138
  RESUME SUMMARY:
139
  {profile.resume[:1000]} # Limit resume text
@@ -202,7 +290,7 @@ CONTEXT: {context}
202
 
203
  CANDIDATE BACKGROUND:
204
  - Skills: {", ".join(profile.skills[:8])}
205
- - Experience Level: {profile.experience_level or 'Not specified'}
206
  - Career Goals: {profile.career_goals}
207
  - Key Background: {profile.resume[:800]}
208
 
 
3
  from typing import Dict, Any, Optional
4
  import openai
5
  from anthropic import Anthropic
6
+ import requests
7
+ import json
8
 
9
  from ..config import get_settings
10
  from .profile_service import UserProfile
 
17
  self.settings = get_settings()
18
  self.openai_client = None
19
  self.anthropic_client = None
20
+ self.hf_client = None
21
  self._initialize_clients()
22
 
23
  def _initialize_clients(self):
 
29
  if self.settings.anthropic_api_key:
30
  self.anthropic_client = Anthropic(api_key=self.settings.anthropic_api_key)
31
 
32
+ if self.settings.hf_access_token:
33
+ # Use the new Inference Providers API
34
+ self.hf_client = {
35
+ "api_url": f"https://router.huggingface.co/{self.settings.hf_inference_provider}/v3/openai/chat/completions",
36
+ "headers": {
37
+ "Authorization": f"Bearer {self.settings.hf_access_token}",
38
+ "Content-Type": "application/json",
39
+ },
40
+ }
41
+ print(
42
+ f"πŸš€ HuggingFace Inference Providers initialized with {self.settings.hf_inference_provider} provider"
43
+ )
44
+
45
  def _get_client(self):
46
  """Get the appropriate LLM client based on provider setting."""
47
+ if self.settings.llm_provider == "huggingface" and self.hf_client:
48
+ return self.hf_client, "huggingface"
49
+ elif self.settings.llm_provider == "openai" and self.openai_client:
50
  return self.openai_client, "openai"
51
  elif self.settings.llm_provider == "anthropic" and self.anthropic_client:
52
  return self.anthropic_client, "anthropic"
53
+ elif self.hf_client:
54
+ return self.hf_client, "huggingface"
55
  elif self.openai_client:
56
  return self.openai_client, "openai"
57
  elif self.anthropic_client:
 
101
  print(f"Anthropic API error: {e}")
102
  return None
103
 
104
+ def _call_huggingface(
105
+ self, messages: list, max_tokens: int = None
106
+ ) -> Optional[str]:
107
+ """Call HuggingFace Inference Providers API."""
108
+ try:
109
+ # Use OpenAI-compatible format for Inference Providers
110
+ payload = {
111
+ "model": self.settings.llm_model,
112
+ "messages": messages,
113
+ "max_tokens": max_tokens or self.settings.max_tokens,
114
+ "temperature": self.settings.temperature,
115
+ "stream": False,
116
+ }
117
+
118
+ print(
119
+ f"πŸ”„ Calling HF Inference Providers API with {self.settings.hf_inference_provider} provider..."
120
+ )
121
+ print(f"πŸ“‘ URL: {self.hf_client['api_url']}")
122
+ print(f"πŸ€– Model: {self.settings.llm_model}")
123
+
124
+ response = requests.post(
125
+ self.hf_client["api_url"],
126
+ headers=self.hf_client["headers"],
127
+ json=payload,
128
+ timeout=60,
129
+ )
130
+
131
+ print(f"πŸ“Š Response status: {response.status_code}")
132
+
133
+ if response.status_code == 200:
134
+ result = response.json()
135
+ print(f"βœ… Success: {result}")
136
+
137
+ # Handle OpenAI-compatible response format
138
+ if "choices" in result and len(result["choices"]) > 0:
139
+ content = result["choices"][0]["message"]["content"]
140
+ return content.strip()
141
+ else:
142
+ print(f"❌ Unexpected response format: {result}")
143
+ return None
144
+ else:
145
+ error_detail = response.text
146
+ print(
147
+ f"❌ HuggingFace Inference Providers API error: {response.status_code} - {error_detail}"
148
+ )
149
+
150
+ # Try to parse error details
151
+ try:
152
+ error_json = response.json()
153
+ if "error" in error_json:
154
+ print(f"πŸ” Error details: {error_json['error']}")
155
+ except:
156
+ pass
157
+
158
+ return None
159
+
160
+ except requests.exceptions.Timeout:
161
+ print(f"⏱️ HuggingFace API timeout error")
162
+ return None
163
+ except Exception as e:
164
+ print(f"❌ HuggingFace API error: {e}")
165
+ return None
166
+
167
  def generate_text(self, messages: list, max_tokens: int = None) -> Optional[str]:
168
  """Generate text using the configured LLM."""
169
  client, provider = self._get_client()
170
  if not client:
171
+ print("❌ No LLM client available")
172
  return None
173
 
174
+ print(f"🎯 Using {provider} provider for text generation")
175
+
176
+ if provider == "huggingface":
177
+ return self._call_huggingface(messages, max_tokens)
178
+ elif provider == "openai":
179
  return self._call_openai(messages, max_tokens)
180
  elif provider == "anthropic":
181
  return self._call_anthropic(messages, max_tokens)
 
218
 
219
  CANDIDATE PROFILE:
220
  - Skills: {skills_text}
221
+ - Experience: {profile.experience_level or "Not specified"}
222
  - Career Goals: {profile.career_goals}
223
+ - Location: {profile.location or "Not specified"}
224
+ - Education: {profile.education or "Not specified"}
225
 
226
  RESUME SUMMARY:
227
  {profile.resume[:1000]} # Limit resume text
 
290
 
291
  CANDIDATE BACKGROUND:
292
  - Skills: {", ".join(profile.skills[:8])}
293
+ - Experience Level: {profile.experience_level or "Not specified"}
294
  - Career Goals: {profile.career_goals}
295
  - Key Background: {profile.resume[:800]}
296
 
uv.lock CHANGED
@@ -886,6 +886,7 @@ dependencies = [
886
  { name = "faiss-cpu" },
887
  { name = "gradio", extra = ["mcp"] },
888
  { name = "httpx" },
 
889
  { name = "lxml" },
890
  { name = "numpy" },
891
  { name = "openai" },
@@ -922,6 +923,7 @@ requires-dist = [
922
  { name = "flake8", marker = "extra == 'dev'", specifier = ">=6.0.0" },
923
  { name = "gradio", extras = ["mcp"], specifier = ">=5.0.0" },
924
  { name = "httpx", specifier = ">=0.24.0" },
 
925
  { name = "isort", marker = "extra == 'dev'", specifier = ">=5.12.0" },
926
  { name = "lxml", specifier = ">=4.9.0" },
927
  { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0.0" },
 
886
  { name = "faiss-cpu" },
887
  { name = "gradio", extra = ["mcp"] },
888
  { name = "httpx" },
889
+ { name = "huggingface-hub" },
890
  { name = "lxml" },
891
  { name = "numpy" },
892
  { name = "openai" },
 
923
  { name = "flake8", marker = "extra == 'dev'", specifier = ">=6.0.0" },
924
  { name = "gradio", extras = ["mcp"], specifier = ">=5.0.0" },
925
  { name = "httpx", specifier = ">=0.24.0" },
926
+ { name = "huggingface-hub", specifier = ">=0.32.4" },
927
  { name = "isort", marker = "extra == 'dev'", specifier = ">=5.12.0" },
928
  { name = "lxml", specifier = ">=4.9.0" },
929
  { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0.0" },