Class RagCitations
The two property keys the pipeline talks over, the pin key, and the marker resolver.
public static class RagCitations
- Inheritance
-
RagCitations
- Inherited Members
Fields
- ContextMessagePropertyKey
Set to
trueon theAdditionalPropertiesof the retrieved-contextChatMessagethis middleware injects, marking it pinned: a history reducer must never evict it.It exists because the injected block breaks the shape a turn-based reducer assumes. The block is a second
ChatRole.Usermessage sitting immediately before the real user message, so the tail of the list is user-then-user rather than the user/assistant pairsEdgeChatTokenBudgetReducerevicts in - and evicting the grounding the whole recipe exists to supply, to make room for history, would be the worst possible trade.The literal is duplicated in
Qavren.Edge.Chat.Onnxon purpose.Qavren.Edge.Ragdoes not reference that package and must not, so the two cannot share a constant; the chat package'sChatHistoryOptions.PinnedMessageKeysdefaults to this exact string and a tier-1 test asserts the two literals are equal. That is the same deliberate duplication, with the same asserted-equality guard, that sub-project 2 already uses forEdgeVectorData.QueryGeneratorServiceKeyandEdgeEmbeddings.QueryServiceKey. A reducer that does not know the key simply sees an ordinary user message - it degrades to the old behaviour rather than breaking.
- GroundedPropertyKey
Response-side only. Whether the answer was produced from retrieved sources.
- SourcesPropertyKey
Carries
IReadOnlyList<RagSource>, ranked, clamped andOrdinal-stamped.It is used on both sides of the call, and that is the specified channel by which an inner client receives the structured sources. Before invoking the inner client,
RagChatClientclones the caller'sChatOptionsand sets this key on the clone'sAdditionalProperties; after the turn it sets the same key on theChatResponse. An inner client that wants the sources reads the request-side entry and never re-parses the rendered text block, which exists for the model to read.ExtractiveChatClientis the one in-box client that does so.The clone matters: MEAI's
IChatClientcontract permits an implementation to mutate theChatOptionsit is handed, so consumers must not share one across concurrent calls - which means this client must not write into the caller's instance either.ChatOptions.Clone()is public and shallow-copiesAdditionalPropertiesinto a new dictionary, which is exactly the isolation needed.A leaf that does not know this key ignores it:
EdgeChatClientforwardsAdditionalPropertiestoGeneratorParams.SetSearchOptiononly forboolanddoublevalues and skips everything else, so aRagSourcelist on the request is inert rather than a failure. A third-party leaf that inspected every property would see a documented, publicly-typed value.
Methods
- AttachTo(ChatResponseUpdate, IReadOnlyList<CitationAnnotation>)
Attaches
citationstoupdateby appending oneTextContent(string.Empty)whoseAnnotationshold them. The carrier is spelled out as a public helper because anAIAnnotationhangs off anAIContentand the final update has no text content of its own to hang them on - and because a consumer writing their own middleware should attach them the same way.
- Build(string, IReadOnlyList<RagSource>)
Finds
[n]markers in a completed answer and builds oneCitationAnnotation { Title, Url, FileId, Snippet }per distinct marker, each carrying aTextSpanAnnotatedRegion(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.