AI scenarios
Conversations
Bit.BlazorUI.AI Scripted transportA 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
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
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.