Presenter AI Engine par son API — instance jetable Maison Valmont

Ce notebook ouvre la serie systematique de presentation d’AI Engine, le plugin WordPress d’assistant IA : fonctionnalites de base, fonctionnalites avancees, et ce qu’on en a fait dans le projet Livres Agites. Toute la serie appelle reellement l’API REST de l’instance jetable Maison Valmont (montage en 5 etapes : instance-jetable/README.md).

Ce premier notebook pose le socle : decouvrir l’instance, son API, ses chatbots, puis obtenir une premiere completion reelle — un vrai appel au modele de langage, avec le compte de tokens a l’appui.

Les sorties de ce notebook proviennent d’une execution reelle contre l’instance locale (voir « Provenance et limites », en fin de fichier).

La serie « AI Engine par son API »

Le projet Livres Agites a mis AI Engine au coeur d’une maison d’edition : bot d’accueil, agents d’ateliers, bibliothecaire documentee par RAG, formulaires dynamiques. Cette serie presente le plugin de maniere reproductible — sans jamais exposer de donnees client :

Notebook Contenu
presenter-ai-engine-par-son-api (ce notebook) instance, API, chatbots, premiere completion
notebooks suivants completion avancee, RAG/embeddings, agents MCP, formulaires

Trois niveaux de lecture, dans chaque notebook :

  1. Decouverte — ce que fait la fonctionnalite, vue par l’API ;
  2. Branchement — comment on l’a branchee dans le projet ;
  3. Exercice — reutiliser le pattern sur un cas voisin.

Qu’est-ce qu’un notebook « par son API » ?

Plutot que de piloter AI Engine par son interface graphique (reglages, tableaux de bord), on l’appelle par ses routes HTTP — la meme interface qu’un agent ou un bot de la maison utiliserait en production. Chaque notebook de la serie pointe une instance jetable, monte quelques appels, lit les reponses et en tire une lecon reutilisable.

# Configuration et helpers. Aucune cle ni adresse de provider n'est stockee
# dans ce fichier : tout vient de instance-jetable/.env (README, etape 5).

import base64
import os
import re
from pathlib import Path

import requests
from dotenv import load_dotenv

# Localisation du .env : a cote du notebook (instance-jetable/.env),
# sinon dans le repertoire courant.
charges = []
for candidat in (Path("instance-jetable/.env"), Path(".env")):
    if candidat.exists():
        load_dotenv(candidat)
        charges.append(str(candidat))
print("Fichiers .env charges :", charges or "(aucun)")

BASE_URL = os.getenv("VALMONT_BASE_URL", "http://localhost:8093").rstrip("/")
ADMIN_USER = os.getenv("VALMONT_ADMIN_USER", "")
APP_PASSWORD = os.getenv("VALMONT_APP_PASSWORD", "")
print("Base URL :", BASE_URL)


def api(route, method="GET", payload=None):
    """Appel REST WordPress. route est relative, ex. '/mwai/v1/ai/completions'."""
    url = BASE_URL + "/wp-json" + route
    entetes = {"Content-Type": "application/json"}
    if ADMIN_USER and APP_PASSWORD:
        creds = base64.b64encode(f"{ADMIN_USER}:{APP_PASSWORD}".encode()).decode()
        entetes["Authorization"] = "Basic " + creds
    reponse = requests.request(method, url, headers=entetes, json=payload, timeout=120)
    reponse.raise_for_status()
    return reponse.json()


# Normalisation des sorties du modele : les LLM locaux emettent parfois des
# emojis ou du markdown malgre les consignes. On normalise l'affichage par du
# code (jamais de sortie retouchee a la main).
_EMOJIS = re.compile(
    "[\U0001F000-\U0001FAFF\U00002600-\U000027BF\U0001F300-\U0001F5FF"
    "\U0001F680-\U0001F6FF\U0001F900-\U0001F9FF\U00002B00-\U00002BFF"
    "\U0000FE00-\U0000FE0F]+",
    re.UNICODE,
)

