Noo88ear's picture
πŸš€ Initial deployment of Multi-Agent Job Application Assistant
7498f2c
from __future__ import annotations
import os
from typing import Optional
# Providers are optional; we import lazily
class LLMClient:
def __init__(self) -> None:
self.provider = os.getenv("LLM_PROVIDER", "openai").lower()
self.openai_key = os.getenv("OPENAI_API_KEY")
self.anthropic_key = os.getenv("ANTHROPIC_API_KEY")
self.gemini_key = os.getenv("GEMINI_API_KEY")
self._openai_client = None
self._anthropic_client = None
self._gemini_model = None
# Optional per-agent Gemini keys (fallback to default if missing)
self._agent_keys = {
"cv": os.getenv("GEMINI_API_KEY_CV") or self.gemini_key,
"cover": os.getenv("GEMINI_API_KEY_COVER") or self.gemini_key,
"chat": os.getenv("GEMINI_API_KEY_CHAT") or self.gemini_key,
"parser": os.getenv("GEMINI_API_KEY_PARSER") or self.gemini_key,
"match": os.getenv("GEMINI_API_KEY_MATCH") or self.gemini_key,
"tailor": os.getenv("GEMINI_API_KEY_TAILOR") or self.gemini_key,
}
# Preload if configured
if self.provider == "openai" and self.openai_key:
try:
from openai import OpenAI
self._openai_client = OpenAI(api_key=self.openai_key)
except Exception:
self._openai_client = None
elif self.provider == "anthropic" and self.anthropic_key:
try:
import anthropic
self._anthropic_client = anthropic.Anthropic(api_key=self.anthropic_key)
except Exception:
self._anthropic_client = None
elif self.provider == "gemini" and self.gemini_key:
# We will lazily configure per-call to support per-agent keys
try:
import google.generativeai as genai # noqa: F401
except Exception:
self._gemini_model = None
@property
def enabled(self) -> bool:
if self.provider == "openai":
return self._openai_client is not None
if self.provider == "anthropic":
return self._anthropic_client is not None
if self.provider == "gemini":
# If we have at least one usable key, consider enabled
return any([self.gemini_key] + list(self._agent_keys.values()))
return False
def generate(self, system_prompt: str, user_prompt: str, model: Optional[str] = None, max_tokens: int = 1200, agent: Optional[str] = None) -> str:
# Fallback behavior if no provider configured
if not self.enabled:
text = (system_prompt + "\n\n" + user_prompt)[: max_tokens * 4]
return text
provider = self.provider
if provider == "openai":
return self._generate_openai(system_prompt, user_prompt, model or os.getenv("LLM_MODEL", "gpt-4o-mini"), max_tokens)
if provider == "anthropic":
return self._generate_anthropic(system_prompt, user_prompt, model or os.getenv("LLM_MODEL", "claude-3-5-sonnet-latest"), max_tokens)
if provider == "gemini":
return self._generate_gemini(system_prompt, user_prompt, model or os.getenv("LLM_MODEL", "gemini-1.5-flash"), max_tokens, agent=agent)
# Unknown provider fallback
return (system_prompt + "\n\n" + user_prompt)[: max_tokens * 4]
def _generate_openai(self, system_prompt: str, user_prompt: str, model: str, max_tokens: int) -> str:
try:
response = self._openai_client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
temperature=0.4,
max_tokens=max_tokens,
)
return response.choices[0].message.content.strip()
except Exception:
return (system_prompt + "\n\n" + user_prompt)[: max_tokens * 4]
def _generate_anthropic(self, system_prompt: str, user_prompt: str, model: str, max_tokens: int) -> str:
try:
msg = self._anthropic_client.messages.create(
model=model,
max_tokens=max_tokens,
system=system_prompt,
messages=[{"role": "user", "content": user_prompt}],
temperature=0.4,
)
# Anthropic returns a list of content blocks
parts = []
for b in msg.content:
if hasattr(b, "text"):
parts.append(b.text)
elif isinstance(b, dict) and b.get("type") == "text":
parts.append(b.get("text", ""))
return "\n".join(p for p in parts if p).strip() or (system_prompt + "\n\n" + user_prompt)[: max_tokens * 4]
except Exception:
return (system_prompt + "\n\n" + user_prompt)[: max_tokens * 4]
def _generate_gemini(self, system_prompt: str, user_prompt: str, model: str, max_tokens: int, agent: Optional[str] = None) -> str:
try:
import google.generativeai as genai
# Resolve API key per agent if provided
api_key = self.gemini_key
if agent:
# Normalize agent to known keys
norm = agent.lower()
if norm == "general":
norm = "chat"
api_key = self._agent_keys.get(norm, self.gemini_key)
# Configure and call
genai.configure(api_key=api_key)
model_instance = genai.GenerativeModel(model)
prompt = system_prompt + "\n\n" + user_prompt
resp = model_instance.generate_content(prompt)
text = getattr(resp, "text", None)
if not text and hasattr(resp, "candidates") and resp.candidates:
text = resp.candidates[0].content.parts[0].text
return (text or prompt)[: max_tokens * 4]
except Exception:
return (system_prompt + "\n\n" + user_prompt)[: max_tokens * 4]
llm = LLMClient()