A bearded chef in a rustic kitchen carefully preparing a beef wellington dish, with pastry dough, herbs, and other ingredients visible on the counter.

Cet article détaille le développement d'un microservice Python nommé « MarkItLikeItsHot », conçu pour rationaliser la conversion de contenu en Markdown dans Willow CMS. Ce microservice utilise un wrapper FastAPI autour de la puissante bibliothèque MarkItDown de Microsoft, permettant une conversion transparente des fichiers, des URL et du texte brut en Markdown propre et formaté. Nous explorerons les technologies sous-jacentes, aborderons la structure du code et mettrons en évidence des aspects clés tels que le déploiement, les tests et la configuration de Docker.

Comprendre les technologies de base

Avant de plonger dans la mise en œuvre, clarifions les rôles des technologies clés :

Qu'est-ce que MarkItDown ?

MarkItDown , développé par Microsoft, est une bibliothèque robuste pour convertir divers formats de documents (tels que DOCX, PDF, HTML) en Markdown. Il gère les complexités de l'analyse de différentes structures de fichiers et de l'extraction de contenu, fournissant une sortie Markdown cohérente.

Qu'est-ce que FastAPI ?

FastAPI est un framework Web Python moderne et performant, idéal pour la création d'API. Il offre rapidité, simplicité d'utilisation et documentation API interactive automatique.

Définition des points de terminaison avec FastAPI

FastAPI utilise des décorateurs pour définir les points de terminaison de l'API. Par exemple :

@app.post("/convert/file")
async def convert_file(file: UploadFile = File(...)):
    # ... conversion logic ...

Ceci définit un point de terminaison POST à/convert/file qui accepte le téléchargement d'un fichier. De même, d'autres points de terminaison gèrent les conversions de texte et d'URL :

Dockerisation du microservice : le déploiement simplifié

Docker simplifie le déploiement et assure la cohérence entre les environnements. La configuration implique unDockerfile etdocker-compose.yml .

Le Dockerfile : construire l'image

Le Dockerfile définit l'environnement de notre microservice :

FROM python:3.13.1-slim

WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    ffmpeg \
    libsm6 \
    libxext6 \
    libmagic1 \
    tesseract-ocr \
    python3-venv \
    && rm -rf /var/lib/apt/lists/*

# Create and activate virtual environment
ENV VIRTUAL_ENV=/opt/venv
RUN python -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"

# Copy requirements first for better caching
COPY requirements.txt .

# Update pip and install requirements in the virtual environment
RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir -r requirements.txt

# Copy the rest of the application
COPY ./app /app/app

# Command to run the application
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

LeDockerfile met en place un environnement léger et efficace pour le microservice. Nous commençons par baser notre image surpython:3.13.1-slim , une version allégée de l'image officielle Python 3.13.1, minimisant la taille globale de l'image et la surface d'attaque. Nous définissons ensuite le répertoire de travail à l'intérieur du conteneur sur/app .

L'étape suivante consiste à installer les dépendances du système.apt-get , nous mettons à jour la liste des packages et installons des outils tels quegit ,ffmpeg ,tesseract-ocr , et d'autres. Ceux-ci sont nécessaires pour diverses fonctionnalités de la bibliothèque MarkItDown, telles que la gestion de différents formats de fichiers et le traitement d'images.rm -rf /var/lib/apt/lists/* la commande nettoie le cache apt pour réduire davantage la taille de l'image Docker.

Pour isoler les dépendances de notre projet et éviter les conflits, nous créons et activons un environnement virtuel Python. Nous définissons lesVIRTUAL_ENV variable d'environnement à/opt/venv et utiliserpython -m venv pour le créer. Modification duPATH garantit que les commandes de l'environnement virtuel sont prioritaires. Pour une meilleure mise en cache de build Docker, nous copionsrequirements.txt avant d'installer les dépendances Python. Cela permet à Docker de réutiliser les couches mises en cache si les exigences n'ont pas changé. Nous mettons ensuite à niveaupip et installez les packages Python du projet en utilisantpip install avec le--no-cache-dir indicateur pour éviter la mise en cache dans le conteneur (la mise en cache est gérée par le système en couches de Docker).

Enfin, nous copions le code de l'application dans le conteneur/app/app répertoire. LeCMD L'instruction spécifie la commande à exécuter au démarrage du conteneur. Elle utiliseuvicorn pour servir notre application FastAPI, en écoutant sur toutes les interfaces (0.0.0.0 ) sur le port 8000. Ce port est ensuite mappé à un port sur la machine hôte lors de l'exécution du conteneur, permettant un accès externe au microservice.

Docker Compose : orchestration du service

docker-compose.yml simplifie l'exécution du service avec des ports et des volumes définis :

services:
  markitdown:
    build: 
      context: ./markitdown-service
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    volumes:
      - ./markitdown-service/app:/app/app
    environment:
      - ENVIRONMENT=development

Construire et exécuter devient simple :

docker compose build
docker compose up -d

Plongée en profondeur dansmain.py :La logique de base

Lemain.py le fichier héberge la logique principale du microservice : lien vers main.py

  • Traitement et validation des données : modèles pydantiques ( TextInput , UrlInput ) garantir l’intégrité des données.

Ce modèle TextInput valide la structure du JSON entrant pour le point de terminaison /convert/text, en imposant la présence d'un champ de contenu et d'un champ d'options facultatif qui peut être un dictionnaire (dict) ou Aucun.

class TextInput(BaseModel):
    content: str
    options: Optional[dict] = None

Voici comment cela fonctionne en pratique :

Requête valide (avec options) :

{
  "content": "Some text here",
  "options": {
    "some_option": "some_value"
  }
}

Requête valide (sans options) :

{
  "content": "Some text here"
}

Dans le deuxième exemple, même si les options ne sont pas fournies, la demande est toujours valide et les options seront définies sur None dans la logique de l'application. Cette flexibilité permet aux utilisateurs de fournir des options supplémentaires si nécessaire, mais ne les rend pas obligatoires. À l'heure actuelle, les options ne sont pas transmises à la bibliothèque MarkItDown. Je les inclue pour une extensibilité future du microservice, comme les options de conversion lors du traitement d'un document ou d'une page Web.

  • Gestion des fichiers temporaires : La save_temp_file La fonction gère le stockage temporaire des fichiers téléchargés et du contenu Web récupéré, garantissant un traitement sécurisé et efficace.
  • Intégration MarkItDown : Le process_conversion La fonction encapsule la logique de conversion à l'aide de la bibliothèque MarkItDown. Cette fonction inclut également une logique spécialisée pour la gestion des URL de Wikipédia, optimisant ainsi l'extraction de contenu pour ces pages.
  • Gestion des erreurs : gestion des erreurs avec des exceptions personnalisées (FileProcessingError ,ConversionError ,URLFetchError ) et les exceptions HTTP fournissent des commentaires informatifs aux utilisateurs. Ceux-ci peuvent être étendus et devraient probablement être déplacés vers un fichier séparé à l'avenir.

Mise en œuvre de la limitation de débit

La limitation du débit est essentielle pour protéger le service contre les abus si je l'expose à Internet. Nous utilisonsslowapi à cet effet et un décorateur :

@limiter.limit(f"{settings.RATE_LIMIT_REQUESTS}/hour")
@app.post("/convert/text")
# ...

Cet exemple limite les requêtes à la/convert/text point de terminaison à 10 par heure (conformément aux paramètres ) par IP client. Lien vers le décorateur de limitation de débit pour ce point de terminaison

Structure et configuration du code

MarkItLikeItsHot maintient une base de code propre et SÈCHE (Ne vous répétez pas) grâce à des fonctions d'assistance et une configuration centralisée.

  • Fonctions d'assistance : fonctions telles que save_temp_file et process_conversion réduire la redondance et améliorer la lisibilité.
  • Configuration centralisée : Laconfig.py fichier, en utilisantpydantic_settings , gère les paramètres à l'échelle de l'application, permettant une personnalisation facile des paramètres tels queMAX_FILE_SIZE ,SUPPORTED_EXTENSIONS ,REQUEST_TIMEOUT , et les limites de débit. Lien vers config.py

Tests avec Pytest et Docker

MarkItLikeItsHot pytest pour les tests, et Docker simplifie l'exécution de ces tests dans un environnement cohérent. Le fichier docker-compose.yml définit un service de test distinct spécifiquement pour l'exécution des tests :

  test:
    build: 
      context: ./markitdown-service
      dockerfile: Dockerfile
    volumes:
      - ./markitdown-service:/app
    environment:
      - ENVIRONMENT=test
      - PYTHONPATH=/app
    entrypoint: python -m pytest
    command: /app/tests/test_api.py -v --capture=no
    depends_on:
      - markitdown
    profiles:
      - test

Voici comment letest le service est configuré :

  • build :Il utilise le mêmeDockerfile en tant qu'application principale, garantissant que l'environnement de test reflète l'environnement de production.
  • volumes : Monte le répertoire du projet (./markitdown-service ) dans le récipient à/app , rendant le code de test accessible.
  • environment : Définit leENVIRONMENT variable àtest . Cela peut être utilisé dans le code de l'application (par exemple, dansconfig.py ) pour ajuster les paramètres spécifiques à l'environnement de test, tels que les niveaux de journalisation ou les connexions à la base de données.PYTHONPATH=/app le paramètre garantit que le testeur peut trouver le code de l'application.
  • entrypoint : Définit le point d'entrée du conteneur commepython -m pytest . Ceci exécute lepytest coureur de tests.
  • command :Spécifie les arguments de ligne de commande pourpytest .-v permet une sortie détaillée et--capture=no désactive la capture de sortie, ce qui facilite la visualisation des instructions d'impression pendant l'exécution du test./app/tests/test_api.py spécifie le fichier de test à exécuter. Vous pouvez le modifier pour exécuter des tests ou des répertoires spécifiques dans letests dossier.
  • depends_on : Assure que lemarkitdown Le service (l'application principale) est en cours d'exécution avant le début des tests. Ceci est crucial pour les tests d'intégration qui interagissent avec les points de terminaison de l'API.
  • profiles : Utilise les profils Docker Compose pour inclure ce service uniquement lors de l'exécution de tests. Cela empêche le conteneur de test de démarrer lors de la simple exécutiondocker compose up Pour exécuter les tests, vous utiliseriezsudo docker-compose --profile test run --rm test où l'option rm supprimera le conteneur après avoir exécuté les tests.

Les tests couvrent un large éventail de scénarios :

La configuration de test utilise des montages définis dansconftest.py pour une configuration et un démontage efficaces des tests. Lisez la source de conftest.py .

Tester avec GitHub Actions

GitHub Actions automatise l'exécution de ces tests sur chaque requête push et pull, en maintenant une intégration continue. Lien vers le flux de travail GitHub Actions

Prochaines étapes

L'étape suivante consiste à intégrer MarkItLikeItsHot dans Willow CMS, en fournissant une interface conviviale pour convertir les téléchargements, les liens et le texte directement en brouillons Markdown pour les articles et les pages de blog. Les autres fonctionnalités incluront :

  • Prise en charge des clés API pour sécuriser le service
  • Exploration et exposition de fonctionnalités MarkItDown supplémentaires
  • ajout de la prise en charge des options de conversion personnalisées (via le actuellement inutiliséoptions champ)
  • mettre en œuvre une gestion des erreurs plus sophistiquée
  • exploration du traitement asynchrone pour améliorer les performances avec des fichiers très volumineux

Alors, jetez-y un œil. Le code est sur GitHub avec un bon fichier readme pour vous aider à commencer à l'utiliser pour vos propres projets.

Mots clés

CMS DockerCompose API rapide Code Édition de contenu Docker Python Actions GitHub Microservices Essai Fichier Docker