""" Configuration des LLMs et utilitaires de chargement. """ import os from dotenv import load_dotenv load_dotenv() import fitz import pymupdf4llm from langchain_groq import ChatGroq from langchain_openai import ChatOpenAI import litellm litellm.set_verbose = False def load_pdf(pdf_path: str) -> str: """Convertit un PDF en texte Markdown via pymupdf4llm (structure + formatage).""" return pymupdf4llm.to_markdown(pdf_path) def get_pdf_page_count(pdf_path: str) -> int: """Retourne le nombre de pages du PDF.""" try: doc = fitz.open(pdf_path) count = doc.page_count doc.close() return count except Exception: return 1 def load_pdf_first_page_text(pdf_path: str) -> str: """Extrait le texte brut de la première page en ordre de lecture (haut → bas, gauche → droite). Utilise fitz directement pour capturer les headers/sidebars que pymupdf4llm peut ignorer ou réordonner sur les CV à mise en page complexe (bannières colorées, colonnes, boîtes décoratives). """ doc = fitz.open(pdf_path) if not doc: return "" page = doc[0] # Récupère les blocs texte avec leurs coordonnées blocks = page.get_text("blocks") # (x0, y0, x1, y1, text, block_no, block_type) # Filtre les blocs texte (type 0) non vides text_blocks = [b for b in blocks if b[6] == 0 and b[4].strip()] # Trie par ligne (y arrondi à 10px pour gérer l'alignement imparfait), puis par colonne (x) text_blocks.sort(key=lambda b: (round(b[1] / 10) * 10, b[0])) doc.close() return "\n".join(b[4].strip() for b in text_blocks) OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") GROQ_API_KEY = os.getenv("GROQ_API_KEY") def get_big_llm(): """GPT-4o pour les tâches complexes — max_tokens élevé pour éviter la troncature JSON.""" return ChatOpenAI( model="gpt-5.2", temperature=0.0, api_key=OPENAI_API_KEY ) def get_small_llm(): """GPT-4o-mini pour l'extraction rapide.""" return ChatOpenAI( model="gpt-4o", temperature=0.0, max_tokens=1500, api_key=OPENAI_API_KEY )