Custom Chat recipe
Source: 08_CustomChat/ · a drop-in replacement for the workspace's default chat experience.
How it looks
A custom header with a topic selector, example chips, a transcript, and per-message thumbs-up/down commands. The recipe routes messages to a workspace endpoint; the runnable Tesserae preview below echoes a canned reply so you can try it here.
What it teaches
- Calling a custom backend endpoint for
PostMessage. - Rendering a custom header with a topic selector beside the assistant-template dropdown.
- Showing custom empty-state examples that pre-fill the input box on click.
- Adding per-message commands (thumbs up / thumbs down) for assistant replies.
Code shape
var endpoints = new CustomChatView
{
Title = "Recipe Chat",
PostMessage = PostMessageAsync // ← the only required override for a basic custom chat
};
_chatView = ChatView(endpoints, state)
.WithCustomHeader(BuildHeader)
.WithCustomExamples(BuildExamples)
.WithMessageCommands(BuildMessageCommands);
Other customization points on CustomChatView:
| Property | Use when |
|---|---|
NewChat |
You want to create the chat row yourself (e.g. attach pre-set context). |
ReplaceMessage |
You support message editing and need to re-trigger your own pipeline. |
ListTools |
The chat should show a curated set of tools instead of every one in the workspace. |
ListChats |
You're filtering the chat list (per-user, per-project, archived view). |
Settings on the chat view itself
Separately from CustomChatView, the view takes a ChatViewConfiguration that decides how the surface behaves for everyone using it — a property of the surface, not a switch the reader has to find:
| Setting | Effect |
|---|---|
MaxMessageCharacters |
Caps how long a single message may be. The composer stops taking text at the limit and shows a "used / max" counter next to the send button once more than half the budget is spent; below that the counter is collapsed, so an ordinary message never carries a number. Zero (the default) means no limit and no counter. |
Skills |
The skills every turn on this surface carries. Leave it null and the view says nothing about skills, so the run falls back to the ones configured on the chat's assistant template. An empty array is a choice, not silence — it carries no skills at all. |
GroupedToolsThreshold |
How long a run of consecutive tool calls has to be before the transcript collapses it into one "Used N tools" summary. The default of 2 groups every run of more than one call; raise it to leave short runs as individual calls, and set it past any run a turn can produce (int.MaxValue) to never group at all. A lone call is never a group, so anything below 2 behaves as 2. |
GroupedToolsDisplay |
How that summary presents itself: a pill opening a modal (the default), a pill expanding in place, or already expanded in place. Only affects the persisted transcript — a streaming reply always shows its calls inline and expanded. |
DisableMessageQueueing |
By default, sending while the assistant is still replying holds the message as the chat's next turn rather than interrupting the reply. Set this to make the composer's trigger stop the reply instead. |
ParseAs |
Which entity-linking parse the composer applies. |
How the backend call works
private async Task<UID128> PostMessageAsync(CustomChatView.PostMessageRequest request)
{
// In a real chat you'd call:
// return await Mosaik.API.Endpoints.CallAsync<UID128>(
// "recipes/chat/post-message",
// new RecipeChatRequest { Message = request.Message, Topic = _topic.Value });
var reply = await RecipeEndpoints.PostChatMessageAsync(new RecipeChatRequest { ... });
Toast().Information(reply.Reply);
return UID128.Empty; // "I handled it myself"
}
RecipeEndpoints.PostChatMessageAsync lives in src/API/Endpoints.cs and currently returns a hard-coded reply. Once you have a real workspace endpoint, uncomment the Mosaik.API.Endpoints.CallAsync<T> line there and delete the canned fallback.
See also
TechnicalSupport.FrontEnd/src/SupportChat.cs— fuller example with chat-scoped context state attached toChatMetadata.- The chat reference in
INSTRUCTIONS.md:custom-front-end/INSTRUCTIONS.md#custom-chat-interface. Mosaik.FrontEnd/src/Desktop/ChatAIView/CustomChatView.cs— the full surface ofCustomChatViewand its request DTOs.