RagAccelerator 1.3.0
dotnet add package RagAccelerator --version 1.3.0
NuGet\Install-Package RagAccelerator -Version 1.3.0
<PackageReference Include="RagAccelerator" Version="1.3.0" />
<PackageVersion Include="RagAccelerator" Version="1.3.0" />
<PackageReference Include="RagAccelerator" />
paket add RagAccelerator --version 1.3.0
#r "nuget: RagAccelerator, 1.3.0"
#:package RagAccelerator@1.3.0
#addin nuget:?package=RagAccelerator&version=1.3.0
#tool nuget:?package=RagAccelerator&version=1.3.0
RagAccelerator
A stateless RAG processing engine as a NuGet library. Install it, register it, and call the services in-process — no HTTP, no App Service, no API key, and nothing is persisted. You keep your own database and store whatever the calls return.
All model credentials + settings are supplied per call (embeddings, chat, OCR) — the library owns no keys.
Install
dotnet add package RagAccelerator
Register
using RagAccelerator;
builder.Services.AddRagAccelerator(builder.Configuration); // configuration optional
Ingest one document — three modes
Pick the mode that matches how you search:
| Mode | Call | Needs embedding creds? | Chunk output |
|---|---|---|---|
| Vector (semantic) | ProcessAsync |
Yes | Embedding[] |
| BM25 / vectorless (keyword) | ProcessLexicalAsync |
No | Bm25Text |
| Both (hybrid) | ProcessHybridAsync |
Yes | Embedding[] + Bm25Text |
using Rag.Application.Abstractions;
using Rag.Application.DTOs;
using Rag.Application.Common;
public class MyIngest(IDocumentProcessingService processing)
{
// Content is a Stream — pass a SharePoint/blob download stream or File.OpenRead(path).
public Task<DocumentProcessingResult> RunAsync(Stream content, string fileName) =>
processing.ProcessAsync(new DocumentProcessingRequest // Vector mode
{
Content = content,
FileName = fileName,
// Chunking (optional; default TopicWise)
ChunkingStrategy = (int)Rag.Domain.Enums.ChunkingStrategyType.TopicWise,
OverlapPercentage = 15,
// Embedding (required for Vector/Both — your vendor + creds)
EmbeddingSpec = new EmbeddingSpec(
deployment: "text-embedding-3-large",
dimensions: 3072,
endpoint: "https://your-aoai.openai.azure.com/",
apiKey: "<key>",
provider: AIProvider.AzureOpenAI)
// OCR fields (OcrEngine/OcrEndpoint/OcrApiKey/ImageProcessingEnabled) only for images/scanned docs.
});
// result.Chunks: [{ Index, Text, Embedding[], Bm25Text, PageNumber }] → store in YOUR DB.
// Unsupported type → result.Skipped == true (not an exception).
}
Supported types: pdf, doc, docx, xls, xlsx, ppt, pptx, png, jpg, jpeg, txt, html.
Input forms — stream, bytes, or raw text
Same three modes (Vector / BM25 / Hybrid) are available for each input form:
| Input | Methods | Notes |
|---|---|---|
| Stream | ProcessAsync / ProcessLexicalAsync / ProcessHybridAsync (DocumentProcessingRequest) |
file stream from disk/SharePoint/blob |
| Bytes | ProcessBytesAsync / ProcessBytesLexicalAsync / ProcessBytesHybridAsync (DocumentBytesRequest) |
in-memory byte[]; OCR/extraction still apply |
| Text | ProcessTextAsync / ProcessTextLexicalAsync / ProcessTextHybridAsync (TextIngestionRequest) |
raw string (e.g. scraped web text); no OCR |
// Raw text you already have (e.g. scraped) — Url is REQUIRED (source reference; never fetched).
var result = await processing.ProcessTextAsync(new TextIngestionRequest
{
Text = scrapedText,
Url = "https://example.com/policies", // becomes result.DocumentName / citation
EmbeddingSpec = embeddingSpec // omit for ProcessTextLexicalAsync (BM25-only)
});
BM25 / vectorless storage (Postgres)
ProcessLexicalAsync/ProcessHybridAsync set chunk.Bm25Text — the normalized text you store for full-text/BM25 search. Store it once and let Postgres build the index; nothing else to do per insert:
ALTER TABLE chunks ADD COLUMN bm25_text text;
ALTER TABLE chunks ADD COLUMN tsv tsvector
GENERATED ALWAYS AS (to_tsvector('english', bm25_text)) STORED;
CREATE INDEX chunks_tsv_idx ON chunks USING GIN (tsv);
-- rank at query time with ts_rank(tsv, plainto_tsquery('english', @q)) (or pg_search for true BM25)
BM25-only ingest (ProcessLexicalAsync) needs no embedding credentials.
QnA
// 1) Embed the question, then run vector similarity search in YOUR DB.
float[] qvec = await embedding.EmbedAsync(question, embeddingSpec);
// 2) Answer over the chunks you retrieved.
AnswerResult answer = await answering.AnswerAsync(new AnswerRequest
{
Question = question,
Chunks = topChunks.Select(c => new AnswerChunk { Text = c.Text, DocumentName = c.Name }).ToList(),
ChatSpec = new ChatModelSpec(AIProvider.AzureOpenAI, "https://your-aoai.openai.azure.com/", "<key>", "gpt-4o"),
Persona = "You are a helpful HR assistant.",
IncludeSuggestedQuestions = true
});
// answer.Answer, answer.References, answer.SuggestedQuestions, answer.Summary
(IEmbeddingService embedding, IAnswerService answering injected.)
Answer options (all optional)
CustomSystemPrompt— supply your own answer prompt (adds to the built-in grounding prompt /Persona/Instructions). The mandatory security preamble is always applied on top — overriding the prompt never removes injection guardrails.IncludeSuggestedQuestions(+SuggestedQuestionsPrompt) — return 2 follow-ups; custom prompt optional.IncludeSummary(+SummaryPrompt) — return a short summary of the answer; custom prompt optional.SanitizeOutput— strip markdown links/images + bare URLs from outputs (anti-exfiltration). Default off.
Rule for every prompt field: you supply one → it's used; you don't → our default.
Multiturn: rewrite a follow-up (IQuestionRewriteService)
Turn "what about 2024?" into a standalone question using the last few turns, before you retrieve:
var r = await rewriting.RewriteAsync(new RewriteQuestionRequest
{
Question = "what about 2024?",
History = last5.Select(t => new ConversationTurn { Question = t.Q, Answer = t.A }).ToList(),
ChatSpec = chatSpec // CustomPrompt optional
});
// r.RewrittenQuestion → embed / search / AnswerAsync
Composite questions: split into sub-queries (IQuerySplitService)
var s = await splitting.SplitAsync(new SplitQueryRequest { Question = q, ChatSpec = chatSpec });
// s.IsComposite, s.SubQueries → retrieve + answer each, then merge (CustomPrompt optional)
Security (prompt injection)
The attacker surface is the end-user question and poisoned document content (indirect injection) — not you, the developer. Enforced automatically (no setup):
- A mandatory anti-injection preamble on every system prompt (can't be removed by a custom prompt).
- Untrusted context is spotlighted in a random per-request fence and sanitized (chunk text, source labels, and conversation history can't forge prompt structure).
- Input length caps (
SecurityOptions; generous defaults — normal top-N chunks pass untouched) guard context-stuffing and cost/DoS. - Structural calls (split/rewrite/summary/suggested) run at temperature 0; content-filter rejections aren't blindly retried.
Opt-in:
AnswerRequest.SanitizeOutput = true— strip exfiltration links from outputs.IContentSafetyhook (default no-op). RegisterAzurePromptShieldsContentSafetyto screen questions/context/output with Azure AI Content Safety Prompt Shields:services.AddScoped<IContentSafety>(sp => new AzurePromptShieldsContentSafety( sp.GetRequiredService<IHttpClientFactory>(), "<content-safety-endpoint>", "<key>"));
Honest limits — injection can't be 100% solved in a stateless library. Also do: enable model-side Prompt Shields, use least-privilege keys, don't auto-render links from Answer/SuggestedQuestions/Summary, treat OCR/GPT-Vision text and conversation history as untrusted, and never paste raw end-user text into CustomSystemPrompt (the trusted zone).
Token usage / cost
Every LLM call reports token usage (input/output/total + model) so you can attribute cost. The library returns tokens only — dollar cost is derived downstream (e.g. Langfuse maps model + tokens → $).
Where usage shows up:
AnswerResult.AnswerUsage,.SuggestedQuestionsUsage,.SummaryUsage, and.TotalUsage(combined).SplitQueryResult.Usage,RewriteQuestionResult.Usage(null when rewrite is skipped).DocumentProcessingResult.EmbeddingUsage(null in BM25-only mode).- Granular:
IChatCompletionService.CompleteWithUsageAsync(...)andIEmbeddingService.EmbedWithUsageAsync(...)/EmbedBatchWithUsageAsync(...).
Combine across a whole request and feed it to your tracing tool:
var total = TokenUsage.Combine(rewrite.Usage, split.Usage, queryEmbed.Usage, answer.TotalUsage);
// e.g. tag an OpenTelemetry span so Langfuse computes cost:
span?.SetTag("gen_ai.request.model", total.Model);
span?.SetTag("gen_ai.usage.input_tokens", total.InputTokens);
span?.SetTag("gen_ai.usage.output_tokens", total.OutputTokens);
Notes: all counts are nullable — AWS Titan reports input tokens only, Cohere reports none. TopicWise chunking's boundary-detection embeddings are not counted in EmbeddingUsage (only the stored-chunk embeddings are).
Notes
- Providers: embeddings — Azure OpenAI, OpenAI, AWS Bedrock; chat — Azure OpenAI, OpenAI; OCR — Azure Document Intelligence, GptVision.
- One document per call (bounded response). You enumerate/fetch your own documents.
- HTTPS is your responsibility — credentials travel in the call arguments.
| Product | Versions 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. |
-
net8.0
- AWSSDK.BedrockRuntime (>= 3.7.412.9)
- Azure.AI.DocumentIntelligence (>= 1.0.0)
- Azure.AI.OpenAI (>= 2.2.0-beta.4)
- DocumentFormat.OpenXml (>= 3.1.0)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- PdfPig (>= 0.1.9)
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 |
|---|