Boutons
Les boutons d'IA de l'admin, AiActions et AiResults, qui les voit, leur formulaire, les modes fill et choose, les propositions gardées à côté d'un contenu et la limite d'appels.
Un bouton d'IA vit dans l'écran d'un autre module : l'éditeur d'un post, la fenêtre d'un média. Le module IA fournit le composant, l'écran le place et décide de ce que devient la réponse. Rien n'est enregistré : la réponse remplit les champs, et la personne relit avant d'enregistrer.
Cette page sert à qui écrit un écran d'admin, dans un module ou dans le projet. Les deux composants se lisent par l'intégration ai, sans importer @fraza/module-ai : sans le module, getIntegration('ai') rend null et l'écran n'affiche rien.
Placer un bouton
'use client';
import { getIntegration } from '@fraza/core/integrations';
export function SummaryButton({ request, onSummary }) {
const AiActions = getIntegration('ai')?.AiActions;
if (!AiActions) return null;
return (
<AiActions
target="site.request"
context={() => ({ requestId: request.id })}
onApply={(values) => onSummary(values.summary)}
/>
);
}| Propriété | Requise | Rôle |
|---|---|---|
target | oui | L'écran, tel que les prompts le nomment dans leur target. |
scope | non | Un objet qui choisit le schéma de sortie, transmis à output(scope). {} par défaut. Changer le scope relit la liste des boutons. |
context | non | Un objet, ou une fonction appelée au clic, pour envoyer le contenu tel qu'il est à ce moment. Transmis à build(). |
onApply | oui | Reçoit la réponse vérifiée. Met les valeurs dans les champs de l'écran, sans enregistrer. |
disabled | non | Désactive le bouton, pendant un enregistrement par exemple. |
Le composant demande au serveur les prompts de cet écran que ce compte peut lancer. Un seul : un bouton à son libellé. Plusieurs : un menu « IA ». Aucun : rien n'est rendu, ni bouton ni message.
Tout prompt qui vise l'écran y apparaît. Un prompt du projet déclaré avec target: 'posts.post' s'ajoute au menu de l'éditeur de Posts. Sa réponse passe alors par le onApply de Posts, qui ne lit que les clés qu'il connaît : un tel prompt reprend leur forme.
Qui voit un bouton
Un prompt apparaît sur l'écran quand toutes ces conditions tiennent :
- Le module est activé, et le compte a
ai.use. - Le prompt a un
targetégal à celui de l'écran, et le projet ne l'a pas désactivé. - Le compte a la
permissionpropre au prompt, s'il en a une. - Son
outputdonne un schéma pour cescope. Une fonction qui rendnulldit que le prompt ne s'applique pas ici, une fonction qui lève retire aussi le bouton.
Le bouton n'apparaît pas tant que le fournisseur n'est pas configuré : chaque clic échouerait. La page IA nomme alors la variable en cause.
Seul un prompt à target se lance depuis le navigateur. Un prompt sans écran ne s'appelle que depuis le code du serveur : il peut lire ce qu'un compte de l'admin ne doit pas atteindre, et la route POST /ai/run le refuse comme un prompt inconnu.
Le formulaire d'un bouton
Un prompt qui déclare un input ouvre un court formulaire avant l'appel. Les champs viennent du schéma, dans son ordre :
| Schéma | Champ |
|---|---|
string avec enum | Une liste. |
string avec maxLength de 120 au plus | Une ligne. |
string plus longue, ou sans maxLength | Plusieurs lignes. |
number, integer | Un nombre, la virgule acceptée comme séparateur. |
boolean | Une case à cocher. |
Le libellé est le title du champ, sinon sa clé, et l'aide sa description. Un champ est requis sauf si son type admet null ; un champ laissé vide part alors à null. Une case à cocher n'est jamais requise. Le formulaire vérifie les champs requis, les entiers et maxLength, puis le serveur vérifie de nouveau tout le schéma : une saisie hors schéma rend invalid_input.
input: {
type: 'object',
properties: {
subject: { type: 'string', maxLength: 200, title: 'Sujet' },
details: { type: ['string', 'null'], maxLength: 2000, title: 'Détails', description: 'Facultatif.' },
},
required: ['subject', 'details'],
},Le formulaire ne sait dessiner que des champs simples, au premier niveau : textes, nombres et cases à cocher. Un input qui imbrique un objet ou une liste est refusé à la déclaration.
Deux modes
fill remplit les champs : la réponse va tout droit à onApply, et un message rappelle de relire avant d'enregistrer. C'est le mode de Posts, qui reçoit un titre, un contenu et des termes.
choose propose des choix. Chaque propriété de la réponse qui est une liste de textes devient un groupe, et la personne en prend un par groupe, ou aucun. onApply reçoit un objet aux mêmes clés, avec le texte choisi à la place de chaque liste, ou null pour un groupe laissé vide. Les autres propriétés passent telles quelles.
// Réponse du modèle
{ filenames: ['chat-roux', 'chat-sur-canape', 'chat-endormi'], altTexts: ['…', '…', '…'] }
// Ce que reçoit onApply
{ filenames: 'chat-sur-canape', altTexts: null }Le titre d'un groupe est le title de la propriété, sinon sa description, sinon sa clé. La première proposition de chaque groupe est cochée à l'ouverture. C'est le mode de Médias : trois noms de fichier et trois textes alternatifs, pour en garder un de chaque.
Les propositions gardées
Une tâche en arrière-plan n'a personne à qui montrer sa réponse. Elle la range avec saveResult() à côté du contenu qu'elle concerne, voir Tâches. L'écran de ce contenu l'affiche avec AiResults :
const AiResults = getIntegration('ai')?.AiResults;
{AiResults && (
<AiResults
target="medias.media"
id={media.id}
labels={{ altText: 'Texte alternatif' }}
onApply={(data) => setAltText(data.altText)}
/>
)}| Propriété | Requise | Rôle |
|---|---|---|
target | oui | Le type de contenu, comme dans saveResult. |
id | oui | L'identifiant du contenu. |
labels | non | Le nom affiché de chaque clé de la réponse. Une clé sans nom s'affiche telle quelle. |
onApply | non | Reçoit (data, result) au clic sur « Utiliser ». Sans lui, seul « Ignorer » est offert. |
Chaque proposition s'affiche dans une carte marquée « Proposé par l'IA », la plus récente en tête. « Utiliser » passe la réponse à l'écran, qui la met dans ses champs sans enregistrer, puis la marque comme utilisée. « Ignorer » la marque seulement. Une proposition marquée ne s'affiche plus, jusqu'à ce que la tâche en range une nouvelle pour le même contenu et le même prompt. Rien n'est rendu sans proposition, ni pour un compte sans ai.use.
Limite d'appels
Chaque appel coûte des jetons. POST /ai/run, et le bouton « Tester » de la page IA, comptent sur leur propre préréglage, ai_run : 10 tentatives par fenêtre de 10 minutes, blocage de 10 minutes. Comme tout préréglage d'une route connectée, il est compté sur le compte et triplé pour une session vérifiée, soit 30 appels par compte et par fenêtre, les deux routes ensemble. Une personne qui clique reste loin en dessous, une boucle ne fait pas grimper la facture. Voir Rate limit.
Notes
- Les composants se lisent par
getIntegration('ai')dans un fichier'use client'. Le core fusionne sous ce nom les composants publiés par@fraza/module-ai/adminet les fonctions serveur. Voir Intégrations entre modules. - La route passe le signal de la requête à l'appel : une requête interrompue arrête l'attente du fournisseur, et l'historique note
cancelled. - Un message d'erreur s'affiche dans une notification, en français, tel que le module le rend. Voir API.