Etend le pipeline de detection de techniques (tech-step-matcher.ts) pour resoudre, par clause, les metadonnees qui accompagnent une technique detectee : - Ingredients : nouvelle fonction findIngredientMentions (ingredient-matcher.ts) qui scanne le texte d'une clause contre le catalogue Ingredient existant (reutilise INGREDIENT_LABELS_FR/EN deja utilise par matchIngredientName), avec extraction best-effort de la quantite+unite immediatement avant la mention. - Ustensiles : nouveau catalogue Utensil (Prisma) + second PhraseMatcher cote service Python (intent_service/utensil_vocabulary.py), independant du textcat des techniques (pas d'interpretation necessaire pour un ustensile). POST /v1/process distingue desormais chaque entite via un champ kind (technique|utensil). - Persistance : deux nouvelles tables StepTechStepIngredient/ StepTechStepUtensil, liees a StepTechStep par sa cle composite (stepId, order), peuplees au moment du matching (recipe.service.ts) et exposees via StepTechStepView (packages/shared). Aucune analyse syntaxique ajoutee (le parser spaCy reste exclu du pipeline) : l'association se fait par appartenance a la clause deja calculee par splitIntoClauses. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
54 lines
1.7 KiB
Python
54 lines
1.7 KiB
Python
"""Modèles Pydantic du contrat HTTP — voir le plan de migration pour le
|
|
contrat exact attendu côté `apps/api` (`IntentServiceClient`,
|
|
`apps/api/src/lib/recipe-matching/intent-service-client.ts`).
|
|
|
|
Pas de `POST /v1/train` ici — ce service s'entraîne lui-même au démarrage
|
|
depuis `training_data.py` (voir `pipeline_registry.py`/`main.py`), plus
|
|
besoin d'un contrat HTTP pour ça.
|
|
"""
|
|
|
|
from typing import Literal
|
|
|
|
from pydantic import BaseModel
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# POST /v1/process
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class ProcessRequest(BaseModel):
|
|
locale: str
|
|
text: str
|
|
|
|
|
|
class EntityPayload(BaseModel):
|
|
"""Une mention candidate — technique ou ustensile, voir `kind` — offsets
|
|
caractère `[start, end)` dans `text`, convention identique à
|
|
`String.prototype.slice` côté `apps/api` (pas de décalage `+1` à
|
|
appliquer côté Node, contrairement à l'ancien `NlpManager` de
|
|
node-nlp).
|
|
|
|
`kind` distingue de quel `PhraseMatcher` la mention vient (voir
|
|
`locale_pipeline.py`'s `Entity`) — `apps/api`'s `tech-step-matcher.ts`
|
|
en a besoin pour savoir laquelle des deux résoudre (`TechStep.key` vs
|
|
`Utensil.key`)."""
|
|
|
|
uid: str
|
|
start: int
|
|
end: int
|
|
kind: Literal["technique", "utensil"] = "technique"
|
|
|
|
|
|
class ProcessResponse(BaseModel):
|
|
entities: list[EntityPayload]
|
|
intent: str | None
|
|
score: float
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GET /health
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class HealthResponse(BaseModel):
|
|
status: str
|