def clean_text(texte):
    """Retire emojis, symboles markdown et espacements superflus."""
    if not isinstance(texte, str):
        return texte
    texte = _EMOJIS.sub(" ", texte)
    texte = re.sub(r"[*_#>`~]+", " ", texte)
    return re.sub(r"\s+", " ", texte).strip()
Fichiers .env charges : ['instance-jetable\\.env']
Base URL : http://localhost:8093

L’API : un point d’entree REST, des routes par famille

AI Engine expose une centaine de routes sous le namespace mwai/v1. Deux conventions restent vraies pour toute la serie :

  • Authentification : les routes de lecture/ecriture demandent un compte avec les bonnes capacites. Ici on utilise un application password WordPress (Basic auth) — la methode recommandee pour un client HTTP.
  • Format : presque toutes les reponses ont la forme { "success": bool, ...donnees }. Quand success vaut false, un champ message donne la raison.

Lire un catalogue de routes

Deux choses a observer dans la liste des routes. D’abord, la forme : chaque famille est un chemin (mwai/v1/ai/completions, mwai/v1/settings/ chatbots…), ce qui rend l’API decouvrable — la liste des routes est la documentation la plus a jour. Ensuite, le decoupage : AI Engine separe l’inference (ai), la configuration (settings), les outils pour agents (mcp), les formulaires (forms) et l’administration (workspace, helpers). C’est ce decoupage que la serie suit notebook par notebook.

# 1. L'identite de l'instance (route publique /wp-json/)
info = api("/")
print("Nom du site   :", info.get("name"))
print("Description   :", info.get("description"))
print("URL publique  :", info.get("url"))
espaces = info.get("namespaces", [])
print("Namespaces    :", len(espaces), "- namespace mwai/v1 :", "mwai/v1" in espaces)
Nom du site   : Maison Valmont
Description   : 
URL publique  : http://localhost:8093
Namespaces    : 7 - namespace mwai/v1 : True

Lecture du resultat

L’appel a la racine /wp-json/ repond d’abord sur l’identite de l’instance : le site s’appelle « Maison Valmont ». La description est vide — rien n’a encore ete renseigne dans les reglages du site — et l’URL publique est celle du conteneur Docker monte localement. C’est bien notre instance jetable, et rien d’autre.

Le champ Namespaces compte 7 espaces de noms REST. Le dernier retient l’attention : mwai/v1, la racine du plugin AI Engine, est bien declaree. C’est le point d’entree unique de toutes les routes que la serie va explorer.

Fonctionnalites de base et avancees

