← 全部文档
AI / 服务器

服务商、流式输出与 DOCX 导出

AI Toolkit 的服务器端配套功能,全部包含在 RichTextBox.AspNetCore NuGet 包(1.0.0-preview.11+)中。一行依赖注入即可接入真实的服务商,提供端到端的 SSE 流式输出,以及一个 HTML → DOCX 端点。

内置的服务商解析器

三个即插即用的 IRichTextBoxAiResolver 实现,使用了 HttpClientFactory、取消机制和 ILogger<T>。选择其中一个,在 Program.cs 中注册:

using RichTextBox.AiResolvers;

builder.Services.AddRichTextBox();
builder.Services.AddRichTextBoxOpenAiResolver(opts =>
{
    opts.ApiKey = builder.Configuration["OpenAI:ApiKey"];
    opts.Model  = "gpt-6-luna";
});

可以把 AddRichTextBoxOpenAiResolver 换成 AddRichTextBoxAnthropicResolver 或 AddRichTextBoxAzureOpenAiResolver。三者共用 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 在 AzureOpenAiResolverOptions 上另有三个选项:Endpoint(https://my-resource.openai.azure.com)、DeploymentName(不是模型名)和 ApiVersion(可选)。

OpenAiResolver 适用于任何兼容 OpenAI 的端点——对于 Groq、Together.ai 或本地 Ollama,覆盖 BaseUrl 即可。

流式响应(Server-Sent Events)

MapRichTextBoxUploads() 还会映射 POST /richtextbox/ai/stream(可通过 RichTextBoxOptions.AiStreamEndpoint 配置)。OpenAI 和 Anthropic 解析器实现了 IStreamingRichTextBoxAiResolver;不支持流式输出的服务商会透明地退回为单个数据帧。

传输格式

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\n

客户端

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 text

该辅助函数使用 fetch + ReadableStream。不支持流式读取的浏览器会自动退回为通过 XHR 请求非流式端点。

DOCX 导出

MapRichTextBoxUploads() 还会映射 POST /richtextbox/export/docx(可通过 DocxExportEndpoint 配置)。基于 MIT 许可的 DocumentFormat.OpenXml 构建,不依赖外部服务。

请求

POST /richtextbox/export/docx
Content-Type: application/json

{
    "html": "<h1>Memo</h1><p>Body.</p>",
    "title": "Memo",
    "fileName": "memo.docx"
}

响应

二进制流,带有 Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document 和 Content-Disposition: attachment; filename=memo.docx。

支持的 HTML

  • 段落、h1–h6、blockquote、pre、hr
  • 有序和无序的嵌套列表
  • 表格(含表头行)
  • 内联:b/strong、i/em、u、s/del、sub、sup、code、br
  • 超链接、内联图片(仅限 data URI——出于安全考虑跳过外部 URL)
  • 样式属性:font-weight、font-style、text-decoration、color、background-color、text-align

客户端辅助函数

editor.aiToolkit.exportDocx({
    url:      "/richtextbox/export/docx",
    fileName: "report.docx",
    title:    "Quarterly report"
});

自定义解析器

对于 Bedrock、Gemini、您自己的模型或其他任何服务——实现 IRichTextBoxAiResolver(以及可选的 IStreamingRichTextBoxAiResolver)并注册您的类即可。AI Toolkit 的其余部分都与服务商无关。

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>();