Table of Contents

Method Build

Namespace
Qavren.Edge.Rag
Assembly
Qavren.Edge.Rag.dll

Build(string, IReadOnlyList<RagSource>)

Finds [n] markers in a completed answer and builds one CitationAnnotation { Title, Url, FileId, Snippet } per distinct marker, each carrying a TextSpanAnnotatedRegion(StartIndex, EndIndex) per occurrence. A marker with no matching source is left as plain text, counted, and never fabricated into a citation. A total function: it does not throw.

public static IReadOnlyList<CitationAnnotation> Build(string answer, IReadOnlyList<RagSource> sources)

Parameters

answer string

The complete answer text, and the string every returned StartIndex and EndIndex indexes. On the streaming path that is the concatenation of every text delta in the response; after aggregation it is ChatResponse.Text. They are the same string, which is why one set of offsets serves both.

sources IReadOnlyList<RagSource>

The ranked, clamped, Ordinal-stamped list the block was rendered from. A marker is matched against RagSource.Ordinal, never against list position, so an unstamped list resolves nothing rather than citing the wrong chunk.

Returns

IReadOnlyList<CitationAnnotation>

Remarks

EndIndex is exclusive: answer[StartIndex..EndIndex] is exactly the marker text. Both are always set, so a consumer's EndIndex - StartIndex needs one null check and no fallback. A marker inside a code fence is treated like any other marker - the scan is lexical, and that is documented behaviour rather than an oversight.