Fournisseurs, streaming et export DOCX
Des compléments côté serveur à l’AI Toolkit, tous fournis dans le package NuGet RichTextBox.AspNetCore (1.0.0-preview.11+). Une ligne d’injection de dépendances pour brancher un vrai fournisseur, du streaming SSE de bout en bout, et un point de terminaison HTML → DOCX.
Résolveurs de fournisseurs intégrés
Trois implémentations prêtes à l’emploi d'IRichTextBoxAiResolver, qui utilisent HttpClientFactory, l’annulation et ILogger<T>. Choisissez-en une et enregistrez-la dans Program.cs :
using RichTextBox.AiResolvers;
builder.Services.AddRichTextBox();
builder.Services.AddRichTextBoxOpenAiResolver(opts =>
{
opts.ApiKey = builder.Configuration["OpenAI:ApiKey"];
opts.Model = "gpt-6-luna";
});Remplacez AddRichTextBoxOpenAiResolver par AddRichTextBoxAnthropicResolver ou AddRichTextBoxAzureOpenAiResolver. Les trois partagent AiResolverOptions :
public class AiResolverOptions
{
public string ApiKey { get; set; } // required
public string? Model { get; set; } // provider-specific default
public string? BaseUrl { get; set; } // for proxies / Ollama / Groq
public int MaxTokens { get; set; } = 1024;
public double Temperature { get; set; } = 0.4;
public string? SystemSuffix { get; set; } // appended to every mode's system prompt
public TimeSpan Timeout { get; set; } = 60s;
}Azure a trois options supplémentaires sur AzureOpenAiResolverOptions : Endpoint (https://my-resource.openai.azure.com), DeploymentName (et non le modèle) et ApiVersion (facultatif).
OpenAiResolver fonctionne avec n’importe quel point de terminaison compatible OpenAI — redéfinissez BaseUrl pour Groq, Together.ai ou un Ollama local.
Réponses en streaming (Server-Sent Events)
MapRichTextBoxUploads() déclare aussi POST /richtextbox/ai/stream (configurable via RichTextBoxOptions.AiStreamEndpoint). Les résolveurs OpenAI et Anthropic implémentent IStreamingRichTextBoxAiResolver ; les fournisseurs sans streaming se rabattent de façon transparente sur une seule trame de données.
Format des échanges
data: <json-encoded delta string>\n\n
data: <json-encoded delta string>\n\n
...
event: response
data: { ...RichTextBoxAiResponse payload... }\n\n
event: done
data: {}\n\nClient
var stream = editor.aiToolkit.streamRequest({
url: "/richtextbox/ai/stream",
body: { mode: "rewrite", selectionText: "..." },
onDelta: function (chunk, full) { /* render progressively */ },
onResponse: function (payload) { /* final structured result */ },
onDone: function (fullText) { },
onError: function (err) { }
});
// stream.abort() — cancel mid-response
// stream.promise — resolves with the concatenated full textLa fonction utilitaire utilise fetch + ReadableStream. Les navigateurs sans streaming se rabattent automatiquement sur une XHR vers le point de terminaison sans streaming.
Export DOCX
MapRichTextBoxUploads() déclare aussi POST /richtextbox/export/docx (configurable via DocxExportEndpoint). Repose sur DocumentFormat.OpenXml, sous licence MIT. Aucun service externe.
Requête
POST /richtextbox/export/docx
Content-Type: application/json
{
"html": "<h1>Memo</h1><p>Body.</p>",
"title": "Memo",
"fileName": "memo.docx"
}Réponse
Un flux binaire avec Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document et Content-Disposition: attachment; filename=memo.docx.
HTML pris en charge
- Paragraphes,
h1–h6, blockquote, pre, hr - Listes ordonnées et non ordonnées imbriquées
- Tableaux (avec lignes d’en-tête)
- En ligne :
b/strong,i/em,u,s/del,sub,sup,code,br - Liens hypertexte, images en ligne (URI data uniquement — les URL externes sont ignorées par sécurité)
- Attributs de style :
font-weight,font-style,text-decoration,color,background-color,text-align
Fonction utilitaire côté client
editor.aiToolkit.exportDocx({
url: "/richtextbox/export/docx",
fileName: "report.docx",
title: "Quarterly report"
});Résolveurs personnalisés
Pour Bedrock, Gemini, votre propre modèle ou n’importe quoi d’autre, implémentez IRichTextBoxAiResolver (et éventuellement IStreamingRichTextBoxAiResolver) et enregistrez votre classe. Le reste de l’AI Toolkit est indépendant du fournisseur.
public sealed class MyBedrockResolver : IRichTextBoxAiResolver
{
public async ValueTask<RichTextBoxAiResponse> ResolveAsync(
RichTextBoxAiRequest request, CancellationToken cancellationToken = default)
{
// ...call Bedrock, map response to RichTextBoxAiResponseBuilder...
}
}
builder.Services.AddRichTextBox();
builder.Services.AddSingleton<IRichTextBoxAiResolver, MyBedrockResolver>();