Ports d'E/S typés
Chaque fonction expose des ports d’entrée et de sortie nommés. Les ports portent des types issus du catalogue canonique ; l’éditeur de workflow s’en sert pour valider les connexions au tracé, et le worker s’en sert pour acheminer les valeurs vers les bons emplacements dans le conteneur.
Forme de la déclaration
Section intitulée « Forme de la déclaration »inputs: et outputs: sous une fonction sont des dictionnaires dont
chaque clé est un nom de port et chaque valeur décrit le port :
functions: size: handler: bolts:size inputs: diameter: type: Length description: "Diamètre nominal du boulon (m)." required: true # défaut : true load: type: Force required: false # entrée optionnelle outputs: stress: type: Stress description: "Contrainte équivalente de von Mises." report: type: File[pdf] required: false # les sorties File peuvent être optionnellesChaque entrée a la même forme :
| Champ | Type | Requis | Notes |
|---|---|---|---|
type | chaîne ou mapping | oui | Expression de type — voir ci-dessous. |
description | chaîne | non | Texte libre, affiché dans l’éditeur et l’AutoForm. |
required | booléen | non | Défaut : true. Sémantique détaillée plus bas. |
symbol | chaîne | non | Notation du document source (LaTeX accepté, ex. "F_0^{max}") — affichée en badge à côté du nom du port. |
unit | chaîne | non | Unité de la grandeur (ex. "N*m") — affichée en badge ; purement informative, aucune conversion automatique. |
Le nom du port est la clé du dictionnaire — il doit être une chaîne
non vide, typiquement en snake_case. C’est l’argument nommé que le
runner passe à votre handler en mode A, et la clé JSON dans
/workspace/in/data.json en modes B/C.
Expressions de types
Section intitulée « Expressions de types »Chaque valeur de type: est analysée contre le
catalogue de types. Le parseur accepte :
Types nominaux
Section intitulée « Types nominaux »type: Lengthtype: Forcetype: Stresstype: Integertype: StringTous les noms canoniques du catalogue. Le sous-typage s’applique à la
connexion — Force → Numeric passe, Force → Length est refusé.
Fichiers
Section intitulée « Fichiers »type: File # toute extensiontype: File[pdf] # uniquement .pdftype: File[step,iges,brep] # intersection autoriséeLes extensions sont insensibles à la casse et n’incluent jamais le
point de tête (File[.pdf] est refusé — écrivez File[pdf]). À la
validation, la plateforme compare l’extension réelle du fichier
téléversé à la liste déclarée.
Collections de fichiers (list[File])
Section intitulée « Collections de fichiers (list[File]) »type: list[File] # toute extension, plusieurs fichierstype: list[File[mmed,med]] # intersection d'extensions contrainteÀ utiliser quand une fonction accepte un ensemble non borné de
fichiers auxiliaires sous un même port logique — maillages,
includes, tables de données, etc. — sans avoir à inventer une liste
fixe de ports nommés. Le frontend affiche un sélecteur multi-fichiers
(un upload par fichier avec un bouton « ajouter un fichier ») ; l’API
transmet chaque upload sous le même var_name.
Différence de staging avec un port File unique. Les fichiers
d’une collection sont écrits sous leur nom d’origine dans
/workspace/in/files/<original>, pas renommés en <port_name>.<ext>.
Cela préserve les références par nom de fichier dans les payloads
utilisateur (par exemple un .export code_aster écrit
F mmed forma01a.mmed D 20 se résout tel quel — la plateforme ne le
renomme pas en mesh.mmed). Quand deux uploads partagent le même nom
d’origine, le second reçoit un suffixe _2 avant la dernière
extension, et ainsi de suite.
Sorties. Une fonction peut aussi déclarer un port sortie
list[File]. Elle écrit la collection dans le sous-dossier
out/files/<port>/ — un fichier par membre, sous n’importe quel nom.
Une sortie list[File] required est satisfaite dès que ce dossier
contient au moins un fichier. Les nœuds de workflow en aval se lient
à toute la collection par une seule arête, exactement comme pour une
entrée list[File].
type: list[Force] # liste homogènetype: list[Vector3]type: list[Torsor]Le sous-typage s’applique élément par élément : list[Force] → list[Numeric]
✅, list[Force] → list[Length] ❌.
Dictionnaires
Section intitulée « Dictionnaires »type: dict[str, Length]type: dict[str, Force]Toujours indexé par chaîne. Seul le type de valeur est analysé — la
clé est figée à str.
Littéraux
Section intitulée « Littéraux »type: literal[8.8, 10.9, 12.9] # seules ces valeurs sont validesUtile pour les énumérations comme les classes de qualité. Toute valeur hors liste échoue à la validation.
Structures inline
Section intitulée « Structures inline »Forme chaîne ou forme YAML mapping, équivalentes :
# forme chaînetype: "{F: Force3, M: Moment3}"
# forme YAMLtype: F: Force3 M: Moment3Les structures inline correspondent champ par champ à la connexion —
ajouter ou retirer un champ change le type. Pour des structures
récurrentes, définissez-les dans le catalogue (ou utilisez le
canonique Torsor pour {F, M}).
Sémantique de required
Section intitulée « Sémantique de required »La sémantique de required dépend du côté et du type du port :
| Type de port | required: true | required: false |
|---|---|---|
| Entrée | L’appelant doit fournir la valeur ; absence → 422 à la soumission. | Peut être omise ; absente des kwargs / in/data.json. |
| Sortie (File) | Le worker vérifie que le fichier est dans out/files/ ; absence → run en échec. | Le worker accepte présence comme absence. |
Sortie (list[File]) | Satisfaite ssi out/files/<port>/ contient au moins un fichier ; vide → run en échec. | Le worker accepte une collection vide ou absente. |
| Sortie (non-File) | La fonction doit retourner cette clé dans son dictionnaire de sortie. | n/a — les sorties non-File sont toujours requises, le parseur refuse required: false dessus. |
Le défaut est true partout. Ne déclarez required: false que quand
l’absence de la valeur porte un sens réel (un fichier de diagnostic
optionnel, une entrée par défaut).
Le type Object
Section intitulée « Le type Object »Object est le sommet de la hiérarchie : il accepte n’importe quelle
forme JSON. Il est utilisable des deux côtés — entrée et sortie.
inputs: config: { type: Object } # forme libre côté appelantoutputs: result: { type: Object } # le contenu dépend du calculQuand l’utiliser. Pour des fonctions méta-paramétrées dont la forme
exacte de la sortie dépend d’une entrée — par exemple un solveur
Excel dont les clés de sortie sont choisies par la table de
correspondance fournie par l’appelant. Les connexions de workflow
issues d’une sortie Object se branchent sur des ports d’entrée
acceptant Object (sommet du treillis), donc le typage statique
côté workflow reste cohérent.
Quand l’éviter. Si la sortie a une forme connue, préférez un type
nominal, un dict[str, T] typé, ou plusieurs sorties distinctes. Un
port Object est opaque aux nœuds en aval — l’éditeur ne peut pas
proposer d’autocomplétion des champs ni détecter une erreur de
branchement avant l’exécution.
Ports File — sémantique des extensions
Section intitulée « Ports File — sémantique des extensions »Pour les entrées File, le fichier est préparé à
/workspace/in/files/<port_name>.<ext> où <ext> provient du
fichier téléversé par l’appelant. Le runner du mode A passe un
pathlib.Path indexé par le nom du port ; les modes B/C le lisent
eux-mêmes.
Pour les entrées collection File (list[File]), chaque upload est
préparé à /workspace/in/files/<nom_original> (sans renommage vers
<port_name>). Utilisez cette forme quand le payload utilisateur
référence des noms de fichiers spécifiques (par exemple un .export
code_aster déclarant F mmed forma01a.mmed D 20). Voir
le contrat de runtime pour la disposition
exacte des dossiers.
Pour les sorties File, le worker attend exactement un fichier sous
/workspace/out/files/ dont le stem correspond au nom du port —
out/files/report.pdf pour une sortie nommée report. L’extension
est préservée verbatim dans la réponse.
La contrainte d’extension déclarée dans le manifest (File[pdf]) est
vérifiée contre :
- l’extension réelle des fichiers d’entrée à la soumission,
- l’extension réelle écrite sous
out/files/après exécution.
Introspection en mode A
Section intitulée « Introspection en mode A »En mode A, la signature annotée du handler est lue à la lecture du manifest pour produire le schéma du formulaire d’exécution (AutoForm). C’est utile pour qu’un utilisateur puisse lancer la fonction depuis l’interface sans connaître son catalogue.
def size(diameter: float, load: float) -> dict: return {"stress": load / area(diameter)}Sans inputs: dans mecapy.yml, l’introspection produit deux champs
numériques (diameter, load) dans l’AutoForm. Le retour dict non
typé tombe sur un schéma permissif.
L’introspection ne nourrit pas les ports typés du workflow. Pour des fonctions destinées à être composées dans des workflows, déclarez explicitement les ports avec les types du catalogue :
functions: stress_bolt: handler: handler:stress_bolt inputs: torsor: { type: Torsor, description: "Torseur force/moment dans la section du boulon." } diameter: { type: Length, description: "Diamètre nominal du boulon (m)." } pitch: { type: Length, description: "Pas du filetage (m)." } outputs: sigma_eq: { type: Stress, description: "Contrainte équivalente de von Mises (Pa)." }En modes B et C, inputs: / outputs: sont obligatoires : il n’y a
pas de signature Python à lire.
Persistance et exposition
Section intitulée « Persistance et exposition »Une fois lus, les ports typés sont stockés sur la version de fonction et exposés par l’API en lecture. Le formulaire d’exécution et le vérificateur de connexions de l’éditeur de workflow les consomment — les déclarer correctement, c’est ce qui débloque l’expérience riche en aval.