Skip to content

AI scenarios

Conversations

Bit.BlazorUI.AI Scripted transport

A sidebar over a conversation store, grouped by recency, with new, switch, and delete - and a reply that keeps streaming in a thread you switched away from. Then a conversation written out as JSON and read back.

Live demo

Send a message to start a thread, press New for another one, and switch between them - even while a reply is still arriving.

Threads over a conversation store

New conversation
Send a message and this thread will appear in the list.

How it works

A BitAiConversation is a plain observable object, and a BitAiChatSession drives one of them. Switching threads means pointing the chat at a different session, not tearing anything down: each thread keeps its own live session here, so a reply started in one keeps streaming while another is open, and is there when you come back.

Persistence is an interface. This page saves through IBitAiConversationStore, registered for the site as the in-memory store with ai.UseConversationStore<BitAiInMemoryConversationStore>(); a real application implements it over its own database. It is scoped, so on Blazor Server each visitor gets their own threads rather than everyone's.

The store's four methods are the whole contract: ListAsync returns the BitAiConversationSummarys the sidebar draws (id, title, last change, a preview, the message count), LoadAsync one conversation in full, SaveAsync creates or replaces one, and DeleteAsync removes one. A loaded conversation is an IBitAiConversation; BitAiConversationSerializer.ToConversation(loaded) turns it into the BitAiConversation a session drives, returning it as it is when it already is one.

A new thread is saved when its first message arrives (BitAiConversation.MessageAdded), and again as each reply completes (BitAiChatSession.TurnCompleted), since a store over a database keeps the reply as it was handed - empty, had it been saved only as it arrived. New reuses a thread nobody has written in yet, so pressing it twice does not leave an empty conversation in the list.

Saved and restored

Write a message or two and press Save: the thread is written out as JSON. Keep writing, then press Restore: the thread is read back from that JSON into a session of its own.

A conversation as JSON

Write something to save
Then press Save, keep writing, and press Restore.

Persisting a conversation

A store over a database keeps data, not live objects, and BitAiConversationSerializer is what turns one into the other. ToSnapshot(conversation) takes a BitAiConversationSnapshot - plain records of the conversation, its messages (BitAiMessageSnapshot) and their parts (BitAiPartSnapshot), its artifacts and their versions, its agent runs, under a SchemaVersion - and ToJson(snapshot) writes it; SnapshotFromJson(json) and ToConversation(snapshot) go back, and ToJson(conversation) / FromJson(json) do both steps at once, as the demo above does. A reply that was still streaming when it was saved comes back cancelled, and a tool call that never finished likewise: nothing will ever finish them.

An attachment is kept by reference (Url) and with its inline Data; a ContentFactory, like an error's Exception, cannot be written and is dropped. The JSON is written through the package's source-generated metadata, so it is safe to trim. A BitAiConversationSerializerOptions adapts it: SnapshotPart and RestorePart persist a part type of the app's own (without them a BitAiCustomJsonPart keeps its JSON, but any other part of the app's keeps only its text, and comes back as a BitAiCustomJsonPart), and Json takes settings that know the app's types, for values the app keeps in the Extensions or Metadata dictionaries (CreateJsonOptions() plus the app's JsonSerializerContext). The FileConversationStore.cs tab is a store over a folder of JSON files built that way.

Components used

  • BitAiConversationList - the sidebar: today, yesterday, the previous seven and thirty days, then by month.
  • BitAiChat - the open thread, keyed by its conversation so switching starts it afresh.
  • BitAiCodeBlockView - the saved JSON, with its copy button.

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.