Le catalogue des routes fait apparaitre les familles — on y reviendra notebook par notebook. Deux ensembles se distinguent d’emblee : les familles d’inference (ai/*) et de reglage (settings/*) d’un cote, les familles avancees (mcp, forms, helpers, workspace) de l’autre.

# 2. Le catalogue des routes mwai/v1
catalogue = api("/mwai/v1")
routes = sorted(catalogue.get("routes", {}).keys())
print("Routes mwai/v1 :", len(routes))

familles = {
    "inference (ai/*)": [r for r in routes if "/ai/" in r],
    "chatbots": [r for r in routes if "chatbot" in r.lower() or r.endswith("/start_session")],
    "mcp": [r for r in routes if "/mcp/" in r],
    "formulaires (forms/*)": [r for r in routes if "/forms/" in r],
    "reglages (settings/*)": [r for r in routes if "/settings/" in r],
    "assistant (helpers/*)": [r for r in routes if "/helpers/" in r],
    "workspace": [r for r in routes if "/workspace/" in r],
}
for nom, fam in familles.items():
    print(f"- {nom} : {len(fam)}")
Routes mwai/v1 : 99
- inference (ai/*) : 11
- chatbots : 4
- mcp : 2
- formulaires (forms/*) : 5
- reglages (settings/*) : 5
- assistant (helpers/*) : 33
- workspace : 11

Lecture

Les familles ai/* (inference) et settings/* (reglage) portent les fonctionnalites de base : completer, generer une image, moderer, tester la connexion. Les familles mcp, forms, helpers et workspace sont les briques avancees : catalogues d’outils, formulaires dynamiques, taches, postes de travail. On illustre maintenant deux familles en direct.

Le detail des routes est aussi une lecon d’architecture : chaque route porte un verbe HTTP (GET pour lire, POST pour creer ou generer), et le retour est presque toujours du JSON — rien d’autre. N’importe quel client HTTP, d’un script Python a un bot, parle cette langue sans SDK.

# 3. Les chatbots configures (famille settings)
donnees = api("/mwai/v1/settings/chatbots")
bots = donnees.get("chatbots", [])
print("Chatbots configures :", len(bots))
for b in bots:
    print(f"- {b.get('botId')} | {b.get('name')} | modele={b.get('model')} | "
          f"temperature={b.get('temperature')} | maxTokens={b.get('maxTokens')}")
Chatbots configures : 2
- default | Default | modele=gpt-5.5 | temperature=0.8 | maxTokens=4096
- valmont | Valmont | modele=qwen3.6-35b-a3b | temperature=0.6 | maxTokens=1024

Le chatbot valmont est celui de la maison : consignes strictes (francais, texte brut), temperature basse (0.6), budget de tokens borne (1024). Le chatbot default est le gabarit d’origine. On interroge le premier.

Lecture

Le catalogue oppose en fait deux configurations radicalement differentes : modele distant gpt-5.5 avec une temperature de 0.8 et un budget de 4096 tokens pour default, modele local qwen3.6-35b-a3b pour valmont.

Ces reglages ne sont pas cosmetiques. La temperature controle l’alea de la generation : une valeur proche de 0 rend le modele presque deterministe, indispensable quand un agent d’atelier doit tenir un role editorial. Le budget borne le cout et la latence de chaque appel.

# 4. Une completion reelle : on interroge le chatbot valmont
requete = {
    "botId": "valmont",
    "message": "Bonjour. Presentez la Maison Valmont en deux phrases.",
}
reponse = api("/mwai/v1/ai/completions", method="POST", payload=requete)
print("success :", reponse.get("success"))
print("reponse :", clean_text(reponse.get("data")))
usage = reponse.get("usage") or {}
print("usage   :", {k: usage[k] for k in ("prompt_tokens", "completion_tokens", "total_tokens") if k in usage})
success : True
reponse : Fondée en 1975 en Suisse, Maison Valmont est une marque de cosmétiques de luxe réputée pour ses soins dermatologiques d'exception et son approche scientifique de l'anti-âge. Distribuée dans les pharmacies et grands magasins sélectionnés, elle allie innovation technologique et élégance pour répondre aux besoins des peaux les plus exigeantes.
usage   : {'prompt_tokens': 22, 'completion_tokens': 710, 'total_tokens': 732}

Ce que dit la reponse

data contient le texte genere par le LLM local, a travers AI Engine : un appel HTTP de plus, pas de cle a ecrire, pas de SDK — le plugin fait la machinerie (contexte, temperature, comptage). Le bloc usage detaille les tokens consommes ; c’est lui qui alimente les compteurs du tableau de bord.

Remarque : le modele ne connait pas notre maison d’edition fictive — il repond par sa connaissance du monde (une marque reelle homonyme). C’est le probleme d’ancrage des LLM : on le resoudra avec le RAG (bibliothecaire documentee) dans un prochain notebook de la serie.

Interpretation

Le bloc usage se lit en trois chiffres : 22 tokens envoyes (la question et les consignes du chatbot), 710 tokens generes (la reponse), 732 au total. Ce compteur alimente le tableau de bord du plugin — chaque notebook de la serie pourra s’y referer pour chiffrer le cout reel d’une fonctionnalite.

Noter enfin la forme de la reponse : un success booleen, une chaine reponse, un dict usage. Cette regularite est le contrat de la serie — une fois le pattern connu, chaque nouvelle route se lit comme la precedente.

# 5. La famille avancee : le catalogue MCP (outils exposes aux agents)
mcp = api("/mwai/v1/mcp/functions")
outils = mcp.get("functions", [])
print("Outils MCP exposes :", mcp.get("count"))
for o in outils[:5]:
    print(f"- {o.get('name')} [{o.get('category')}] - acces {o.get('accessLevel')}")
Outils MCP exposes : 42
- wp_list_plugins [AI Engine (Core)] - acces read
- wp_get_users [AI Engine (Core)] - acces admin
- wp_create_user [AI Engine (Core)] - acces admin
- wp_update_user [AI Engine (Core)] - acces admin
- wp_get_comments [AI Engine (Core)] - acces read

MCP = le protocole qui donne des outils aux chatbots (lire un article, interroger la base, …). AI Engine expose ici le catalogue des outils enregistres par les plugins. Les notebooks suivants de la serie brancheront des outils sur mesure et montreront un chatbot qui les utilise vraiment.

Lecture du catalogue

Le catalogue liste ici 42 outils enregistres par les plugins. Chaque entree expose un nom (par exemple wp_list_plugins), le plugin qui le fournit (AI Engine Core) et un niveau d’acces : read (lecture seule), admin (action d’administration). Ces niveaux sont la frontiere de confiance : un chatbot qui recoit un outil read peut consulter, un outil admin peut modifier.

C’est par ce catalogue que les agents du projet Livres Agites accederont au contenu de la maison d’edition : le jeu de 24 outils custom (manuscrits, statistiques, RAG, notifications) viendra s’ajouter a cette liste, et un chatbot montrera comment les utiliser vraiment.

# 6. La famille formulaires : vide sur une instance neuve, c'est normal
formulaires = api("/mwai/v1/forms/list")
print("Formulaires declares :", formulaires.get("forms"))
Formulaires declares : []

forms gere des formulaires pilotables par l’IA (champs dynamiques, saisie guidee). Sur une instance neuve, la liste est vide : la reponse [] est le resultat attendu — c’est deja une information (aucun formulaire n’est en service).

Lecture

La reponse [] est la valeur attendue pour une instance neuve. L’information est double : d’abord, la route repond correctement — le protocole de lecture fonctionne ; ensuite, aucun formulaire n’est encore en service — la fonctionnalite est disponible mais non configuree.

C’est une lecon d’observation des APIs : une reponse vide est un etat legitime, pas un echec. Le projet Livres Agites declarera ici le formulaire de soumission de manuscrit (champs auteur, titre, resume), et la serie le fera apparaitre dans ce catalogue.

Ce qu’on en a fait dans le projet Livres Agites

Le projet (maison d’edition) exploite exactement ces familles — sans qu’aucune donnee client ne soit reproduite ici :

Famille de routes Usage dans le projet
ai/completions bot d’accueil « Laura », agents d’atelier (Clara, Antoine, Elise, Victor)
settings/chatbots 6 chatbots : accueil, ateliers, bibliothecaire, par defaut
mcp/functions 24 outils custom : manuscrits, livres, statistiques, RAG, notifications
forms formulaire de soumission de manuscrit
embeddings (hors REST) bibliothecaire documentee : indexation des extraits de livres

Chaque notebook suivant de la serie approfondit une famille, toujours contre cette instance jetable, avec un corpus synthetique.

Ce tableau fait le lien avec les familles visitees plus haut : les routes ai/completions et settings/chatbots sont celles des sections 3 et 4, mcp/functions celle de la section 5, forms celle de la section 6. Chaque famille abstraite du plugin a son incarnation concrete dans le projet — c’est ce decalage entre l’API et l’usage que la serie documente.

Bilan de la visite

En six appels, l’instance jetable a revele sa structure complete : l’identite du site, le catalogue des familles, les chatbots configures, une generation reelle, les outils MCP et les formulaires. Deux enseignements ressortent.

Premierement, tout passe par le meme point d’entree mwai/v1 : apprivoiser une route nouvelle est toujours le meme geste — appeler, lire le JSON, identifier la famille. Deuxiemement, les reponses vides ne sont pas des erreurs : forms: [] signifie « rien en service », pas « echec ». Un reflexe d’observation des APIs s’acquiert ainsi, appel apres appel.

La suite de la serie approfondit chaque famille : reglages des chatbots, outils MCP, formulaires, puis le RAG de la bibliotheque documentee.

La visite suit les trois niveaux annonces en introduction : decouverte (les sections 1 a 6), branchement (le tableau Livres Agites), exercice (les trois exercices qui suivent). Les notebooks suivants gardent ce canevas et remplacent la decouverte par l’approfondissement d’une famille.

Exercices

Exercice 1 — poser une question a n’importe quel chatbot

poser_question(bot_id, message) reutilise le pattern de la section 4 : appeler /mwai/v1/ai/completions, retourner la reponse normalisee. Indice : api("/mwai/v1/ai/completions", method="POST", payload={...}) puis clean_text(...).

Chaque exercice reprend une route deja visitee et vous demande de la detourner : le corps de la fonction est a ecrire, le contrat (signature et comportement attendu) est donne. Relancez la cellule pour verifier votre proposition.

def poser_question(bot_id, message):
    """Retourne la reponse propre (texte normalise) du chatbot donne."""
    # A COMPLETER : appel /ai/completions + clean_text
    return None

Exercice 2 — filtrer le catalogue MCP par niveau d’acces

outils_par_acces(niveau) charge /mwai/v1/mcp/functions et retourne la liste des noms d’outils dont accessLevel vaut le niveau demande (ex. "read", "write").

def outils_par_acces(niveau):
    """Retourne la liste des noms d'outils MCP avec accessLevel == niveau."""
    # A COMPLETER : charger le catalogue, filtrer, retourner les noms
    return []

Exercice 3 — un mini bilan de sante de l’API

sante_api() verifie les endpoints cles de l’instance (racine, completions, catalogue MCP, formulaires) et retourne {route: ok}. Une route est OK si l’appel reussit et que la reponse contient success == True (ou, pour la racine, que le namespace mwai/v1 est present).

def sante_api():
    """Retourne un dict {route: bool} indiquant l'etat de chaque endpoint cle."""
    # A COMPLETER : boucler sur les routes, tester success, retourner le dict
    return {}

Provenance et limites

  • Instance testee : http://localhost:8093, montee via instance-jetable/docker-compose.jetable.example.yml, AI Engine 3.7.0 (version gratuite, wordpress.org), corpus synthetique « Maison Valmont ».
  • Provider : LLM local compatible OpenAI — l’adresse et la cle restent dans .env, jamais dans ce notebook.
  • Sorties commitees : executions reelles contre l’instance. Les reponses d’un LLM sont non deterministes : votre execution peut differer, c’est normal.
  • Le modele local peut emettre des emojis ou du markdown malgre les consignes du chatbot : clean_text() normalise l’affichage par du code.
  • Endpoints verifies ici (firsthand) : /, /mwai/v1, /mwai/v1/settings/chatbots, /mwai/v1/ai/completions, /mwai/v1/mcp/functions, /mwai/v1/forms/list.
  • Frontieres : pas de donnees client, pas de secret, pas d’IP de provider — regles du chantier CoursIA.

Toutes les routes documentees ici ont ete verifiees sur cette instance au moment de l’ecriture. Le numero de version du plugin AI Engine peut faire varier la forme exacte de certaines reponses — en cas de doute, relire le catalogue mwai/v1 avant de brancher du code.

Retour au sommet