Last active
March 13, 2026 13:20
-
-
Save mattbrailsford/199d0b45e926ffb122fa96467039bd90 to your computer and use it in GitHub Desktop.
Umbraco.AI demo install complete with seed data
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
| # Ensure we have the latest Umbraco templates | |
| dotnet new install Umbraco.Templates --force | |
| # Create solution/project | |
| dotnet new sln --name "Umbraco.AI.Demo" | |
| dotnet new umbraco --force -n "Umbraco.AI.Demo" --friendly-name "Administrator" --email "admin@example.com" --password "password1234" --development-database-type SQLite | |
| dotnet sln add "Umbraco.AI.Demo" | |
| # Add starter kit | |
| dotnet add "Umbraco.AI.Demo" package clean | |
| # Add Umbraco AI | |
| dotnet add "Umbraco.AI.Demo" package Umbraco.AI | |
| # Add Umbraco AI addons | |
| dotnet add "Umbraco.AI.Demo" package Umbraco.AI.Prompt | |
| dotnet add "Umbraco.AI.Demo" package Umbraco.AI.Agent --prerelease | |
| dotnet add "Umbraco.AI.Demo" package Umbraco.AI.Agent.Copilot --prerelease | |
| # Add Umbraco AI providers | |
| dotnet add "Umbraco.AI.Demo" package Umbraco.AI.Amazon | |
| dotnet add "Umbraco.AI.Demo" package Umbraco.AI.Anthropic | |
| dotnet add "Umbraco.AI.Demo" package Umbraco.AI.Google | |
| dotnet add "Umbraco.AI.Demo" package Umbraco.AI.MicrosoftFoundry | |
| dotnet add "Umbraco.AI.Demo" package Umbraco.AI.OpenAI | |
| # Seed demo data (connection, profile, context, prompts, agents) | |
| curl -o Umbraco.AI.Demo/UmbracoAISeedData.cs https://gist.githubusercontent.com/mattbrailsford/199d0b45e926ffb122fa96467039bd90/raw/UmbracoAISeedData.cs?v=1 | |
| dotnet run --project "Umbraco.AI.Demo" | |
| #Running |
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
| // UmbracoAISeedData.cs | |
| // | |
| // Drop this file into an Umbraco site project that has Umbraco.AI packages installed. | |
| // It seeds demo data (connection, profile, context, prompts, agents) on first startup. | |
| // | |
| // Prerequisites: | |
| // - Umbraco.AI, Umbraco.AI.OpenAI, Umbraco.AI.Prompt, Umbraco.AI.Agent packages installed | |
| // - OpenAI API key configured in connection settings | |
| // | |
| // The seeder is idempotent — it checks for existing data by alias and skips if already seeded. | |
| using Umbraco.AI.Core.Connections; | |
| using Umbraco.AI.Core.Contexts; | |
| using Umbraco.AI.Core.Models; | |
| using Umbraco.AI.Core.Profiles; | |
| using Umbraco.AI.Core.Settings; | |
| using Umbraco.AI.Core.Tools.Scopes; | |
| using Umbraco.AI.Agent.Core.Agents; | |
| using Umbraco.AI.OpenAI; | |
| using Umbraco.AI.Prompt.Core.Prompts; | |
| using Umbraco.Cms.Core; | |
| using Umbraco.Cms.Core.Composing; | |
| using Umbraco.Cms.Core.Events; | |
| using Umbraco.Cms.Core.Notifications; | |
| namespace Umbraco.AI.Demo; | |
| public class UmbracoAISeedDataComposer : IComposer | |
| { | |
| public void Compose(IUmbracoBuilder builder) | |
| => builder.AddNotificationAsyncHandler<UmbracoApplicationStartedNotification, UmbracoAISeedDataHandler>(); | |
| } | |
| public class UmbracoAISeedDataHandler( | |
| IAIConnectionService connectionService, | |
| IAIProfileService profileService, | |
| IAIContextService contextService, | |
| IAIPromptService promptService, | |
| IAIAgentService agentService, | |
| IAISettingsService settingsService, | |
| AIToolScopeCollection toolScopes, | |
| ILogger<UmbracoAISeedDataHandler> logger) | |
| : INotificationAsyncHandler<UmbracoApplicationStartedNotification> | |
| { | |
| public async Task HandleAsync(UmbracoApplicationStartedNotification notification, CancellationToken ct) | |
| { | |
| // Skip if already seeded (check for our connection alias) | |
| if (await connectionService.GetConnectionByAliasAsync("openai-demo", ct) is not null) | |
| { | |
| logger.LogInformation("Demo data already seeded, skipping."); | |
| return; | |
| } | |
| logger.LogInformation("Seeding Umbraco.AI demo data..."); | |
| // Resolve all available tool scope IDs for full agent permissions | |
| var allToolScopeIds = toolScopes.Select(s => s.Id).ToList(); | |
| // 1. Context | |
| var context = await contextService.SaveContextAsync(new AIContext | |
| { | |
| Alias = "brand-voice", | |
| Name = "Brand Voice", | |
| Resources = | |
| [ | |
| new AIContextResource | |
| { | |
| ResourceTypeId = "brand-voice", | |
| Name = "Brand Guidelines", | |
| Description = "Core brand voice and tone guidelines", | |
| SortOrder = 0, | |
| Data = new | |
| { | |
| ToneDescription = "We are friendly, professional, and approachable. Use clear, simple language. Avoid jargon. Speak directly to the reader using 'you'. Keep sentences short and paragraphs focused.", | |
| TargetAudience = "Web developers and content editors using Umbraco CMS. Our audience ranges from technical developers building sites to non-technical content editors managing day-to-day content. Write so both groups can understand.", | |
| StyleGuidelines = "Use active voice. Lead with the benefit or outcome, not the feature. Use sentence case for headings. Prefer short paragraphs (2-3 sentences). Use bullet points for lists of three or more items. Write at a secondary school reading level.", | |
| AvoidPatterns = "Marketing buzzwords (leverage, synergy, cutting-edge). Exclamation marks. Overly casual language (gonna, wanna). Passive voice where active is possible. Filler phrases (in order to, it is important to note that). Starting sentences with 'So' or 'Basically'." | |
| }, | |
| InjectionMode = AIContextResourceInjectionMode.Always | |
| } | |
| ] | |
| }, ct); | |
| // 2. Connection (API key resolved from IConfiguration via $-prefix) | |
| var connection = await connectionService.SaveConnectionAsync(new AIConnection | |
| { | |
| Alias = "openai-demo", | |
| Name = "OpenAI", | |
| ProviderId = "openai", | |
| Settings = new OpenAIProviderSettings { ApiKey = "YOUR_OPENAI_API_KEY" }, | |
| IsActive = true | |
| }, ct); | |
| // 3. Profile | |
| var profile = await profileService.SaveProfileAsync(new AIProfile | |
| { | |
| Alias = "default-chat", | |
| Name = "Default Chat", | |
| Capability = AICapability.Chat, | |
| ConnectionId = connection.Id, | |
| Model = new AIModelRef("openai", "gpt-4o"), | |
| Settings = new AIChatProfileSettings | |
| { | |
| Temperature = 0.7f, | |
| ContextIds = [context.Id] | |
| } | |
| }, ct); | |
| // 4. Set profile as default in settings | |
| var settings = await settingsService.GetSettingsAsync(ct); | |
| settings.DefaultChatProfileId = profile.Id; | |
| await settingsService.SaveSettingsAsync(settings, ct); | |
| // 5. Prompts | |
| await promptService.SavePromptAsync(new AIPrompt | |
| { | |
| Alias = "summarize", | |
| Name = "Summarize", | |
| Description = "Summarize the current content into a concise paragraph", | |
| Instructions = "Summarize the following content in a single, concise paragraph that captures the key points:\n\n{{currentValue}}\n\nReturn just the result.", | |
| ProfileId = profile.Id, | |
| IsActive = true, | |
| IncludeEntityContext = false, | |
| OptionCount = 3, | |
| Scope = new AIPromptScope | |
| { | |
| AllowRules = [new AIPromptScopeRule { PropertyEditorUiAliases = ["Umb.PropertyEditorUi.TextArea", "Umb.PropertyEditorUi.TextBox"] }] | |
| } | |
| }, ct); | |
| await promptService.SavePromptAsync(new AIPrompt | |
| { | |
| Alias = "seo-description", | |
| Name = "SEO Description", | |
| Description = "Generate an SEO-friendly meta description", | |
| Instructions = "Write an SEO-optimized meta description (150-160 characters) for this content. Include relevant keywords naturally. Return just the result.", | |
| ProfileId = profile.Id, | |
| IsActive = true, | |
| IncludeEntityContext = true, | |
| OptionCount = 1, | |
| Scope = new AIPromptScope | |
| { | |
| AllowRules = [new AIPromptScopeRule { PropertyEditorUiAliases = ["Umb.PropertyEditorUi.TextArea", "Umb.PropertyEditorUi.TextBox"] }] | |
| } | |
| }, ct); | |
| // 6. Agents | |
| await agentService.SaveAgentAsync(new AIAgent | |
| { | |
| Alias = "content-assistant", | |
| Name = "Content Assistant", | |
| Description = "Helps create and edit content across the site", | |
| ProfileId = profile.Id, | |
| ContextIds = [context.Id], | |
| SurfaceIds = ["copilot"], | |
| Instructions = "You are a helpful content assistant for an Umbraco CMS website. Help users create, edit, and improve their content. Be concise and practical.", | |
| IsActive = true, | |
| AllowedToolScopeIds = allToolScopeIds, | |
| Scope = new AIAgentScope | |
| { | |
| AllowRules = [new AIAgentScopeRule { Sections = ["content"] }] | |
| } | |
| }, ct); | |
| await agentService.SaveAgentAsync(new AIAgent | |
| { | |
| Alias = "media-assistant", | |
| Name = "Media Assistant", | |
| Description = "Helps manage and describe media assets", | |
| ProfileId = profile.Id, | |
| ContextIds = [context.Id], | |
| SurfaceIds = ["copilot"], | |
| Instructions = "You are a media assistant for an Umbraco CMS website. Help users write alt text, captions, and descriptions for their media assets. Focus on accessibility and SEO.", | |
| IsActive = true, | |
| AllowedToolScopeIds = allToolScopeIds, | |
| Scope = new AIAgentScope | |
| { | |
| AllowRules = [new AIAgentScopeRule { Sections = ["media"] }] | |
| } | |
| }, ct); | |
| await agentService.SaveAgentAsync(new AIAgent | |
| { | |
| Alias = "legal-specialist", | |
| Name = "Legal Specialist", | |
| Description = "Helps draft and review legal content like terms and conditions, privacy policies, and disclaimers", | |
| ProfileId = profile.Id, | |
| ContextIds = [context.Id], | |
| SurfaceIds = ["copilot"], | |
| Instructions = "You are a legal content specialist for an Umbraco CMS website. Help users draft and review legal content such as terms and conditions, privacy policies, cookie policies, and disclaimers. Write in clear, plain language that is accessible to non-lawyers while remaining legally sound. Always recommend professional legal review for final versions.", | |
| IsActive = true, | |
| AllowedToolScopeIds = allToolScopeIds, | |
| Scope = new AIAgentScope | |
| { | |
| AllowRules = [new AIAgentScopeRule { Sections = ["content"] }] | |
| } | |
| }, ct); | |
| logger.LogInformation("Umbraco.AI demo data seeded successfully."); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment