TXTextControl.AI.AspNetCore 0.1.0-beta.1

Prefix Reserved
This is a prerelease version of TXTextControl.AI.AspNetCore.
dotnet add package TXTextControl.AI.AspNetCore --version 0.1.0-beta.1
                    
NuGet\Install-Package TXTextControl.AI.AspNetCore -Version 0.1.0-beta.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TXTextControl.AI.AspNetCore" Version="0.1.0-beta.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TXTextControl.AI.AspNetCore" Version="0.1.0-beta.1" />
                    
Directory.Packages.props
<PackageReference Include="TXTextControl.AI.AspNetCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TXTextControl.AI.AspNetCore --version 0.1.0-beta.1
                    
#r "nuget: TXTextControl.AI.AspNetCore, 0.1.0-beta.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TXTextControl.AI.AspNetCore@0.1.0-beta.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TXTextControl.AI.AspNetCore&version=0.1.0-beta.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=TXTextControl.AI.AspNetCore&version=0.1.0-beta.1&prerelease
                    
Install as a Cake Tool

TX Text Control AI ASP.NET Core Integration

Build your own AI-enabled TX Text Control website with reusable .NET services, HTTP workflows and browser SDKs. This package supplies chat, document assistance, streaming, exports, runtime administration, optional private Knowledge and a secure server-to-server integration pattern.

Your application owns its pages, layout, sign-in experience and editor. No sample pages, CSS framework, complete admin UI or Document Editor license are included. The MCP document server remains independently deployable.

Contents

Getting started

Requires ASP.NET Core/.NET 8 and an application-owned GGUF and compatible llama-server runtime. Document processing requires a separately deployed, licensed TX Text Control MCP server. Restore the preview from your approved feed:

dotnet add package TXTextControl.AI.AspNetCore --prerelease

Minimal loopback-only development host:

using TXTextControl.AI.AspNetCore;

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseUrls("http://127.0.0.1:5001");
builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 64 * 1024 * 1024);
builder.Services.AddTextControlAI(builder.Configuration.GetSection("LocalAI"));
var app = builder.Build();
app.UseStaticFiles();
app.UseTextControlAI();
app.MapTextControlAI(enableRuntimeConfiguration: false);
app.Run();

Example configuration, assuming the runtime has already been provisioned:

{
  "LocalAI": {
    "ModelDirectory": "Models",
    "ModelPath": "chat.gguf",
    "WarmupOnStartup": false,
    "AutoInstallRuntime": false,
    "McpEndpoint": "http://127.0.0.1:5000/mcp",
    "ContextSize": 8192,
    "Generation": { "MaxOutputTokens": 1024 }
  }
}

Add your own HTML/Razor page and load the browser script described below. Neither UseTextControlAI nor MapTextControlAI supplies authentication. Before deployment, configure authentication and user/admin policies, call UseAuthentication and UseAuthorization, and protect the returned group:

// "AiUsers" and "AiAdministrators" must be registered by your host.
app.MapTextControlAI(administrationPolicy: "AiAdministrators")
   .RequireAuthorization("AiUsers");
app.MapTextControlAIRuntimeAdministration("AiAdministrators");

Use this protected mapping instead of, not in addition to, the development mapping. Do not expose unauthenticated configuration/model-loading endpoints.

Architecture and hosting choices

Component Responsibility / location
Website Pages, login, editor and optional same-origin AI proxy
Integration host Chat history, loaded model, runtime administration, artifacts and optional Knowledge
llama-server Owned local process or explicitly trusted external raw inference endpoint
MCP server TX document creation/edit/conversion/extraction; separately licensed Windows/Linux host

In local mode the integration host is the website process. In remote mode it is your AI.Service host; the website proxies requests and does not load its own model. Do not point LlamaServerEndpoint at AI.Service: it is for a raw llama-server API. macOS AI hosting does not require the native TX document engine; keep MCP on a supported host and provision a compatible Mac inference executable and SQLite.

C# registration accepts either the LocalAI configuration section, optionally with an Action<WebAiOptions> override, or an action alone. UseTextControlAI adds session middleware. Registered services include the shared LocalDocumentAiService, runtime installation worker, conversation/artifact stores, sample catalog, warmup service and chunked document analyzer.

Complete LocalAI configuration

Defaults are library defaults, not necessarily the sample's appsettings values. Configuration environment variables use double underscores, for example LocalAI__ModelDirectory and LocalAI__Generation__MaxOutputTokens.

Models and native runtime

WebAiOptions property Default Purpose
WarmupOnStartup true Attempt warmup using the configured model at host startup
AutoInstallRuntime false Explicit opt-in to acquire a missing supported runtime
RuntimeCacheDirectory null Private runtime storage; blank uses account-local application-data cache
RuntimeDownload new() Version, BaseUrl and pinned Assets; details below
RuntimeDownloadSettingsFile null Optional private JSON admin override path relative to content root; null disables persistence
LlamaServerExecutablePath null Trusted explicit native engine executable
LlamaServerEndpoint null Existing raw inference endpoint; no owned chat process
LlamaServerApiKey local Server-side raw inference credential
HardwareBackend Auto Auto, Cpu, Cuda, Vulkan
ModelDirectory Models Application-owned GGUF directory, resolved from content root
ModelPath Qwen3-8B-Q6_K.gguf Default model selection; package supplies no such file
ContextSize 65536 Allocated model context; reduce for constrained hardware
GpuLayers -1 Maximum possible offload; 0 disables layer offload
Threads null Automatic CPU thread selection
FlashAttention null Runtime-selected; explicit true/false overrides
EnableReasoning false Model/runtime reasoning preference
InferenceTimeoutSeconds 600 Inference deadline
MaxConversationTurns 6 Retained conversation-turn bound, additionally constrained by token budgets

MCP connections and export limits

Property Default Purpose
McpEndpoint http://127.0.0.1:5000/mcp Document-processing endpoint
McpEnabled true Enables document tools; local model can run without MCP
McpConnectionTimeoutSeconds 15 Connection/discovery deadline
RestrictMcpEndpoints false Restrict configurable MCP destinations to trusted endpoints; enable on remote/shared hosts
TrustedMcpEndpoints empty Explicit non-loopback HTTPS destinations approved by the host
TrustedInferenceEndpoints empty Approved remote raw inference endpoints for private Knowledge
McpCredentials empty Exact endpoint-scoped { Endpoint, Token } bearer credentials, never browser input
MaximumExportBytes 134217728 128 MiB export-transfer bound
ExportDownloadTimeoutSeconds 120 Artifact HTTP deadline including body

Loopback processing is trusted by the built-in processing-endpoint helper. Non-loopback Knowledge processing requires explicitly approved HTTPS URLs; an admin typing a URL is not sufficient approval. Redirects and TLS validation must not be bypassed. MCP credentials and AI-service credentials are independent.

Generation

LocalAI:Generation is ChatGenerationSettings:

Property Default
MaxOutputTokens 1024
Temperature 0.6
TopK 40
TopP 0.95
FrequencyPenalty null
PresencePenalty null
Seed null

Use positive output/top-k limits, temperature 0–2, top-p 0–1 and model-compatible penalties. Null leaves optional values to the runtime.

Context management

LocalAI:ContextManagement:

Property Default Purpose
Enabled true Token-aware input preparation
CharactersPerToken 2.0 Conservative estimate, not an exact tokenizer
SafetyMarginTokens 1024 Template/tokenization headroom
ToolResultReserveTokens 4096 Tool-loop reserve, adapted to available context
MaxDocumentChunkCharacters 48000 Upper bound for one analysis chunk
MaxDocumentChunks 128 Analysis-work safety bound

The integration budgets instructions, tool schemas, history, output and evidence. Long-document analysis pages through MCP text and combines bounded intermediate results. Document binaries may travel between browser, integration and MCP for loading/editing; they should not be mistaken for text sent to the LLM. Summaries can be lossy and limits can prevent complete coverage. Do not infer that a larger context guarantees full-document analysis or exhaustive retrieval.

Download source

RuntimeDownload defaults to fixed Version: "b10621", BaseUrl: "https://github.com/ggml-org/llama.cpp/releases/download" and empty Assets (the bundled manifest). It is not automatically latest. Downloads use {BaseUrl}/{Version}/{FileName} with size/SHA-256 verification. A mirror must host identical archives. Another version requires all approved asset fields: platform, backend, fileName, bytes, sha256. Platforms are win-x64/linux-x64, backends Cpu/Cuda/Vulkan, filenames .zip/.tar.gz, at most 32 assets of at most 16 GiB each. BaseUrl must be HTTPS without credentials/query/fragment. The pinned catalog has no Linux CUDA or Mac archive. Models, GPU drivers and OS prerequisites are never downloaded by this API.

HTTP API

MapTextControlAI(prefix = "/api", administrationPolicy = null, enableRuntimeConfiguration = true) returns a route group. JSON field names use camelCase. Unless the host changes serialization, enum values in raw HTTP JSON are numeric; prefer the browser SDK's declared shapes.

Method / path relative to /api Request / response
GET /runtime RuntimeStatus: state, model, runtime, hardware, gpuLayers, toolCount, error, revision, configuration
GET /models ModelChoice[]: fileName, displayName, sizeInBytes; configuration routes only
POST /runtime RuntimeConfigurationRequest; applies shared model/runtime configuration
POST /mcp/test { mcpEndpoint }; endpoint/tool-count/capability/timing result
POST /chat ChatRequest; NDJSON stream ending in complete or error
POST /chat/reset Empty object; reset chat conversation
POST /answers/export { mode: "chat" or "document", answerId, format }; exports a stored generated answer
GET /artifacts/{fileName} Owned downloadable artifact; do not expose arbitrary server paths
GET /document-samples Available host-provided samples
GET /document-samples/{fileName} DocumentSample: fileName, streamType, data
POST /document/assist DocumentAssistRequest; NDJSON assistance stream
POST /document/classify { documentBase64, sessionId }; classification and suggested actions
POST /document/reset Empty object; reset document conversation, not the editor contents

The model/configuration routes are omitted when enableRuntimeConfiguration is false. Otherwise the supplied admin policy protects them; null does not create an admin boundary.

RuntimeConfigurationRequest fields are modelFile, mcpEndpoint, enableMcp, contextSize, gpuLayers, threads, flashAttention, enableReasoning, maxOutputTokens, temperature, topK, topP, frequencyPenalty, presencePenalty, seed and hardwareBackend. Start from the current GET /runtime configuration, modify desired values and post the complete object; this is not a JSON Patch endpoint. Configuration changes affect the shared integration instance, not just one chat.

ChatRequest contains message, optional attachment: { fileName, data } (Base64) and optional knowledge. Attachment import is different from generated answer export.

DocumentAssistRequest contains action (question/summary/rewrite), documentBase64 (native TX snapshot), sessionId, question, selectionStart, selectionLength, selectedText, tone and knowledge. Rewrite uses selected text; do not treat arbitrary browser offsets as stable MCP document coordinates.

Streams use application/x-ndjson, not SSE. Each line is a JSON event with type. Handle status updates and terminal complete or error. Completion can include kind, text, html, sessionId, answerId, artifacts, citations, knowledgeMode, summary, documentBase64, streamType and selection information depending on workflow. An HTTP 200 stream can still end with an error event; a stream without completion is incomplete.

Each artifact has fileName, format, byteCount and downloadUrl. Build the download bubble from structured artifacts, not URLs invented in prose. "PDF please" exports the current document. Use a distinct answer-export button and answerId to export a chat explanation/summary.

Browser client

The package ships these static web assets (no npm install or UI framework):

<script src="/_content/TXTextControl.AI.AspNetCore/client.js"></script>
<script src="/_content/TXTextControl.AI.AspNetCore/editor.js"></script>

The second script is optional. Matching client.d.ts and editor.d.ts are included for global-namespace TypeScript integrations. Adapt asset URLs and API prefixes for your application's PathBase.

const ai = new TXTextControlAI.Client({ apiBaseUrl: "/api" });
const controller = new AbortController();
const answer = await ai.chat("Explain this workflow.", event => {
    if (event.type === "status") console.log(event.message);
}, { signal: controller.signal });
console.log(answer.text);
for (const artifact of answer.artifacts || []) {
    console.log(artifact.fileName, ai.url(artifact.downloadUrl));
}
if (answer.answerId) {
    // Invoke only when the user clicks your "Export answer" control.
    // await ai.exportAnswer({ mode: "chat", answerId: answer.answerId, format: "pdf" });
}

Constructor options: apiBaseUrl (default /api), credentials (same-origin), headers (HeadersInit or sync/async function). Never embed service API keys in those browser headers. Per-call options use Fetch RequestInit including signal. Generic url rejects resources outside the configured API; fetch returns a Response, request parses JSON, post posts JSON, and stream consumes NDJSON. The SDK rejects unsuccessful HTTP responses, error events and incomplete streams.

Client method family Calls
Conversation chat(message, onEvent, options), resetChat(options)
Document assistDocument(request, onEvent, options), classifyDocument(request, options), resetDocument(options)
Answer export exportAnswer({ mode, answerId, format }, options)
Runtime runtimeInstallation, installRuntime, configureRuntimeDownload, cancelRuntimeInstallation, removeRuntime, unloadModel
Knowledge collections knowledgeCollections, knowledgeStatus, createKnowledgeCollection, renameKnowledgeCollection, deleteKnowledgeCollection
Knowledge sources/jobs uploadKnowledge, knowledgeJobs, knowledgeSources, changeKnowledgeJob, reindexKnowledgeSource, removeKnowledgeSource
Retrieval searchKnowledge, resolveKnowledgePassage, knowledgeSourceUrl
Embedding admin knowledgeEmbedding, configureKnowledgeEmbedding
Generic helpers url, fetch, request, post, stream, runtimeAdmin, knowledgePost

The complete argument/return declarations are provided in the browser reference appendix. Render text/Markdown safely; the SDK does not insert HTML or supply a sanitizing UI. Protect cookie authentication with host anti-CSRF/origin policies.

Document Editor binding

The application provides its licensed editor instance. Bind once the MVC bootstrap object exists, before textControlLoaded; the adapter waits for the full API:

const assistant = new TXTextControlAI.DocumentAssistant({
    client: ai,
    editor: TXTextControl
});
assistant.on("progress", event => console.log(event));
await assistant.ready();
const result = await assistant.execute('Change the style "Heading 1" to red.');
console.log(result.text, result.applied);

If the host already observed textControlLoaded, pass editorOptions: { alreadyReady: true }. Do not guess readiness. The other option, timeoutMilliseconds, defaults to 30000 for native callbacks/readiness, not inference.

Object Methods
EditorAdapter ready, on, getSelection, selectRange, replaceSelection, saveDocument, loadDocument, loadFile, dispose
EditorAdapter static streamTypeForFile(name)
DocumentAssistant ready, on, getSelection, selectRange, saveDocument, loadDocument, loadFile, classify, execute, summarize, rephrase, reset, cancel, dispose

getSelection returns { start, length, text }. Default saves use a native TX snapshot. loadDocument(streamType, base64) accepts the editor stream-type name; loadFile maps supported TX/RTF/DOCX/DOC/HTML/PDF extensions. execute(question, options), summarize(options) and rephrase(tone, options) accept cancellation and progress options. reset does not clear the editor. An externally supplied EditorAdapter remains caller-owned.

on returns an unsubscribe function. Adapter events: ready, selectionChanged, documentChanged, documentLoaded, error, disposed. Assistant events: selectionChanged, documentChanged, classified, progress, completed, conflict, error.

Operations serialize and validate document/selection snapshots before applying AI edits. EditorConflictError rejects stale edits. Commits briefly use read-only mode and restore it; protected documents are not bypassed. Cancellation before commit prevents dispatch but cannot undo a native mutation already sent. Timeout/disconnection fails closed; reinitialize an uncertain editor binding. Use one active assistant per browser/API session, including across tabs. New Client objects do not create isolated server conversations.

Runtime administration

Opt in with MapTextControlAIRuntimeAdministration("AiAdministrators"). It supplies endpoints/services, not an admin page. All mutations require X-TextControl-Runtime-Admin: 1 in addition to authentication/authorization. The browser SDK adds it automatically.

Relative to /api/runtime-installation Body / result
GET / Inventory, compatibility limitations and current installation job
POST /source RuntimeDownloadOptions; set validated source/manifest, persist only if configured
POST /install { backend: "Auto", "Cpu", "Cuda", or "Vulkan" }; start background install
POST /cancel {}; cancel current installation
POST /unload {}; unload shared chat model
POST /remove { installationId }; remove managed runtime after checking active use

Closing the page does not cancel an installation job. Poll runtimeInstallation() for state/progress/stage/error and inventory. Other hosts sharing the cache must be stopped before removing their engine. Runtime administration affects the integration host, not the MCP host or browser.

Private Knowledge

Register on the integration host before building the app:

using TXTextControl.AI.Knowledge;

var privateDirectory = Path.Combine(
    builder.Environment.ContentRootPath, "App_Data", "Knowledge");
builder.Services.AddTextControlAIKnowledge(new KnowledgeWebOptions(
    new KnowledgeOptions
    {
        DatabasePath = Path.Combine(privateDirectory, "knowledge.db")
    }, "AiUsers")
{
    AdministrationPolicy = "AiAdministrators",
    EvidenceTokenBudget = 2400
});
builder.Services.AddTextControlAIKnowledgeEmbeddings(
    Path.GetFullPath("Models", builder.Environment.ContentRootPath),
    Path.Combine(privateDirectory, "embedding.json"));
// After authentication/authorization middleware:
app.MapTextControlAIKnowledge();

Treat the before/after-build fragments as additions to your host, not a second Program.cs. Embedding registration is optional; keyword retrieval works without a model. Storage options are DatabasePath, MaxFileBytes (32 MiB), MaxExtractedCharacters (2000000), ChunkCharacters (1800), MaxPendingJobs (32), MaxChunks (10000) and ApprovedImportDirectories (empty). KnowledgeWebOptions requires Storage and AccessPolicy; EvidenceTokenBudget defaults to 2400 (128–16000), AdministrationPolicy defaults to null. Set an admin policy explicitly: otherwise embedding administration only inherits the Knowledge access policy.

Every non-GET Knowledge route requires X-TextControl-Knowledge: 1. Default prefix is /api/knowledge:

Method / suffix Body / result
GET / Authorized collections
GET /status enabled, embeddingConfigured
POST /collections { name }; collection
POST /{collectionId}/rename { name }; 204
POST /{collectionId}/remove {}; delete collection, stored sources and jobs
POST /search { collectionId, query, mode, limit, evidenceTokenBudget, versionId }; evidence
GET /{collectionId}/jobs Job list
GET /{collectionId}/sources Active source versions
POST /{collectionId}/upload Binary body, Content-Type application/octet-stream, URL-encoded X-File-Name; jobId
POST /{collectionId}/jobs/{jobId} { retry: true/false }; retry or discard
POST /{collectionId}/versions/{versionId}/reindex {}; jobId
POST /{collectionId}/sources/{documentId}/remove {}; delete source
GET /{collectionId}/passages/{chunkId} Authorized original passage
GET /{collectionId}/versions/{versionId}/download Stored source bytes
GET /embedding Global configuration and available GGUFs, when registered
POST /embedding KnowledgeEmbeddingSettings; restartRequired

Search modes in raw default JSON are 0 Auto, 1 Keyword, 2 Semantic, 3 Hybrid. Default limit is 6 (1–50); query length at most 4000 characters. An optional source version restricts retrieval; results are not exhaustive.

KnowledgeEmbeddingSettings: modelFileName (null = keyword only), dimensions (384 default, 1–65536), pooling (mean default, cls or last), queryPrefix and documentPrefix (empty, at most 1000 characters), sha256 (computed by the host on save). Files must be within the approved GGUF directory. Changing these settings requires restarting the integration host and reindexing. In remote mode that means AI.Service, not the website. Embeddings use a separate lazy CPU process by default and do not replace the chat model.

Chat/document requests may supply knowledge: { collectionId, allowDocumentChanges: false, sourceVersionId: null }. Grounding uses that authorized scope, treats sources as untrusted data and returns citations. AllowDocumentChanges permits only explicitly requested mutations; it is not blanket authorization. Reference passages use source download/resolve routes, not editor loading. Collection/source deletion is not secure erasure of SQLite free pages, WAL or backups.

Remote AI service

The same package supports a separate AI host and a same-origin website proxy. The website owns user login. The trusted service credential allows it to assert a delegated user and administrator role; protect it like a privileged secret.

AI host

Provision ServiceAuthentication:ApiKey through a secret store/environment, with at least 32 random characters. Never commit it or send it to the browser.

using TXTextControl.AI.AspNetCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTextControlAIServiceAuthentication(
    builder.Configuration.GetSection("ServiceAuthentication"));
builder.Services.AddTextControlAI(
    builder.Configuration.GetSection("LocalAI"),
    options => options.RestrictMcpEndpoints = true);
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.UseTextControlAI();
app.MapTextControlAI(administrationPolicy: RemoteServiceAuthenticationHandler.AdminPolicy)
   .RequireAuthorization(RemoteServiceAuthenticationHandler.UserPolicy);
app.MapTextControlAIRuntimeAdministration(RemoteServiceAuthenticationHandler.AdminPolicy);
app.Run();

Configure HTTPS and ingress limits on the host. Register/map Knowledge there if required. RemoteServiceAuthenticationOptions has ApiKey (empty default) and AllowLoopbackHttp (false). The HTTP exception is only for explicitly enabled loopback development, not Docker-to-host production networking.

Service requests require Authorization: Bearer ... and X-TX-AI-Subject. Administrator requests also require X-TX-AI-Administrator: true, asserted only by an authenticated trusted website. The package registers policies TextControlAIUser and TextControlAIAdmin.

Website proxy

RemoteIntegrationOptions:

Property Default Purpose
ServiceUrl null Approved AI-service API root, including /api/; blank selects local mode in host routing
ApiKey empty Same server-held service key, at least 32 characters
AllowedServiceUrls empty Exact approved API-root allowlist
AllowLoopbackHttp false Opt-in local development only

Create RemoteIntegrationConnection(options, absolutePrivateSettingsPath) and register it with AddTextControlAIRemoteConnection(connection). A saved URL overrides the configured selection. Choose local registration/mapping or MapTextControlAIRemoteProxy("AiUsers", "AiAdministrators") at website startup; do not map both over the same paths.

MapTextControlAIConnectionAdministration("AiAdministrators") maps /integration-connection GET/POST and POST /test. POST uses { serviceUrl }, requires X-TextControl-Connection: 1 and same-origin checks. It saves the URL, not credentials. A changed URL requires a website restart; it does not restart the AI host or move models. Test validates API reachability/credentials, not successful model loading.

The proxy forwards GET/POST under its API prefix, streams without response buffering, rejects redirects, validates destinations and supplies delegated identity from the authenticated user. It does not forward browser-provided service authorization or arbitrary identity. Configure trusted reverse-proxy headers so HTTPS/origin checks reflect the real public request.

Models, engine cache, generated files and Knowledge live on the AI-service machine in remote mode. MCP owns document-engine sessions/files independently.

Storage, security and operations

  • Session cookie: .TXTextControl.LocalAI.Session, HttpOnly, SameSite Lax, two-hour idle timeout. Conversation/model state is process-local; a distributed cache alone does not make model/session/artifact ownership horizontally scalable.
  • Keep private artifacts, model files, runtime settings, SQLite files and secrets out of static folders. Apply quotas, retention, backups and filesystem permissions.
  • Protect all APIs with host authentication and separate admin authorization. Shared model/runtime configuration affects all users of the instance.
  • Configure body limits at every hop. Base64 adds roughly one-third transfer size. Knowledge defaults to 32 MiB binary uploads; MCP/proxies may impose lower limits.
  • Runtime/model install and embedding changes run on the integration host, not wherever the browser or MCP happens to be.
  • A Knowledge database warning may indicate filesystem permissions, native SQLite loading/trust or database initialization; do not delete the database to diagnose it.
  • TLS UntrustedRoot requires correcting certificate trust, not disabling validation.
  • Context errors require budgeting schemas/history/evidence as well as document text. A model's context allocation and available RAM remain hard constraints.
  • No built-in sample admin login is provided by this package. Authentication and page rendering are application responsibilities.

License and support

Preview APIs may change before a stable release. Distributed by Text Control GmbH under the Text Control AI Source Available License 1.0, included as LICENSE.txt. Solutions must retain properly licensed TX Text Control products as a core component. This is not an unrestricted open-source license. Commercial SDKs, model weights, inference engines and other dependencies retain their own licenses. Strong-name signing is assembly identity, not Apple notarization or a NuGet package-signature guarantee.

Visit Text Control for product, licensing and support.

Public C# reference

Source-derived public types, signatures, parameter defaults and DTO fields for this version. Bodies are omitted: these signatures are not a standalone compilable program. Concise initializers and JSON-name/required attributes are preserved. Workflow validation may require more than C# nullability alone. External types retain their package contracts. Public engine/service implementation types are advanced integration surfaces; prefer the documented registration and facade APIs for ordinary applications.

TXTextControl.AI.AspNetCore.AnswerArtifactRequest

public sealed record AnswerArtifactRequest(string Mode, string AnswerId, string Format);

TXTextControl.AI.AspNetCore.ApiEndpoints

public static class ApiEndpoints
{
    public static RouteGroupBuilder MapTextControlAI(this IEndpointRouteBuilder endpoints, string prefix = "/api", string? administrationPolicy = null, bool enableRuntimeConfiguration = true);
}

TXTextControl.AI.AspNetCore.ArtifactOwnership

/// <summary>Session-bound download grants. Restarting the host revokes these in-memory grants.</summary>
public sealed class ArtifactOwnership
{
    public void Register(string fileName, string owner);
    public bool CanDownload(string fileName, string owner);
}

TXTextControl.AI.AspNetCore.ChatArtifact

public sealed record ChatArtifact(string FileName, string Format, long ByteCount, string DownloadUrl);

TXTextControl.AI.AspNetCore.ChatAttachmentRequest

public sealed record ChatAttachmentRequest(string FileName, string Data);

TXTextControl.AI.AspNetCore.ChatRequest

public sealed record ChatRequest(string? Message, ChatAttachmentRequest? Attachment = null, KnowledgeScope? Knowledge = null);

TXTextControl.AI.AspNetCore.ChunkedDocumentAnalysisResult

public sealed record ChunkedDocumentAnalysisResult(string Text, int ChunksAnalyzed, int TotalParagraphs);

TXTextControl.AI.AspNetCore.ChunkedDocumentAnalysisService

public sealed class ChunkedDocumentAnalysisService
{
    public ChunkedDocumentAnalysisService(LocalDocumentAiService ai, IOptions<WebAiOptions> configuredOptions);
    public bool Enabled { get; }

    public async Task<ChunkedDocumentAnalysisResult> AnalyzeAsync(string sessionId, string request, bool structuredSummary, Func<DocumentAnalysisProgress, ValueTask>? progress = null, CancellationToken cancellationToken = default);
}

TXTextControl.AI.AspNetCore.ContextManagementOptions

public sealed class ContextManagementOptions
{
    public bool Enabled { get; set; } = true;
    public double CharactersPerToken { get; set; } = 2.0;
    public int SafetyMarginTokens { get; set; } = 1_024;
    public int ToolResultReserveTokens { get; set; } = 4_096;
    public int MaxDocumentChunkCharacters { get; set; } = 48_000;
    public int MaxDocumentChunks { get; set; } = 128;
}

TXTextControl.AI.AspNetCore.ConversationStore

public sealed class ConversationStore(IOptions<WebAiOptions> options)
{
    public ConversationState Get(string browserSessionId, string mode);
    public sealed class ConversationState(int maxHistoryMessages)
    {
        public SemaphoreSlim Gate { get; } = new(1, 1);
        public List<ChatMessage> Messages { get; } = [];
        public long ModelRevision { get; set; } = -1;
        public string? DocumentSessionId { get; set; }
        public string? LastExportFormat { get; set; }

        public ChatMessage[] CreateModelMessages();
        public void Compact();
        public string StoreGeneratedAnswer(string markdown, string request, KnowledgeScope? knowledge = null);
        public bool TryGetGeneratedAnswer(string id, out GeneratedAnswer answer);
        public void Reset(long revision, string systemPrompt);
    }

    public sealed record GeneratedAnswer(string Markdown, string Request, KnowledgeScope? Knowledge = null);
}

TXTextControl.AI.AspNetCore.DocumentAnalysisProgress

public sealed record DocumentAnalysisProgress(string Message, int Chunk, int? TotalChunks);

TXTextControl.AI.AspNetCore.DocumentAssistRequest

public sealed record DocumentAssistRequest(string Action, string DocumentBase64, string? SessionId, string? Question, int? SelectionStart, int? SelectionLength, string? SelectedText, string? Tone, KnowledgeScope? Knowledge = null);

TXTextControl.AI.AspNetCore.DocumentClassification

public sealed record DocumentClassification(string SessionId, string Category, double Confidence, IReadOnlyList<string> Signals, IReadOnlyList<DocumentQuickAction> SuggestedActions);

TXTextControl.AI.AspNetCore.DocumentClassificationRequest

public sealed record DocumentClassificationRequest(string DocumentBase64, string? SessionId);

TXTextControl.AI.AspNetCore.DocumentInspectionPage

public sealed record DocumentInspectionPage(string SessionId, int TotalParagraphs, IReadOnlyList<DocumentInspectionParagraph> Paragraphs, bool Truncated, int? NextParagraphIndex);

TXTextControl.AI.AspNetCore.DocumentInspectionParagraph

public sealed record DocumentInspectionParagraph(int Index, string Text, string? StyleName);

TXTextControl.AI.AspNetCore.DocumentQuickAction

public sealed record DocumentQuickAction(string Id, string Title, string Description, string Prompt);

TXTextControl.AI.AspNetCore.DocumentSample

public sealed record DocumentSample(string FileName, string StreamType, string Data);

TXTextControl.AI.AspNetCore.DocumentSampleCatalog

public sealed class DocumentSampleCatalog(IWebHostEnvironment environment)
{
    public IReadOnlyList<string> List();
    public async Task<DocumentSample> ReadAsync(string fileName, CancellationToken cancellationToken);
}

TXTextControl.AI.AspNetCore.DocumentSummary

public sealed record DocumentSummary(string Overview, IReadOnlyList<string> KeyPoints);

TXTextControl.AI.AspNetCore.InstallRuntimeRequest

public sealed record InstallRuntimeRequest(string Backend);

TXTextControl.AI.AspNetCore.KnowledgeCitation

public sealed record KnowledgeCitation(string CitationId, KnowledgePassage Source);

TXTextControl.AI.AspNetCore.KnowledgeEmbeddingAdministration

/// <summary>Host-approved model directory and private settings. Changes take effect on the next host restart.</summary>
public sealed class KnowledgeEmbeddingAdministration : IDisposable
{
    public KnowledgeEmbeddingSettings Current { get; }

    public void Dispose();
    public KnowledgeEmbeddingAdministration(string modelDirectory, string settingsPath);
    public object Status();
    public async Task SaveAsync(KnowledgeEmbeddingSettings settings, CancellationToken ct);
}

TXTextControl.AI.AspNetCore.KnowledgeEmbeddingServiceCollectionExtensions

public static class KnowledgeEmbeddingServiceCollectionExtensions
{
    public static IServiceCollection AddTextControlAIKnowledgeEmbeddings(this IServiceCollection services, string modelDirectory, string settingsPath, LlamaServerModelOptions? runtime = null);
}

TXTextControl.AI.AspNetCore.KnowledgeEmbeddingSettings

public sealed record KnowledgeEmbeddingSettings(string? ModelFileName = null, int Dimensions = 384, string Pooling = "mean", string QueryPrefix = "", string DocumentPrefix = "", string? Sha256 = null);

TXTextControl.AI.AspNetCore.KnowledgeEvidence

public sealed record KnowledgeEvidence(KnowledgeSearchResult Search, bool AllowDocumentChanges)
{
    public ChatMessage[] AddTo(ChatMessage[] messages);
    public KnowledgeCitation[] Citations(string response);
    public string SanitizeCitationIds(string response);
}

TXTextControl.AI.AspNetCore.KnowledgeGrounding

public sealed class KnowledgeGrounding(KnowledgeStore store, KnowledgeWebOptions options, IAuthorizationService authorization)
{
    public async Task CheckAccessAsync(KnowledgeScope scope, HttpContext context, CancellationToken ct);
    public async Task<KnowledgeEvidence> RetrieveAsync(KnowledgeScope scope, string question, HttpContext context, CancellationToken ct);
}

TXTextControl.AI.AspNetCore.KnowledgeIntegration

public static class KnowledgeIntegration
{
    public static IServiceCollection AddTextControlAIKnowledge(this IServiceCollection services, KnowledgeWebOptions options);
    public static IEndpointRouteBuilder MapTextControlAIKnowledge(this IEndpointRouteBuilder endpoints, string prefix = "/api/knowledge");
    public sealed record CreateKnowledgeCollectionRequest(string Name);
    public sealed record ChangeKnowledgeJob(bool Retry);
}

TXTextControl.AI.AspNetCore.KnowledgeScope

public sealed record KnowledgeScope(string CollectionId, bool AllowDocumentChanges = false, string? SourceVersionId = null);

TXTextControl.AI.AspNetCore.KnowledgeWebOptions

public sealed record KnowledgeWebOptions(KnowledgeOptions Storage, string AccessPolicy)
{
    public int EvidenceTokenBudget { get; init; } = 2400;
    public string? AdministrationPolicy { get; init; }
}

TXTextControl.AI.AspNetCore.LocalAiWarmupService

public sealed partial class LocalAiWarmupService(LocalDocumentAiService ai, IOptions<WebAiOptions> options, ILogger<LocalAiWarmupService> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken);
}

TXTextControl.AI.AspNetCore.LocalDocumentAiService

public sealed partial class LocalDocumentAiService : IAsyncDisposable
{
    public LocalDocumentAiService(IWebHostEnvironment environment, IOptions<WebAiOptions> configuredOptions, ILogger<LocalDocumentAiService> logger);
    public RuntimeStatus Status { get; }

    public IReadOnlyList<ModelChoice> GetModels();
    public async Task InitializeAsync(CancellationToken cancellationToken = default);
    public async Task<RuntimeStatus> ConfigureAsync(RuntimeConfigurationRequest requested, CancellationToken cancellationToken = default);
    public async Task<McpConnectionTestResult> TestMcpConnectionAsync(string endpoint, CancellationToken cancellationToken = default);
    public async Task<ChatResponse> GetResponseAsync(IReadOnlyList<ChatMessage> messages, bool useDocumentTools, IReadOnlySet<string>? allowedToolNames = null, CancellationToken cancellationToken = default);
    public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(IReadOnlyList<ChatMessage> messages, bool useDocumentTools, IReadOnlySet<string>? allowedToolNames = null, CancellationToken cancellationToken = default);
    public async Task<string> LoadDocumentIntoMcpAsync(string base64Document, string? sessionId, string? sourceFormat = null, CancellationToken cancellationToken = default);
    public async Task<string> GetDocumentFromMcpAsync(string sessionId, CancellationToken cancellationToken = default);
    public async Task<DocumentClassification> ClassifyDocumentAsync(string sessionId, CancellationToken cancellationToken = default);
    public async Task<DocumentInspectionPage> InspectDocumentAsync(string sessionId, int startParagraphIndex, int maxCharacters, string? query = null, int contextParagraphs = 0, CancellationToken cancellationToken = default);
    public async Task<DocumentExportResult> SaveDocumentAsync(string sessionId, string format, string? fileName = null, CancellationToken cancellationToken = default);
    public async Task<DocumentExportResult> CreateAnswerDocumentAsync(string markdown, string format, string? fileName = null, CancellationToken cancellationToken = default);
    public async Task<DocumentExportResult> DownloadMcpExportAsync(string sessionId, string format, string downloadUri, string? fileName = null, CancellationToken cancellationToken = default);
    public async ValueTask DisposeAsync();
    public async Task UnloadModelAsync(CancellationToken token = default);
}

TXTextControl.AI.AspNetCore.McpConnectionTestRequest

public sealed record McpConnectionTestRequest(string McpEndpoint);

TXTextControl.AI.AspNetCore.McpConnectionTestResult

public sealed record McpConnectionTestResult(string Endpoint, int ToolCount, bool HasCreateDocument, bool HasDocumentExport, long ElapsedMilliseconds);

TXTextControl.AI.AspNetCore.McpEndpointCredential

public sealed class McpEndpointCredential
{
    public string Endpoint { get; set; } = "";
    public string Token { get; set; } = "";
}

TXTextControl.AI.AspNetCore.McpKnowledgeDocumentExtractor

/// <summary>Reads text locally and delegates rich-document extraction to a trusted MCP host, never to the LLM.</summary>
public sealed class McpKnowledgeDocumentExtractor(HttpClient http, Func<Uri> endpoint, Func<Uri, bool> isTrusted, Func<Uri, string?> credential, int maximumCharacters = 2_000_000, int maximumFileBytes = 32 * 1024 * 1024) : IKnowledgeDocumentExtractor, IDisposable
{
    public string Version { get; }

    public void Dispose();
    public async Task<IReadOnlyList<KnowledgeBlock>> ExtractAsync(string fileName, ReadOnlyMemory<byte> content, CancellationToken cancellationToken);
}

TXTextControl.AI.AspNetCore.ModelChoice

public sealed record ModelChoice(string FileName, string DisplayName, long SizeInBytes);

TXTextControl.AI.AspNetCore.RemoteIntegrationConnection

/// <summary>Host-owned connection configuration. Changes take effect after restarting the website.</summary>
public sealed class RemoteIntegrationConnection : IDisposable
{
    public Uri? ServiceUri { get; }
    public string ApiKey { get; }

    public void Dispose();
    public RemoteIntegrationConnection(RemoteIntegrationOptions options, string settingsPath);
    public Uri? Validate(string? value);
    public async Task SaveAsync(string? value, CancellationToken ct);
    public sealed record ConnectionRequest(string? ServiceUrl);
}

TXTextControl.AI.AspNetCore.RemoteIntegrationExtensions

public static class RemoteIntegrationExtensions
{
    public static IServiceCollection AddTextControlAIRemoteConnection(this IServiceCollection services, RemoteIntegrationConnection connection);
    public static void MapTextControlAIConnectionAdministration(this IEndpointRouteBuilder endpoints, string administrationPolicy, string prefix = "/integration-connection");
    public static RouteGroupBuilder MapTextControlAIRemoteProxy(this IEndpointRouteBuilder endpoints, string accessPolicy, string administrationPolicy, string prefix = "/api");
}

TXTextControl.AI.AspNetCore.RemoteIntegrationOptions

public sealed class RemoteIntegrationOptions
{
    public string? ServiceUrl { get; set; }
    public string ApiKey { get; set; } = "";
    public string[] AllowedServiceUrls { get; set; } = [];
    public bool AllowLoopbackHttp { get; set; }
}

TXTextControl.AI.AspNetCore.RemoteServiceAuthenticationExtensions

public static class RemoteServiceAuthenticationExtensions
{
    public static IServiceCollection AddTextControlAIServiceAuthentication(this IServiceCollection services, IConfiguration configuration);
}

TXTextControl.AI.AspNetCore.RemoteServiceAuthenticationHandler

public sealed class RemoteServiceAuthenticationHandler(IOptionsMonitor<RemoteServiceAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder) : AuthenticationHandler<RemoteServiceAuthenticationOptions>(options, logger, encoder)
{
    public const string SchemeName = "TextControlAIService";
    public const string UserPolicy = "TextControlAIUser";
    public const string AdminPolicy = "TextControlAIAdmin";
    protected override Task<AuthenticateResult> HandleAuthenticateAsync();
}

TXTextControl.AI.AspNetCore.RemoteServiceAuthenticationOptions

/// <summary>Server-to-server credential. Provision through a secret store, never browser configuration.</summary>
public sealed class RemoteServiceAuthenticationOptions : AuthenticationSchemeOptions
{
    public string ApiKey { get; set; } = "";
    public bool AllowLoopbackHttp { get; set; }
}

TXTextControl.AI.AspNetCore.RemoveRuntimeRequest

public sealed record RemoveRuntimeRequest(string InstallationId);

TXTextControl.AI.AspNetCore.RuntimeAdministrationEndpoints

/// <summary>Maps opt-in runtime administration, separately from ordinary chat endpoints.</summary>
public static class RuntimeAdministrationEndpoints
{
    public static RouteGroupBuilder MapTextControlAIRuntimeAdministration(this IEndpointRouteBuilder endpoints, string administrationPolicy, string prefix = "/api/runtime-installation");
}

TXTextControl.AI.AspNetCore.RuntimeConfigurationRequest

public sealed record RuntimeConfigurationRequest(string ModelFile, string McpEndpoint, bool EnableMcp, int ContextSize, int GpuLayers, int? Threads, bool? FlashAttention, bool EnableReasoning, int MaxOutputTokens, float Temperature, int TopK, float TopP, float? FrequencyPenalty, float? PresencePenalty, int? Seed, HardwareBackend HardwareBackend = HardwareBackend.Auto);

TXTextControl.AI.AspNetCore.RuntimeInstallationService

/// <summary>Owns a cancellable installation job independent of HTTP request lifetimes.</summary>
public sealed class RuntimeInstallationService(IOptions<WebAiOptions> options, LocalDocumentAiService ai) : IHostedService, IDisposable
{
    public RuntimeInstallationState Status { get; }

    public async Task<RuntimeInstallationState> GetStatusAsync(CancellationToken token);
    public async Task ConfigureDownloadAsync(RuntimeDownloadOptions download, CancellationToken token);
    public void Install(string requestedBackend);
    public void Cancel();
    public async Task RemoveAsync(string installationId, CancellationToken token);
    public Task StartAsync(CancellationToken cancellationToken);
    public async Task StopAsync(CancellationToken cancellationToken);
    public void Dispose();
}

TXTextControl.AI.AspNetCore.RuntimeInstallationState

public sealed record RuntimeInstallationState(string State, float Progress, string Stage, string? Error, RuntimeInstallationInfo Runtime)
{
    public bool DownloadSettingsPersistenceEnabled { get; init; }
}

TXTextControl.AI.AspNetCore.RuntimeStatus

public sealed record RuntimeStatus(string State, string? Model, string? Runtime, string? Hardware, string? GpuLayers, int ToolCount, string? Error, long Revision, RuntimeConfigurationRequest Configuration);

TXTextControl.AI.AspNetCore.ServiceCollectionExtensions

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddTextControlAI(this IServiceCollection services, IConfiguration configuration, Action<WebAiOptions>? configure = null);
    public static IServiceCollection AddTextControlAI(this IServiceCollection services, Action<WebAiOptions>? configure = null);
    public static IApplicationBuilder UseTextControlAI(this IApplicationBuilder app);
}

TXTextControl.AI.AspNetCore.TrustedProcessingEndpoints

/// <summary>Explicit host-owned trust policy; remote private-document processing is never enabled by a browser URL alone.</summary>
public static class TrustedProcessingEndpoints
{
    public static bool IsTrusted(Uri endpoint, IEnumerable<string> allowed);
    public static void ValidateKnowledge(WebAiOptions settings, bool enableMcp, Uri mcpEndpoint);
}

TXTextControl.AI.AspNetCore.WebAiOptions

public sealed class WebAiOptions
{
    public const string SectionName = "LocalAI";
    public bool WarmupOnStartup { get; set; } = true;
    public bool AutoInstallRuntime { get; set; }
    public long MaximumExportBytes { get; set; } = 128L * 1024 * 1024;
    public int ExportDownloadTimeoutSeconds { get; set; } = 120;
    /// <summary>Runtime storage directory. Omitted, null or blank values use the standard application-data directory.</summary>
    public string? RuntimeCacheDirectory { get; set; }
    /// <summary>Pinned runtime release, HTTPS mirror root and optional custom asset manifest.</summary>
    public LlamaServer.RuntimeDownloadOptions RuntimeDownload { get; set; } = new();
    /// <summary>Optional host-owned JSON file for admin overrides, relative to content root. Null disables persistence.</summary>
    public string? RuntimeDownloadSettingsFile { get; set; }
    public string? LlamaServerExecutablePath { get; set; }
    public Uri? LlamaServerEndpoint { get; set; }
    public string LlamaServerApiKey { get; set; } = "local";
    public HardwareBackend HardwareBackend { get; set; } = HardwareBackend.Auto;
    public string ModelDirectory { get; set; } = "Models";
    public string ModelPath { get; set; } = "Qwen3-8B-Q6_K.gguf";
    public string McpEndpoint { get; set; } = "http://127.0.0.1:5000/mcp";
    public bool McpEnabled { get; set; } = true;
    public string[] TrustedMcpEndpoints { get; set; } = [];
    public string[] TrustedInferenceEndpoints { get; set; } = [];
    public bool RestrictMcpEndpoints { get; set; }
    /// <summary>Optional server-side bearer credentials scoped to exact MCP endpoints. Never supplied by the browser.</summary>
    public McpEndpointCredential[] McpCredentials { get; set; } = [];
    public int McpConnectionTimeoutSeconds { get; set; } = 15;
    public int ContextSize { get; set; } = 65_536;
    public int GpuLayers { get; set; } = -1;
    public int? Threads { get; set; }
    public bool? FlashAttention { get; set; }
    public bool EnableReasoning { get; set; }
    public int InferenceTimeoutSeconds { get; set; } = 600;
    public int MaxConversationTurns { get; set; } = 6;
    public ContextManagementOptions ContextManagement { get; set; } = new();
    public ChatGenerationSettings Generation { get; set; } = new();
}

Browser reference

Global declarations shipped beside the scripts: all methods, options, events and result fields. No npm/ES-module package is implied.

client.d.ts

/** Browser globals provided by client.js. No dependency on the Document Editor. */
declare namespace TXTextControlAI {
    interface ClientOptions {
        apiBaseUrl?: string;
        credentials?: RequestCredentials;
        headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
    }
    interface RequestOptions extends RequestInit { }
    interface ChatAttachment { fileName: string; data: string; }
    interface ChatOptions extends RequestOptions { attachment?: ChatAttachment; knowledge?: KnowledgeScope | null; }
    interface KnowledgeScope { collectionId: string; allowDocumentChanges?: boolean; sourceVersionId?: string | null; }
    interface KnowledgeCollection { id: string; name: string; owner: string; }
    interface KnowledgePassage { id: string; collectionId: string; documentId: string; versionId: string; fileName: string; locator: string; text: string; score: number; }
    interface KnowledgeSearchRequest { collectionId: string; query: string; mode?: 0 | 1 | 2 | 3; limit?: number; evidenceTokenBudget?: number; versionId?: string | null; }
    interface KnowledgeSearchResult { mode: 0 | 1 | 2 | 3; passages: KnowledgePassage[]; estimatedTokens: number; notice: string; }
    interface KnowledgeJob { id: string; collectionId: string; documentId: string; fileName: string; state: string; progress: number; error?: string; versionId?: string; }
    interface KnowledgeSource { documentId: string; versionId: string; fileName: string; hash: string; chunkCount: number; }
    interface KnowledgeEmbeddingSettings { modelFileName?: string | null; dimensions: number; pooling: "mean" | "cls" | "last"; queryPrefix?: string; documentPrefix?: string; sha256?: string; }
    interface Artifact { fileName: string; format: string; byteCount: number; downloadUrl: string; }
    interface StreamEvent {
        type: string;
        message?: string;
        tool?: string;
        elapsedMilliseconds?: number;
        [key: string]: unknown;
    }
    interface CompleteEvent extends StreamEvent {
        type: "complete";
        kind?: string;
        text?: string;
        html?: string | null;
        sessionId?: string | null;
        answerId?: string;
        artifacts?: Artifact[];
        citations?: { citationId: string; source: KnowledgePassage }[];
        knowledgeMode?: string;
        documentBase64?: string | null;
        streamType?: string | null;
        selectionStart?: number | null;
        selectionLength?: number | null;
        summary?: { overview: string; keyPoints: string[] };
    }
    interface DocumentAssistRequest {
        action: "question" | "summary" | "rewrite";
        documentBase64: string;
        sessionId?: string | null;
        question?: string | null;
        selectionStart?: number | null;
        selectionLength?: number | null;
        selectedText?: string | null;
        tone?: string | null;
        knowledge?: KnowledgeScope | null;
    }
    interface ClassificationRequest { documentBase64: string; sessionId?: string | null; }
    interface Classification {
        sessionId: string;
        category: string;
        confidence: number;
        signals: string[];
        suggestedActions: { id: string; title: string; description: string; prompt: string }[];
    }
    interface RuntimeDownload {
        version: string;
        baseUrl: string;
        assets: { platform: "win-x64" | "linux-x64"; backend: "Cpu" | "Cuda" | "Vulkan"; fileName: string; bytes: number; sha256: string }[];
    }
    class Client {
        constructor(options?: ClientOptions);
        readonly baseUrl: URL;
        url(path: string): string;
        fetch(path: string, options?: RequestOptions): Promise<Response>;
        request<T = unknown>(path: string, options?: RequestOptions): Promise<T>;
        post<T = unknown>(path: string, payload: unknown, options?: RequestOptions): Promise<T>;
        stream(path: string, payload: unknown, onEvent?: (event: StreamEvent) => void, options?: RequestOptions): Promise<CompleteEvent>;
        chat(message: string, onEvent?: (event: StreamEvent) => void, options?: ChatOptions): Promise<CompleteEvent>;
        assistDocument(request: DocumentAssistRequest, onEvent?: (event: StreamEvent) => void, options?: RequestOptions): Promise<CompleteEvent>;
        classifyDocument(request: ClassificationRequest, options?: RequestOptions): Promise<Classification>;
        resetDocument(options?: RequestOptions): Promise<unknown>;
        resetChat(options?: RequestOptions): Promise<unknown>;
        exportAnswer(request: { mode: "chat" | "document"; answerId: string; format: string }, options?: RequestOptions): Promise<unknown>;
        runtimeInstallation(options?: RequestOptions): Promise<unknown>;
        runtimeAdmin(action: string, payload?: unknown, options?: RequestOptions): Promise<unknown>;
        installRuntime(backend?: "Auto" | "Cpu" | "Cuda" | "Vulkan", options?: RequestOptions): Promise<unknown>;
        configureRuntimeDownload(download: RuntimeDownload, options?: RequestOptions): Promise<unknown>;
        cancelRuntimeInstallation(options?: RequestOptions): Promise<unknown>;
        removeRuntime(installationId: string, options?: RequestOptions): Promise<unknown>;
        unloadModel(options?: RequestOptions): Promise<unknown>;
        knowledgeCollections(options?: RequestOptions): Promise<KnowledgeCollection[]>;
        knowledgeStatus(options?: RequestOptions): Promise<{ enabled: boolean; embeddingConfigured: boolean }>;
        knowledgeEmbedding(options?: RequestOptions): Promise<{ current: KnowledgeEmbeddingSettings; availableModels: string[] }>;
        configureKnowledgeEmbedding(settings: KnowledgeEmbeddingSettings, options?: RequestOptions): Promise<{ restartRequired: boolean; message: string }>;
        createKnowledgeCollection(name: string, options?: RequestOptions): Promise<KnowledgeCollection>;
        renameKnowledgeCollection(collectionId: string, name: string, options?: RequestOptions): Promise<void>;
        deleteKnowledgeCollection(collectionId: string, options?: RequestOptions): Promise<void>;
        searchKnowledge(request: KnowledgeSearchRequest, options?: RequestOptions): Promise<KnowledgeSearchResult>;
        knowledgeJobs(collectionId: string, options?: RequestOptions): Promise<KnowledgeJob[]>;
        knowledgeSources(collectionId: string, options?: RequestOptions): Promise<KnowledgeSource[]>;
        reindexKnowledgeSource(collectionId: string, versionId: string, options?: RequestOptions): Promise<{ jobId: string }>;
        changeKnowledgeJob(collectionId: string, jobId: string, retry: boolean, options?: RequestOptions): Promise<unknown>;
        removeKnowledgeSource(collectionId: string, documentId: string, options?: RequestOptions): Promise<unknown>;
        resolveKnowledgePassage(collectionId: string, chunkId: string, options?: RequestOptions): Promise<KnowledgePassage>;
        knowledgeSourceUrl(collectionId: string, versionId: string): string;
        uploadKnowledge(collectionId: string, file: File, options?: RequestOptions): Promise<{ jobId: string }>;
    }
}

