Images & Attachments
Images travel with an AiInput and are stored as attachments: the message document only holds an
ImagePart with an AttachmentRef, never the bytes.
Sending an image
Section titled “Sending an image”var turn = session.Send( AiInput.Text("What plant is this?") .WithImage(photoBytes, "image/jpeg", description: "plant", retention: ImageRetention.DropAfterTurn));WithImage adds an AiImage(Data, MediaType, Description, Retention) to AiInput.Images. When the turn starts,
the session saves each image through the IAiAttachmentStore and stores the user message with an
ImagePart(Attachment, Description, Retention) per image. AiChatView does this for you when AllowImages is on,
and the camera tool does it for photos.
Retention
Section titled “Retention”ImageRetention |
The model sees the image… | Stored |
|---|---|---|
DropAfterTurn (default) |
in the turn it arrived in; later prompts say [image: {description}] |
yes |
KeepInContext |
in every prompt until the message is compacted (costs tokens every turn) | yes |
DontStore |
once, from memory, in the turn it arrived in; then it’s gone | no |
Images are counted at roughly 1K tokens each in context estimates (providers vary with image size).
Attachment stores
Section titled “Attachment stores”| Store | Registration | Notes |
|---|---|---|
InMemoryAiAttachmentStore |
default | lost when the app exits |
FileAiAttachmentStore |
opts.UseFileAttachments(directory) |
files under {directory}/{sessionId}/; provides a display URI (the file path) for chat bubbles |
| your own | opts.SetAttachmentStore<T>() |
implement IAiAttachmentStore |
builder.Services.AddShinyAiConversation(opts =>{ opts.SetSqliteDocDbStores(Path.Combine(FileSystem.AppDataDirectory, "ai.db")); opts.UseFileAttachments(Path.Combine(FileSystem.AppDataDirectory, "ai_attachments"));});public interface IAiAttachmentStore{ Task<AttachmentRef> Save(ReadOnlyMemory<byte> content, string mediaType, string sessionId, CancellationToken ct = default); Task<byte[]?> Load(string attachmentId, CancellationToken ct = default); Task Delete(string attachmentId, CancellationToken ct = default); Task DeleteForSession(string sessionId, CancellationToken ct = default); string? GetDisplayUri(string attachmentId) => null; // for UIs; optional}IAiSessionManager.Delete(sessionId) calls DeleteForSession, so deleting a conversation removes its images.
Files are not encrypted; use a custom store if conversations are sensitive.
Follow-up input from tools
Section titled “Follow-up input from tools”A tool can’t put an image in front of the model on most providers, but it can queue a follow-up: tools added in
OnBeforeTurn get the TurnContext, and context.QueueFollowUp(input) sends another user input once the current
model round ends, as a second round of the same turn (same turn id, events and voice playback). End the round by
setting FunctionInvokingChatClient.CurrentContext.Terminate = true. This is how the camera tool delivers photos.


