Skip to content

ContentGuardPipeline

Namespace AgentPrism · Assembly AgentPrism.Core.dll

Pipeline that runs the registered IContentGuard implementations in sequence and records their decisions.

public sealed class ContentGuardPipeline

objectContentGuardPipeline

object.GetType(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()

Guards run in registration order and the strictest decision wins: if a guard returns ContentGuardAction.Mask the text is replaced and the replaced version is handed to the next guard; if a guard returns ContentGuardAction.Block the chain stops immediately. Because Block is the taxonomy’s highest value, the outcome does not depend on registration order.

If a guard throws, the run fails. The “observability does not break functionality” rule does not apply here: a guard is a control, not an observation tool, and content that cannot be inspected is not let through.

The recording of the decision, however, is subject to that rule: if the event writer or the audit log fails, the failure is logged and the decision is still applied. The decision itself is never lost.

ContentGuardPipeline(IEnumerable<IContentGuard>, IOptionsMonitor<AgentPrismContentGuardOptions>, IAuditLog, IAuditActorResolver, ITenantContext, ILoggerFactory)

Section titled “ ContentGuardPipeline(IEnumerable<IContentGuard>, IOptionsMonitor<AgentPrismContentGuardOptions>, IAuditLog, IAuditActorResolver, ITenantContext, ILoggerFactory)”

Creates a new pipeline.

public ContentGuardPipeline(IEnumerable<IContentGuard> guards, IOptionsMonitor<AgentPrismContentGuardOptions> options, IAuditLog auditLog, IAuditActorResolver actorResolver, ITenantContext tenantContext, ILoggerFactory loggerFactory)

guards IEnumerable<IContentGuard>

The registered guards. May be empty.

options IOptionsMonitor<AgentPrismContentGuardOptions>

Pipeline settings.

auditLog IAuditLog

The audit log to write blocking decisions to.

actorResolver IAuditActorResolver

The audit log actor resolver.

tenantContext ITenantContext

The tenant context to use when no run scope is available.

loggerFactory ILoggerFactory

The logger factory.

ArgumentNullException

One of the dependencies is null.

Whether at least one guard is registered.

public bool HasGuards { get; }

bool

If false, ModelProviderRegistry never adds the inspection wrapper to the pipeline: not even a single if runs on the model-call path.

The pipeline’s current settings.

public AgentPrismContentGuardOptions Options { get; }

AgentPrismContentGuardOptions

InspectAsync(ContentGuardDirection, string, string?, CancellationToken)

Section titled “ InspectAsync(ContentGuardDirection, string, string?, CancellationToken)”

Runs a piece of text through every guard.

public ValueTask<string?> InspectAsync(ContentGuardDirection direction, string text, string? modelId, CancellationToken cancellationToken = default)

direction ContentGuardDirection

The direction of the inspection.

text string

The text to inspect.

modelId string?

The identity of the model being called.

cancellationToken CancellationToken

The cancellation token.

ValueTask<string?>

The masked text; null if no guard requested a change. A null return tells the caller it needs to rebuild nothing, and is the allocation-free path.

AgentPrismContentBlockedException

A guard returned ContentGuardAction.Block.

PreviewAsync(ContentGuardDirection, string, string?, CancellationToken)

Section titled “ PreviewAsync(ContentGuardDirection, string, string?, CancellationToken)”

Runs text through every guard but records no decision — it writes to neither the event writer nor the audit log.

public ValueTask<string?> PreviewAsync(ContentGuardDirection direction, string text, string? modelId, CancellationToken cancellationToken = default)

direction ContentGuardDirection

text string

modelId string?

cancellationToken CancellationToken

ValueTask<string?>

The text to record; null if no guard requested a change (the caller should use the original text).

This exists for RunRecordingAgent’s BeginRunAsync: the text written to the RunStarted event and to IRunInputStore must be THE SAME as the guard’s decision, but at this stage the run row (runs) does not exist yet. When ContentGuardPipeline.InspectAsync finds a decision it calls scope.Writer.AppendAsync; without a runs row the store rejects it and the writer is PERMANENTLY disabled for the whole run (RunEventWriter.IsDisabled). This method applies THE SAME guard order and masking chain without carrying that risk; the actual decision recording happens when AgentPrism.ContentGuardingChatClient calls ContentGuardPipeline.InspectAsync normally on the way to the model.

On a block, no exception is thrown — the caller should continue starting the run; the real block happens on the way to the model, and the run then closes with Failed/content_blocked.