editor.d.ts

/// <reference path="client.d.ts" />
/** Browser globals provided by editor.js. The application supplies the licensed editor. */
declare namespace TXTextControlAI {
    interface OperationOptions { signal?: AbortSignal; }
    interface Selection { start: number; length: number; text: string; }
    interface EditorAdapterOptions {
        /** Set only if the host has already observed textControlLoaded. */
        alreadyReady?: boolean;
        /** Ready/native callback deadline; default 30000 ms. Not an inference timeout. */
        timeoutMilliseconds?: number;
    }
    type EditorErrorCallback = (error: { msg?: string; message?: string; handled?: boolean }) => void;
    /** MVC startup object, available before the WebSocket supplies the full editor API. */
    interface EditorBootstrap {
        addEventListener(name: string, callback: (...args: unknown[]) => void): void;
    }
    /** Structural subset of the official TXTextControl JavaScript API after textControlLoaded. */
    interface EditorInstance extends EditorBootstrap {
        removeEventListener(name: string, callback: (...args: unknown[]) => void): void;
        StreamType: Record<string, number>;
        EditMode: { Edit: number; ReadOnly: number };
        getEditMode(callback: (mode: number) => void, error?: EditorErrorCallback): void;
        setEditMode(mode: number, callback?: () => void, error?: EditorErrorCallback): void;
        saveDocument(type: number, callback: (event: { data: string }) => void, settings?: unknown, error?: EditorErrorCallback): void;
        loadDocument(type: number, data: string, callback: () => void, settings?: unknown, error?: EditorErrorCallback): void;
        focus(): void;
        selection: {
            getStart(callback: (value: number) => void, error?: EditorErrorCallback): void;
            getLength(callback: (value: number) => void, error?: EditorErrorCallback): void;
            getText(callback: (value: string) => void, error?: EditorErrorCallback): void;
            setStart(value: number, callback?: () => void, error?: EditorErrorCallback): void;
            setLength(value: number, callback?: () => void, error?: EditorErrorCallback): void;
            setText(value: string, callback?: () => void, error?: EditorErrorCallback): void;
        };
    }
    interface EditorEvents {
        ready: void;
        selectionChanged: Selection;
        documentChanged: number;
        documentLoaded: { generation: number };
        error: Error;
        disposed: void;
    }
    class EditorConflictError extends Error { constructor(message?: string); }
    class EditorAdapter {
        static streamTypeForFile(name: string): string | undefined;
        constructor(editor: EditorBootstrap, options?: EditorAdapterOptions);
        readonly editor: EditorBootstrap & Partial<EditorInstance>;
        readonly isReady: boolean;
        ready(options?: OperationOptions): Promise<void>;
        on<K extends keyof EditorEvents>(name: K, callback: (event: EditorEvents[K]) => void): () => void;
        getSelection(options?: OperationOptions): Promise<Selection>;
        selectRange(start: number, length: number, options?: OperationOptions & { focus?: boolean }): Promise<void>;
        replaceSelection(text: string, options?: OperationOptions): Promise<void>;
        saveDocument(options?: OperationOptions & { streamType?: string }): Promise<string>;
        loadDocument(streamType: string, data: string, options?: OperationOptions): Promise<void>;
        loadFile(file: File, options?: OperationOptions): Promise<void>;
        dispose(): void;
    }
    interface AssistanceOptions extends OperationOptions { onProgress?: (event: StreamEvent) => void; knowledge?: KnowledgeScope | null; }
    interface AssistanceResult extends CompleteEvent { applied: boolean; }
    interface AssistantEvents {
        selectionChanged: Selection;
        documentChanged: number;
        classified: Classification;
        progress: StreamEvent;
        completed: AssistanceResult;
        conflict: EditorConflictError;
        error: Error;
    }
    class DocumentAssistant {
        constructor(options: { client: Client; editor: EditorBootstrap | EditorAdapter; editorOptions?: EditorAdapterOptions });
        readonly client: Client;
        readonly adapter: EditorAdapter;
        readonly sessionId: string | null;
        ready(options?: OperationOptions): Promise<void>;
        on<K extends keyof AssistantEvents>(name: K, callback: (event: AssistantEvents[K]) => void): () => void;
        getSelection(options?: OperationOptions): Promise<Selection>;
        selectRange(start: number, length: number, options?: OperationOptions & { focus?: boolean }): Promise<void>;
        saveDocument(options?: OperationOptions & { streamType?: string }): Promise<string>;
        loadDocument(streamType: string, data: string, options?: OperationOptions): Promise<void>;
        loadFile(file: File, options?: OperationOptions): Promise<void>;
        classify(options?: OperationOptions): Promise<Classification>;
        execute(question: string, options?: AssistanceOptions): Promise<AssistanceResult>;
        summarize(options?: AssistanceOptions): Promise<AssistanceResult>;
        rephrase(tone?: string, options?: AssistanceOptions): Promise<AssistanceResult>;
        reset(options?: OperationOptions): Promise<void>;
        cancel(): void;
        dispose(): void;
    }
}
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-beta.1 37 9/9/2026

Beta 1 of Text Control AI integration libraries. APIs may change before a stable release. Requires use with appropriately licensed TX Text Control products.