Proveedores, streaming y exportación a DOCX
Complementos del lado del servidor para el AI Toolkit, todos incluidos en el paquete NuGet RichTextBox.AspNetCore (1.0.0-preview.11+). Inyección de dependencias en una línea para conectar un proveedor real, streaming SSE de extremo a extremo y un endpoint de HTML → DOCX.
Resolvers de proveedores integrados
Tres implementaciones listas para usar de IRichTextBoxAiResolver que usan HttpClientFactory, cancelación e ILogger<T>. Elige una y regístrala en Program.cs:
using RichTextBox.AiResolvers;
builder.Services.AddRichTextBox();
builder.Services.AddRichTextBoxOpenAiResolver(opts =>
{
opts.ApiKey = builder.Configuration["OpenAI:ApiKey"];
opts.Model = "gpt-6-luna";
});Cambia AddRichTextBoxOpenAiResolver por AddRichTextBoxAnthropicResolver o AddRichTextBoxAzureOpenAiResolver. Los tres comparten 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 tiene tres opciones adicionales en AzureOpenAiResolverOptions: Endpoint (https://my-resource.openai.azure.com), DeploymentName (no el modelo) y ApiVersion (opcional).
OpenAiResolver funciona con cualquier endpoint compatible con OpenAI: sobrescribe BaseUrl para Groq, Together.ai u Ollama en local.
Respuestas en streaming (Server-Sent Events)
MapRichTextBoxUploads() también asigna POST /richtextbox/ai/stream (configurable mediante RichTextBoxOptions.AiStreamEndpoint). Los resolvers de OpenAI y Anthropic implementan IStreamingRichTextBoxAiResolver; los proveedores sin streaming recurren de forma transparente a un único frame de datos.
Formato en la conexión
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\nCliente
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 función auxiliar usa fetch + ReadableStream. Los navegadores sin streaming recurren automáticamente a XHR contra el endpoint sin streaming.
Exportación a DOCX
MapRichTextBoxUploads() también asigna POST /richtextbox/export/docx (configurable mediante DocxExportEndpoint). Está construido sobre DocumentFormat.OpenXml, con licencia MIT. Sin servicios externos.
Solicitud
POST /richtextbox/export/docx
Content-Type: application/json
{
"html": "<h1>Memo</h1><p>Body.</p>",
"title": "Memo",
"fileName": "memo.docx"
}Respuesta
Flujo binario con Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document y Content-Disposition: attachment; filename=memo.docx.
HTML admitido
- Párrafos,
h1–h6, blockquote, pre, hr - Listas ordenadas y no ordenadas anidadas
- Tablas (con filas de encabezado)
- En línea:
b/strong,i/em,u,s/del,sub,sup,code,br - Hipervínculos e imágenes en línea (solo URI data; las URL externas se omiten por seguridad)
- Atributos de estilo:
font-weight,font-style,text-decoration,color,background-color,text-align
Función auxiliar del cliente
editor.aiToolkit.exportDocx({
url: "/richtextbox/export/docx",
fileName: "report.docx",
title: "Quarterly report"
});Resolvers personalizados
Para Bedrock, Gemini, tu propio modelo o cualquier otra cosa, implementa IRichTextBoxAiResolver (y, opcionalmente, IStreamingRichTextBoxAiResolver) y registra tu clase. El resto del AI Toolkit no depende del proveedor.
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>();