Repository navigation
Add AI personas: an opt-in catalog of reusable characters with their own instructions, name, avatar and voice - #270
Merged
Merged
Conversation
…and voice A persona describes who the assistant is: rendered Liquid instructions, the name it uses, an optional greeting, its language and the voice it speaks with. An AI profile, chat interaction or profile template links to one optional persona by its technical name through AIPersonaMetadata. - AITemplateSources.Persona, PersonaTemplateMetadata, AIPersonaMetadata and AIPersona. - IAIPersonaResolver / DefaultAIPersonaResolver resolve the persona (missing personas and Liquid errors degrade quietly) and choose the voice: explicit, then the persona's when the speaking deployment belongs to the same provider, then the deployment default. - The profile and chat interaction completion context builders place the persona's instructions before the prompt templates and the system message, and expose it under AICompletionContextKeys.Persona. Realtime sessions get it through the same context. - The chat hubs pick voices through the resolver and use the persona's language as the speech language when none is given. - PersonaChatInteractionSettingsHandler saves the personaId setting; profile JSON accepts PersonaId. - AIChatSession.PersonaId records the persona a session started with. - Persona files use Source: Persona front matter; profile templates may name a PersonaId. - Docs: AI Personas page and 2.0.0 changelog entry.
A greeting belongs to the conversation a profile starts, and the profile's welcome message already covers it. Keeping it off the persona lets one persona serve profiles that open differently.
Both sample hosts can now create AI Persona templates (instructions, assistant name, language, a voice picked from a realtime or text-to-speech deployment, avatar) and link a persona to an AI profile or a Profile template. Applying a Profile template copies its persona link to the new profile. When a profile leaves its voice empty and its persona has one, the voice picker reads "Use persona voice (...)".
A template is a blueprint that is copied when it is used; a persona stays linked, so an edit reaches everything that uses it. Personas now have their own catalog instead of being AI templates with Source = Persona. - AIPersona is a named catalog item; the runtime result is ResolvedAIPersona. PersonaTemplateMetadata and AITemplateSources.Persona are gone. - IAIPersonaManager merges saved personas with IAIPersonaProvider ones: files in the content root's Templates/Personas, or embedded with AddEmbeddedPersonas. A saved persona replaces a file with the same name. - Opt-in: .AddPersonas(p => p.AddYesSqlStores() / AddEntityCoreStores()). It registers the manager, the resolver, the catalog handler (unique name, instructions required) and a completion handler that places the persona's instructions at the top of the system message once the profile or interaction has built it. Without the feature, links are ignored. - The chat hubs resolve personas through IServiceProvider extensions that treat a missing feature as no persona. - YesSql: AIPersonaIndex and CreateAIPersonaIndexSchemaAsync. EF Core: a named document catalog. - MVC and Blazor samples: a Personas admin page and gated persona pickers; the Persona source is removed from the template editors. - No migration: personas had not shipped.
A persona's assistant name was only used for display, so a model asked "Who are you?" did not know it unless the instructions happened to say it. - The persona block now starts with "Your name is X." when the persona has an assistant name (AIPersonaExtensions.GetPersonaBlock, used by PrependTo). - The instructions are rendered with the name as the Liquid variable assistant_name; a link parameter with that name wins.
Both sample hosts list Personas on the dashboard next to Templates, shown only when the host adds the persona feature.
AI Chat sessions, the chat widget and Chat Interactions now show the persona a
profile or interaction links to.
- ai-chat-ui: a new assistantAvatarUrl setting beside assistantLabel. Assistant
messages and the default typing indicator show the name and a round avatar in
place of the robot icon, pulsing while a reply streams; without them the chat
looks as before. Custom templates call getAssistantAvatarUrl(message), and a
typing indicator can use the {{ assistantIcon }} token.
- AIChatAssistantDisplay, AIPersonaExtensions.ToChatAssistantDisplay and
IAIPersonaResolver.ResolveChatAssistantDisplayAsync map a persona to those
settings. The MVC and Blazor sample hosts pass them through.
Rebased onto the persona catalog: the display maps a ResolvedAIPersona, and the new IServiceProvider.ResolveChatAssistantDisplayAsync returns the default look when the host did not add personas. The MVC and Blazor chat pages and widgets use it instead of requiring IAIPersonaResolver.
MikeAlhayek
added a commit
to CrestApps/CrestApps.OrchardCore
that referenced
this pull request
Oct 16, 2026
…to 2.0.0-preview.226 - CrestAppsCoreVersion moves to 2.0.0-preview.326, which carries the system prompt template provider (CrestApps/CrestApps.Core#266) this branch adopts, and the AI persona catalog (CrestApps/CrestApps.Core#270). - @crestapps/ai-chat-ui moves to 2.0.0-preview.226 in AI Chat, Chat Interactions and Resources: vendored assets rebuilt, CDN URLs and SRI hashes recomputed. The package no longer ships CSS source maps, so the two vendored .css.map files and their Assets.json entries are removed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A new, opt-in AI persona feature describes reusable characters: who the assistant is, the name it uses, its language, its avatar and the voice it speaks with. Greetings stay with the profile's welcome message, so one persona can serve profiles that open differently. An AI profile, a chat interaction or a profile template links to one optional persona by its technical name (
AIPersonaMetadata.PersonaId). The persona is linked, not copied, so editing it changes everything that uses it from the next message.A persona answers who; the system message and prompt templates still answer what.
Its own catalog, not an AI template
A template is a blueprint that is copied when it is used; a persona stays linked. So personas have their own catalog instead of being AI templates with a Persona source:
AIPersonais a named catalog item (instructions, assistant name, language, voice and voice deployment, avatar URL).IAIPersonaManagermerges saved personas with everyIAIPersonaProvider: persona files in the content root'sTemplates/Personas, or embedded ones registered withAddEmbeddedPersonas(assembly). A saved persona replaces a file with the same name. The catalog handler requires a unique name and instructions, and an update never changes the name..AddPersonas(personas => personas.AddYesSqlStores())(or.AddEntityCoreStores()) on the AI suite builder. YesSql hosts create the index table withCreateAIPersonaIndexSchemaAsync. Without the feature, persona links are ignored and chats run with the profile's own instructions and voice.How it works
IAIPersonaResolveris the single place that turns a link into aResolvedAIPersona(DefaultAIPersonaResolver). A missing persona is logged and the request carries on without one. A Liquid error drops the instructions but keeps the rest.AIPersonaCompletionContextBuilderHandlerruns once the profile or interaction has built its system message and places the persona block first, then the prompt templates, then the system message, so business rules win a conflict. When the persona has an assistant name, the block starts with "Your name is …", and the instructions can use it as the Liquid variableassistant_name. The persona is also exposed underAICompletionContextKeys.Persona. Realtime sessions read their instructions from the same completion context.SelectVoiceAsyncchooses the explicit voice (caller, profile or interaction), then the persona's voice, then the deployment default. The persona's voice is used only when the speaking deployment has the same provider as the deployment it was picked from. The chat hubs go throughIServiceProvider.ResolvePersonaAsync/SelectPersonaVoiceAsync, so a host without personas keeps working.@crestapps/ai-chat-uitakes a newassistantAvatarUrlsetting (data-coreai-chat-assistant-avatar-url) besideassistantLabel. Assistant messages and the typing indicator show the persona's name and a round avatar in place of the robot icon; without them the chat looks as before. Hosts get the values fromIServiceProvider.ResolveChatAssistantDisplayAsync(resource), which returns the default look when personas are not enabled.PersonaChatInteractionSettingsHandlerreadspersonaId; a client that omits the key keeps the current link. Profile JSON accepts a top-levelPersonaId.AIChatSession.PersonaIdrecords the persona a session started with.Sample hosts
Both the MVC (YesSql) and Blazor (EF Core) hosts add personas:
Both hosts were checked live: personas created through each store, linked to profiles, and chats answered in character with the persona's name and avatar.
New public types
AIPersona,ResolvedAIPersona,IAIPersonaManager/DefaultAIPersonaManager,IAIPersonaProviderwithAIPersonaFileSystemProviderandEmbeddedResourceAIPersonaProvider,AIPersonaFileParser,IAIPersonaResolver/DefaultAIPersonaResolver,AIPersonaMetadata,AIPersonaExtensions,AIPersonaServiceProviderExtensions,AIPersonaResolverExtensions,AIChatAssistantDisplay,CrestAppsAIPersonasBuilder,PersonaChatInteractionSettingsHandler,AICompletionContextKeys.Persona,AIChatSession.PersonaId, and the YesSqlAIPersonaIndex.Tests and docs
-warnaserror.npm run lint,npm run checkandtest:unitpass, and the rebuiltwwwrootassets are committed.The Orchard Core side (persona editor, pickers, built-in persona files, recipe step, chat views) follows on a CrestApps.OrchardCore branch once this ships in a preview.
🤖 Generated with Claude Code