cv-tool — Stack technique

Pourquoi chaque technologie a été choisie

Python 3.11+

Langage principal du projet. Choix motivé par :

PDF extraction — pdfplumber

📦 pdfplumber >= 0.10.0

  • Préserve la structure (tables, colonnes, espacements)
  • API simple : pdfplumber.open(path).pages[i].extract_text()
  • Plus précis que PyPDF2 pour le texte layout-aware

Fallback : PyMuPDF (fitz) si pdfplumber manquant.

OCR — pdf2image + pytesseract

📦 pdf2image >= 1.16.0

Convertit chaque page du PDF en image (PNG) pour l'OCR.

📦 pytesseract >= 0.3.10

Wrapper Python de Tesseract 5.3+ pour la reconnaissance de caractères.

  • Langues : fra+eng (CV bilingue FR/EN)
  • DPI : 300 (bon compromis qualité/performance)

Alternatives possibles : easyocr paddleocr (plus précis mais plus lourd).

LLM local — client openai + LM Studio

📦 openai >= 1.0

Client API compatible OpenAI. On ne connecte pas à l'API d'OpenAI — on change juste le base_url :

client = openai.OpenAI(
    base_url="http://localhost:1234/v1",
    api_key="not-needed",
)
  • Aucune dépendance lourde (pas de transformers, pas de GGUF)
  • Compatible LM Studio, Ollama, et tout endpoint OpenAI-compatible
  • Le modèle actif dans LM Studio est utilisé par défaut

Pourquoi pas HuggingFace ? Dépendances trop lourdes (transformers, torch). LM Studio externalise tout dans le processus local.

Schéma JSON — pyyaml

📦 pyyaml >= 6.0

Le schéma YAML est converti en valeurs par défaut JSON. Le LLM utilise cette structure comme contrainte de sortie.

# YAML → valeurs par défaut JSON
personal:
  full_name: string    →  ""
  email: string        →  ""
experience:
  - company: string    →  [ { "company": "" } ]

CLI — click

📦 click >= 8.1

Framework CLI Python. Plus ergonomique qu'argparse :

@click.command()
@click.argument("pdf", type=click.Path(exists=True))
@click.option("-o", "--output", type=click.Path())
@click.option("--schema", "schema_path", type=click.Path())
@click.option("--extract", is_flag=True)
@click.option("--format", "output_format", type=click.Choice(["json", "docx"]))
@click.option("--verbose", "-v", is_flag=True)
def main(pdf, output, schema_path, extract, output_format, verbose):
    ...

Validation — pydantic

📦 pydantic >= 2.0

Validation du JSON produit par le LLM. Vérifie que la sortie respecte le schéma attendu. Si non conforme, le LLM peut être relancé avec les erreurs.

Export Word — python-docx

📦 python-docx >= 1.1.0 optionnel

Génère un CV Word professionnel à partir du JSON structuré. C'est un bonus — la vraie valeur est le JSON qui alimente Power Platform / Power Automate.

Pourquoi pas Word directement ? python-docx est fragile (mises en forme perdues). Power Platform est fait pour le templating.

Autres dépendances

📦 python-dotenv >= 1.0

Charge les variables d'environnement depuis .env (URL et clé LM Studio).

📦 rich >= 13.0

Output CLI coloré et formaté (utilisé dans cli.py pour les messages de statut).

pyproject.toml — Résumé

[project]
name = "cv-tool"
version = "0.1.0"
requires-python = ">=3.11"

dependencies = [
    "pdfplumber>=0.10.0",
    "PyMuPDF>=1.23.0",
    "pytesseract>=0.3.10",
    "pdf2image>=1.16.0",
    "pydantic>=2.0",
    "pyyaml>=6.0",
    "rich>=13.0",
    "click>=8.1",
    "openai>=1.0",
    "python-dotenv>=1.0",
]

[project.optional-dependencies]
ocr-alt = ["easyocr>=1.2", "paddleocr>=2.0"]
docx = ["python-docx>=1.1.0"]
dev = ["pytest>=7.0", "pytest-cov", "ruff", "mypy"]

[project.scripts]
cv-tool = "cv_tool.cli:main"