Marketing sample migration to AGNext (#234)

This commit is contained in:
Kosta Petan 2024-07-19 13:57:13 -07:00 committed by GitHub
parent e67ab18344
commit c425a447a7
120 changed files with 441 additions and 15880 deletions

View File

@ -27,7 +27,9 @@
"version": "8.0.302"
},
"ghcr.io/elanhasson/devcontainer-features/dotnet-aspire-daily:1": {},
"ghcr.io/devcontainers/features/azure-cli:1": {}
"ghcr.io/devcontainers/features/azure-cli:1": {},
"ghcr.io/azure/azure-dev/azd:0": {},
"ghcr.io/devcontainers/features/node:1": {}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

View File

@ -21,18 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.DevTeam", "sam
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.DevTeam.Dapr", "samples\gh-flow\src\Microsoft.AI.DevTeam.Dapr\Microsoft.AI.DevTeam.Dapr.csproj", "{A7677950-18F1-42FF-8018-870395417465}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "marketing", "marketing", "{1FF691E4-E27D-4A7E-861C-4D6291B6EE35}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4225F3BA-A39D-4680-945E-F2869E98AEA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing", "samples\marketing\src\backend\Marketing.csproj", "{62F276F3-9184-4908-A7FB-065B4E491BE2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "seed-memory", "samples\gh-flow\src\seed-memory\seed-memory.csproj", "{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowsApp", "samples\WorkflowsApp\WorkflowsApp.csproj", "{92CAAA29-8633-4984-B169-29BB89E0EA23}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflowsapp", "workflowsapp", "{65CF8F20-D740-46AC-A869-FA609D960A09}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E6A25E68-EA75-4294-9B45-3CF2BB3B4ACD}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
@ -52,6 +42,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greeter.AgentHost", "sample
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greeter.AgentWorker", "samples\Greeter\Greeter.AgentWorker\Greeter.AgentWorker.csproj", "{7BA721F2-EE46-4A85-A8C8-3695C4ADF93E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "marketing-team", "marketing-team", "{DD53D185-5C0A-4B45-BF4D-F597F899E671}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.AgentHost", "samples\marketing-team\src\Marketing.AgentHost\Marketing.AgentHost.csproj", "{5504D650-D013-4F5E-9A42-EDC9B4A36E1D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.Agents", "samples\marketing-team\src\Marketing.Agents\Marketing.Agents.csproj", "{62D6E39D-E452-47C3-B756-B8B6CE26C3E4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.AppHost", "samples\marketing-team\src\Marketing.AppHost\Marketing.AppHost.csproj", "{8E998B80-202E-4751-9FC4-0623C024AF25}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.Backend", "samples\marketing-team\src\Marketing.Backend\Marketing.Backend.csproj", "{BE82D56A-0281-4B01-9B99-D2F051585AF0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.ServiceDefaults", "samples\marketing-team\src\Marketing.ServiceDefaults\Marketing.ServiceDefaults.csproj", "{85A49174-5545-49D2-B5FD-BEDC9A401AB3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.Shared", "samples\marketing-team\src\Marketing.Shared\Marketing.Shared.csproj", "{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -78,18 +82,10 @@ Global
{A7677950-18F1-42FF-8018-870395417465}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7677950-18F1-42FF-8018-870395417465}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7677950-18F1-42FF-8018-870395417465}.Release|Any CPU.Build.0 = Release|Any CPU
{62F276F3-9184-4908-A7FB-065B4E491BE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62F276F3-9184-4908-A7FB-065B4E491BE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62F276F3-9184-4908-A7FB-065B4E491BE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62F276F3-9184-4908-A7FB-065B4E491BE2}.Release|Any CPU.Build.0 = Release|Any CPU
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Release|Any CPU.Build.0 = Release|Any CPU
{92CAAA29-8633-4984-B169-29BB89E0EA23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92CAAA29-8633-4984-B169-29BB89E0EA23}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92CAAA29-8633-4984-B169-29BB89E0EA23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92CAAA29-8633-4984-B169-29BB89E0EA23}.Release|Any CPU.Build.0 = Release|Any CPU
{20E5C8C3-CE40-4FC3-96F8-B4A2C51936E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20E5C8C3-CE40-4FC3-96F8-B4A2C51936E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20E5C8C3-CE40-4FC3-96F8-B4A2C51936E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -114,6 +110,30 @@ Global
{7BA721F2-EE46-4A85-A8C8-3695C4ADF93E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7BA721F2-EE46-4A85-A8C8-3695C4ADF93E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7BA721F2-EE46-4A85-A8C8-3695C4ADF93E}.Release|Any CPU.Build.0 = Release|Any CPU
{5504D650-D013-4F5E-9A42-EDC9B4A36E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5504D650-D013-4F5E-9A42-EDC9B4A36E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5504D650-D013-4F5E-9A42-EDC9B4A36E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5504D650-D013-4F5E-9A42-EDC9B4A36E1D}.Release|Any CPU.Build.0 = Release|Any CPU
{62D6E39D-E452-47C3-B756-B8B6CE26C3E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62D6E39D-E452-47C3-B756-B8B6CE26C3E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62D6E39D-E452-47C3-B756-B8B6CE26C3E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62D6E39D-E452-47C3-B756-B8B6CE26C3E4}.Release|Any CPU.Build.0 = Release|Any CPU
{8E998B80-202E-4751-9FC4-0623C024AF25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8E998B80-202E-4751-9FC4-0623C024AF25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E998B80-202E-4751-9FC4-0623C024AF25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E998B80-202E-4751-9FC4-0623C024AF25}.Release|Any CPU.Build.0 = Release|Any CPU
{BE82D56A-0281-4B01-9B99-D2F051585AF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE82D56A-0281-4B01-9B99-D2F051585AF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE82D56A-0281-4B01-9B99-D2F051585AF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE82D56A-0281-4B01-9B99-D2F051585AF0}.Release|Any CPU.Build.0 = Release|Any CPU
{85A49174-5545-49D2-B5FD-BEDC9A401AB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85A49174-5545-49D2-B5FD-BEDC9A401AB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85A49174-5545-49D2-B5FD-BEDC9A401AB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85A49174-5545-49D2-B5FD-BEDC9A401AB3}.Release|Any CPU.Build.0 = Release|Any CPU
{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -126,12 +146,7 @@ Global
{50809508-F830-4553-9C4E-C802E0A0F690} = {E0E93575-7187-4975-8D72-6F285CD01767}
{79981945-61F7-4E1A-8949-7808FD75471B} = {50809508-F830-4553-9C4E-C802E0A0F690}
{A7677950-18F1-42FF-8018-870395417465} = {50809508-F830-4553-9C4E-C802E0A0F690}
{1FF691E4-E27D-4A7E-861C-4D6291B6EE35} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
{4225F3BA-A39D-4680-945E-F2869E98AEA2} = {1FF691E4-E27D-4A7E-861C-4D6291B6EE35}
{62F276F3-9184-4908-A7FB-065B4E491BE2} = {4225F3BA-A39D-4680-945E-F2869E98AEA2}
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
{92CAAA29-8633-4984-B169-29BB89E0EA23} = {65CF8F20-D740-46AC-A869-FA609D960A09}
{65CF8F20-D740-46AC-A869-FA609D960A09} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
{20E5C8C3-CE40-4FC3-96F8-B4A2C51936E9} = {290F9824-BAD3-4703-B9B7-FE9C4BE3A1CF}
{B9188ADC-D322-4B38-B3D6-95338E89C34B} = {290F9824-BAD3-4703-B9B7-FE9C4BE3A1CF}
{320B05A6-4E1B-4B15-B3F6-745819D2BF22} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
@ -139,6 +154,13 @@ Global
{E45990FD-85B3-44A2-8646-4AB2E868BC5F} = {320B05A6-4E1B-4B15-B3F6-745819D2BF22}
{590BACCE-7310-4D7B-9618-46496F2EB171} = {320B05A6-4E1B-4B15-B3F6-745819D2BF22}
{7BA721F2-EE46-4A85-A8C8-3695C4ADF93E} = {320B05A6-4E1B-4B15-B3F6-745819D2BF22}
{DD53D185-5C0A-4B45-BF4D-F597F899E671} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
{5504D650-D013-4F5E-9A42-EDC9B4A36E1D} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
{62D6E39D-E452-47C3-B756-B8B6CE26C3E4} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
{8E998B80-202E-4751-9FC4-0623C024AF25} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
{BE82D56A-0281-4B01-9B99-D2F051585AF0} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
{85A49174-5545-49D2-B5FD-BEDC9A401AB3} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C9250809-2B94-4552-99DE-333E4646A16F}

View File

@ -2,76 +2,75 @@
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.0" />
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.1" />
<PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="8.0.1-preview.8.24267.1" />
<PackageVersion Include="Aspire.Hosting.AppHost" Version="8.0.1" />
<PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="8.0.1" />
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="8.0.1" />
<PackageVersion Include="Aspire.Hosting.NodeJs" Version="8.0.1" />
<PackageVersion Include="Aspire.Hosting.Orleans" Version="8.0.1" />
<PackageVersion Include="Aspire.Hosting.Qdrant" Version="8.0.1" />
<PackageVersion Include="Aspire.Hosting.Redis" Version="8.0.1" />
<PackageVersion Include="Azure.Data.Tables" Version="12.8.1" />
<PackageVersion Include="Aspire.Hosting.AppHost" Version="8.0.2" />
<PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="8.0.2" />
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="8.0.2" />
<PackageVersion Include="Aspire.Hosting.NodeJs" Version="8.0.2" />
<PackageVersion Include="Aspire.Hosting.Orleans" Version="8.0.2" />
<PackageVersion Include="Aspire.Hosting.Qdrant" Version="8.0.2" />
<PackageVersion Include="Aspire.Hosting.Redis" Version="8.0.2" />
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
<PackageVersion Include="Azure.Data.Tables" Version="12.8.3" />
<PackageVersion Include="Azure.Identity" Version="1.12.0" />
<PackageVersion Include="Azure.ResourceManager.ContainerInstance" Version="1.2.0" />
<PackageVersion Include="Azure.ResourceManager.ContainerInstance" Version="1.2.1" />
<PackageVersion Include="Azure.Storage.Files.Shares" Version="12.18.0" />
<PackageVersion Include="CloudNative.CloudEvents.SystemTextJson" Version="2.7.1" />
<PackageVersion Include="Dapr.Actors" Version="1.13.0" />
<PackageVersion Include="Dapr.Actors.AspNetCore" Version="1.13.0" />
<PackageVersion Include="Dapr.AspNetCore" Version="1.13.0" />
<PackageVersion Include="Dapr.Client" Version="1.13.0" />
<PackageVersion Include="Elsa" Version="3.0.6" />
<PackageVersion Include="Elsa.EntityFrameworkCore" Version="3.0.6" />
<PackageVersion Include="Elsa.EntityFrameworkCore.Sqlite" Version="3.0.6" />
<PackageVersion Include="Elsa.Http" Version="3.0.6" />
<PackageVersion Include="Elsa.Identity" Version="3.0.6" />
<PackageVersion Include="Elsa.Workflows.Api" Version="3.0.6" />
<PackageVersion Include="Elsa.Workflows.Core" Version="3.0.6" />
<PackageVersion Include="Dapr.Actors" Version="1.13.1" />
<PackageVersion Include="Dapr.Actors.AspNetCore" Version="1.13.1" />
<PackageVersion Include="Dapr.AspNetCore" Version="1.13.1" />
<PackageVersion Include="Dapr.Client" Version="1.13.1" />
<PackageVersion Include="Elsa" Version="3.1.3" />
<PackageVersion Include="Elsa.EntityFrameworkCore" Version="3.1.3" />
<PackageVersion Include="Elsa.EntityFrameworkCore.Sqlite" Version="3.1.3" />
<PackageVersion Include="Elsa.Http" Version="3.1.3" />
<PackageVersion Include="Elsa.Identity" Version="3.1.3" />
<PackageVersion Include="Elsa.Workflows.Api" Version="3.1.3" />
<PackageVersion Include="Elsa.Workflows.Core" Version="3.1.3" />
<PackageVersion Include="Elsa.Workflows.Designer" Version="3.0.0-preview.727" />
<PackageVersion Include="Elsa.Workflows.Management" Version="3.0.6" />
<PackageVersion Include="Elsa.Workflows.Management" Version="3.1.3" />
<PackageVersion Include="Grpc.AspNetCore" Version="2.63.0" />
<PackageVersion Include="Grpc.Net.ClientFactory" Version="2.63.0" />
<PackageVersion Include="Grpc.Tools" Version="2.63.0" />
<PackageVersion Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.7.2" />
<PackageVersion Include="Grpc.Tools" Version="2.64.0" />
<PackageVersion Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.7.4" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.6.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.7.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.1" />
<PackageVersion Include="Microsoft.Orleans.Clustering.Cosmos" Version="8.1.0" />
<PackageVersion Include="Microsoft.Orleans.Persistence.Cosmos" Version="8.1.0" />
<PackageVersion Include="Microsoft.Orleans.Reminders" Version="8.1.0" />
<PackageVersion Include="Microsoft.Orleans.Reminders.Cosmos" Version="8.1.0" />
<PackageVersion Include="Microsoft.Orleans.Runtime" Version="8.1.0" />
<PackageVersion Include="Microsoft.Orleans.Sdk" Version="8.1.0" />
<PackageVersion Include="Microsoft.Orleans.Serialization.Protobuf" Version="8.1.0" />
<PackageVersion Include="Microsoft.Orleans.Server" Version="8.1.0" />
<PackageVersion Include="Microsoft.Orleans.Streaming" Version="8.1.0" />
<PackageVersion Include="Microsoft.Orleans.Streaming.EventHubs" Version="8.1.0" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="1.10.0-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Plugins.Memory" Version="1.10.0-alpha" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.2" />
<PackageVersion Include="Microsoft.Orleans.Clustering.Cosmos" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Persistence.Cosmos" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Reminders" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Reminders.Cosmos" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Runtime" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Sdk" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Serialization.Protobuf" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Server" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Streaming" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Streaming.EventHubs" Version="8.2.0" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.1" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="1.15.1-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Plugins.Memory" Version="1.15.1-alpha" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Octokit" Version="10.0.0" />
<PackageVersion Include="Octokit.Webhooks.AspNetCore" Version="2.0.3" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.8.0" />
<PackageVersion Include="OrleansDashboard" Version="8.0.0" />
<PackageVersion Include="Octokit" Version="13.0.1" />
<PackageVersion Include="Octokit.Webhooks.AspNetCore" Version="2.2.2" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
<PackageVersion Include="OrleansDashboard" Version="8.2.0" />
<PackageVersion Include="PdfPig" Version="0.1.9-alpha-20240324-e7896" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.4.1" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.6.3" />
</ItemGroup>
</Project>

View File

@ -1,11 +1,8 @@
using Microsoft.AI.Agents.Worker;
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.Services.AddProblemDetails();
builder.Services.AddGrpc();
builder.Logging.SetMinimumLevel(LogLevel.Information);
builder.AddServiceDefaults();
builder.AddAgentService();
var app = builder.Build();

View File

@ -4,9 +4,12 @@ using AgentId = Microsoft.AI.Agents.Worker.Client.AgentId;
namespace Greeter.AgentWorker;
internal sealed class Client(ILogger<Client> logger, AgentWorkerRuntime runtime) : AgentBase(new ClientContext(logger, runtime))
public sealed class AgentClient(ILogger<AgentClient> logger, AgentWorkerRuntime runtime) : AgentBase(new ClientContext(logger, runtime))
{
private sealed class ClientContext(ILogger<Client> logger, AgentWorkerRuntime runtime) : IAgentContext
public async ValueTask PublishEventAsync(Event @event) => await PublishEvent(@event);
public async ValueTask<RpcResponse> SendRequestAsync(AgentId target, string method, Dictionary<string, string> parameters) => await RequestAsync(target, method, parameters);
private sealed class ClientContext(ILogger<AgentClient> logger, AgentWorkerRuntime runtime) : IAgentContext
{
public AgentId AgentId { get; } = new AgentId("client", Guid.NewGuid().ToString());
public AgentBase? AgentInstance { get; set; }

View File

@ -11,7 +11,7 @@ builder.AddServiceDefaults();
var agentBuilder = builder.AddAgentWorker("https://agenthost");
agentBuilder.AddAgent<GreetingAgent>("greeter");
builder.Services.AddHostedService<MyBackgroundService>();
builder.Services.AddSingleton<Client>();
builder.Services.AddSingleton<AgentClient>();
var app = builder.Build();
@ -21,7 +21,7 @@ app.Run();
internal sealed class GreetingAgent(IAgentContext context, ILogger<GreetingAgent> logger) : AgentBase(context)
{
protected override Task HandleEvent(Microsoft.AI.Agents.Abstractions.Event @event)
protected override Task HandleEvent(Event @event)
{
logger.LogInformation("[{Id}] Received event: '{Event}'.", AgentId, @event);
return base.HandleEvent(@event);
@ -34,7 +34,7 @@ internal sealed class GreetingAgent(IAgentContext context, ILogger<GreetingAgent
}
}
internal sealed class MyBackgroundService(ILogger<MyBackgroundService> logger, Client client) : BackgroundService
internal sealed class MyBackgroundService(ILogger<MyBackgroundService> logger, AgentClient client) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
@ -44,7 +44,7 @@ internal sealed class MyBackgroundService(ILogger<MyBackgroundService> logger, C
{
var generatedCodeId = Guid.NewGuid().ToString();
var instanceId = Guid.NewGuid().ToString();
var response = await client.RequestAsync(
var response = await client.SendRequestAsync(
new AgentId("greeter", "foo"),
"echo",
new Dictionary<string, string> { ["message"] = "Hello, agents!" }).ConfigureAwait(false);

View File

@ -2,7 +2,6 @@ using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Orleans.Runtime;
namespace Microsoft.AI.DevTeam;

View File

@ -3,7 +3,6 @@ using Microsoft.AI.Agents.Orleans;
using Microsoft.AI.DevTeam.Events;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Orleans.Runtime;
namespace Microsoft.AI.DevTeam;

View File

@ -4,7 +4,6 @@ using Microsoft.AI.DevTeam.Events;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Memory;
using Orleans.Runtime;
namespace Microsoft.AI.DevTeam;
[ImplicitStreamSubscription(Consts.MainNamespace)]

View File

@ -3,7 +3,6 @@ using Microsoft.AI.Agents.Orleans;
using Microsoft.AI.DevTeam.Events;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Orleans.Runtime;
namespace Microsoft.AI.DevTeam;

View File

@ -1,7 +1,6 @@
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.AI.DevTeam.Events;
using Orleans.Runtime;
using Orleans.Timers;
namespace Microsoft.AI.DevTeam;

View File

@ -6,9 +6,9 @@ using Octokit.Webhooks.Events;
using Octokit.Webhooks.Events.IssueComment;
using Octokit.Webhooks.Events.Issues;
using Octokit.Webhooks.Models;
using Orleans.Runtime;
namespace Microsoft.AI.DevTeam;
public sealed class GithubWebHookProcessor : WebhookEventProcessor
{
private readonly ILogger<GithubWebHookProcessor> _logger;

View File

@ -1 +0,0 @@
.azure

View File

@ -1,40 +0,0 @@
# [In progress] Marketing Saple application
This is a demo application that showcase the different features of the AI Agent framework.
There are five agents in this application that control the different areas of the UI autonomously.
The agents are designed to be able to interact with each other and the user to achieve their goals.
To do that each agent has
![Agents](readme-media/screenshot.png)
![Agents](readme-media/agents.png)
## Requirements to run locally
### Frontend
The latest version of Node.js and npm
### Backend
Visual Studio or Visual Studio code and the latest version of dotnet
## Running with azd
To run the application with azd, you need to have the azd cli installed. You can install it following the instructions here:
https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows%2Cbrew-mac%2Cscript-linux
Then you can run the following command to start the application:
```powers
## How to run the application locally
Execute Run.ps1. IF you are missing the config file the script will create an empty one for you and ask you to fill it out.
```
.\run.ps1
```
## How to debug the application locally
To debug the backend, you can simply open the solution in Visual Studio, and press F5 to start debugging.
Remember to copy `appsettings.local.template.json` to `appsettings.json` and fill out the values.</p>
The frontend is a NodeJS React application. You can debug it using Visual Studio code.

View File

@ -1,20 +0,0 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
name: marketing
metadata:
template: azd-init@1.8.2
services:
backend:
project: "src/backend"
host: containerapp
language: dotnet
docker:
path: Dockerfile
context: ../../../../
frontend:
project: "src/frontend"
host: containerapp
language: ts
dist: build
docker:
path: Dockerfile

View File

@ -1,135 +0,0 @@
{
"analysisServicesServers": "as",
"apiManagementService": "apim-",
"appConfigurationStores": "appcs-",
"appManagedEnvironments": "cae-",
"appContainerApps": "ca-",
"authorizationPolicyDefinitions": "policy-",
"automationAutomationAccounts": "aa-",
"blueprintBlueprints": "bp-",
"blueprintBlueprintsArtifacts": "bpa-",
"cacheRedis": "redis-",
"cdnProfiles": "cdnp-",
"cdnProfilesEndpoints": "cdne-",
"cognitiveServicesAccounts": "cog-",
"cognitiveServicesFormRecognizer": "cog-fr-",
"cognitiveServicesTextAnalytics": "cog-ta-",
"computeAvailabilitySets": "avail-",
"computeCloudServices": "cld-",
"computeDiskEncryptionSets": "des",
"computeDisks": "disk",
"computeDisksOs": "osdisk",
"computeGalleries": "gal",
"computeSnapshots": "snap-",
"computeVirtualMachines": "vm",
"computeVirtualMachineScaleSets": "vmss-",
"containerInstanceContainerGroups": "ci",
"containerRegistryRegistries": "cr",
"containerServiceManagedClusters": "aks-",
"databricksWorkspaces": "dbw-",
"dataFactoryFactories": "adf-",
"dataLakeAnalyticsAccounts": "dla",
"dataLakeStoreAccounts": "dls",
"dataMigrationServices": "dms-",
"dBforMySQLServers": "mysql-",
"dBforPostgreSQLServers": "psql-",
"devicesIotHubs": "iot-",
"devicesProvisioningServices": "provs-",
"devicesProvisioningServicesCertificates": "pcert-",
"documentDBDatabaseAccounts": "cosmos-",
"eventGridDomains": "evgd-",
"eventGridDomainsTopics": "evgt-",
"eventGridEventSubscriptions": "evgs-",
"eventHubNamespaces": "evhns-",
"eventHubNamespacesEventHubs": "evh-",
"hdInsightClustersHadoop": "hadoop-",
"hdInsightClustersHbase": "hbase-",
"hdInsightClustersKafka": "kafka-",
"hdInsightClustersMl": "mls-",
"hdInsightClustersSpark": "spark-",
"hdInsightClustersStorm": "storm-",
"hybridComputeMachines": "arcs-",
"insightsActionGroups": "ag-",
"insightsComponents": "appi-",
"keyVaultVaults": "kv-",
"kubernetesConnectedClusters": "arck",
"kustoClusters": "dec",
"kustoClustersDatabases": "dedb",
"logicIntegrationAccounts": "ia-",
"logicWorkflows": "logic-",
"machineLearningServicesWorkspaces": "mlw-",
"managedIdentityUserAssignedIdentities": "id-",
"managementManagementGroups": "mg-",
"migrateAssessmentProjects": "migr-",
"networkApplicationGateways": "agw-",
"networkApplicationSecurityGroups": "asg-",
"networkAzureFirewalls": "afw-",
"networkBastionHosts": "bas-",
"networkConnections": "con-",
"networkDnsZones": "dnsz-",
"networkExpressRouteCircuits": "erc-",
"networkFirewallPolicies": "afwp-",
"networkFirewallPoliciesWebApplication": "waf",
"networkFirewallPoliciesRuleGroups": "wafrg",
"networkFrontDoors": "fd-",
"networkFrontdoorWebApplicationFirewallPolicies": "fdfp-",
"networkLoadBalancersExternal": "lbe-",
"networkLoadBalancersInternal": "lbi-",
"networkLoadBalancersInboundNatRules": "rule-",
"networkLocalNetworkGateways": "lgw-",
"networkNatGateways": "ng-",
"networkNetworkInterfaces": "nic-",
"networkNetworkSecurityGroups": "nsg-",
"networkNetworkSecurityGroupsSecurityRules": "nsgsr-",
"networkNetworkWatchers": "nw-",
"networkPrivateDnsZones": "pdnsz-",
"networkPrivateLinkServices": "pl-",
"networkPublicIPAddresses": "pip-",
"networkPublicIPPrefixes": "ippre-",
"networkRouteFilters": "rf-",
"networkRouteTables": "rt-",
"networkRouteTablesRoutes": "udr-",
"networkTrafficManagerProfiles": "traf-",
"networkVirtualNetworkGateways": "vgw-",
"networkVirtualNetworks": "vnet-",
"networkVirtualNetworksSubnets": "snet-",
"networkVirtualNetworksVirtualNetworkPeerings": "peer-",
"networkVirtualWans": "vwan-",
"networkVpnGateways": "vpng-",
"networkVpnGatewaysVpnConnections": "vcn-",
"networkVpnGatewaysVpnSites": "vst-",
"notificationHubsNamespaces": "ntfns-",
"notificationHubsNamespacesNotificationHubs": "ntf-",
"operationalInsightsWorkspaces": "log-",
"portalDashboards": "dash-",
"powerBIDedicatedCapacities": "pbi-",
"purviewAccounts": "pview-",
"recoveryServicesVaults": "rsv-",
"resourcesResourceGroups": "rg-",
"searchSearchServices": "srch-",
"serviceBusNamespaces": "sb-",
"serviceBusNamespacesQueues": "sbq-",
"serviceBusNamespacesTopics": "sbt-",
"serviceEndPointPolicies": "se-",
"serviceFabricClusters": "sf-",
"signalRServiceSignalR": "sigr",
"sqlManagedInstances": "sqlmi-",
"sqlServers": "sql-",
"sqlServersDataWarehouse": "sqldw-",
"sqlServersDatabases": "sqldb-",
"sqlServersDatabasesStretch": "sqlstrdb-",
"storageStorageAccounts": "st",
"storageStorageAccountsVm": "stvm",
"storSimpleManagers": "ssimp",
"streamAnalyticsCluster": "asa-",
"synapseWorkspaces": "syn",
"synapseWorkspacesAnalyticsWorkspaces": "synw",
"synapseWorkspacesSqlPoolsDedicated": "syndp",
"synapseWorkspacesSqlPoolsSpark": "synsp",
"timeSeriesInsightsEnvironments": "tsi-",
"webServerFarms": "plan-",
"webSitesAppService": "app-",
"webSitesAppServiceEnvironment": "ase-",
"webSitesFunctions": "func-",
"webStaticSites": "stapp-"
}

View File

@ -1,133 +0,0 @@
param name string
param location string = resourceGroup().location
param tags object = {}
param identityName string
param containerRegistryName string
param containerAppsEnvironmentName string
param applicationInsightsName string
param allowedOrigins array
param exists bool
@secure()
param appDefinition object
var appSettingsArray = filter(array(appDefinition.settings), i => i.name != '')
var secrets = map(filter(appSettingsArray, i => i.?secret != null), i => {
name: i.name
value: i.value
secretRef: i.?secretRef ?? take(replace(replace(toLower(i.name), '_', '-'), '.', '-'), 32)
})
var env = map(filter(appSettingsArray, i => i.?secret == null), i => {
name: i.name
value: i.value
})
resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: identityName
location: location
}
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' existing = {
name: containerRegistryName
}
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' existing = {
name: containerAppsEnvironmentName
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
name: applicationInsightsName
}
resource acrPullRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: containerRegistry
name: guid(subscription().id, resourceGroup().id, identity.id, 'acrPullRole')
properties: {
roleDefinitionId: subscriptionResourceId(
'Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
principalType: 'ServicePrincipal'
principalId: identity.properties.principalId
}
}
module fetchLatestImage '../modules/fetch-container-image.bicep' = {
name: '${name}-fetch-image'
params: {
exists: exists
name: name
}
}
resource app 'Microsoft.App/containerApps@2023-05-02-preview' = {
name: name
location: location
tags: union(tags, {'azd-service-name': 'backend' })
dependsOn: [ acrPullRole ]
identity: {
type: 'UserAssigned'
userAssignedIdentities: { '${identity.id}': {} }
}
properties: {
managedEnvironmentId: containerAppsEnvironment.id
configuration: {
ingress: {
external: true
targetPort: 5244
transport: 'auto'
corsPolicy: {
allowedOrigins: union(allowedOrigins, [
// define additional allowed origins here
])
}
}
registries: [
{
server: '${containerRegistryName}.azurecr.io'
identity: identity.id
}
]
secrets: union([
],
map(secrets, secret => {
name: secret.secretRef
value: secret.value
}))
}
template: {
containers: [
{
image: fetchLatestImage.outputs.?containers[?0].?image ?? 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
name: 'main'
env: union([
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: applicationInsights.properties.ConnectionString
}
{
name: 'PORT'
value: '5244'
}
],
env,
map(secrets, secret => {
name: secret.name
secretRef: secret.secretRef
}))
resources: {
cpu: json('1.0')
memory: '2.0Gi'
}
}
]
scale: {
minReplicas: 1
maxReplicas: 10
}
}
}
}
output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
output name string = app.name
output uri string = 'https://${app.properties.configuration.ingress.fqdn}'
output id string = app.id

View File

@ -1,132 +0,0 @@
param name string
param location string = resourceGroup().location
param tags object = {}
param identityName string
param containerRegistryName string
param containerAppsEnvironmentName string
param applicationInsightsName string
param apiUrls array
param exists bool
@secure()
param appDefinition object
var appSettingsArray = filter(array(appDefinition.settings), i => i.name != '')
var secrets = map(filter(appSettingsArray, i => i.?secret != null), i => {
name: i.name
value: i.value
secretRef: i.?secretRef ?? take(replace(replace(toLower(i.name), '_', '-'), '.', '-'), 32)
})
var env = map(filter(appSettingsArray, i => i.?secret == null), i => {
name: i.name
value: i.value
})
resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: identityName
location: location
}
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' existing = {
name: containerRegistryName
}
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' existing = {
name: containerAppsEnvironmentName
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
name: applicationInsightsName
}
resource acrPullRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: containerRegistry
name: guid(subscription().id, resourceGroup().id, identity.id, 'acrPullRole')
properties: {
roleDefinitionId: subscriptionResourceId(
'Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
principalType: 'ServicePrincipal'
principalId: identity.properties.principalId
}
}
module fetchLatestImage '../modules/fetch-container-image.bicep' = {
name: '${name}-fetch-image'
params: {
exists: exists
name: name
}
}
resource app 'Microsoft.App/containerApps@2023-05-02-preview' = {
name: name
location: location
tags: union(tags, {'azd-service-name': 'frontend' })
dependsOn: [ acrPullRole ]
identity: {
type: 'UserAssigned'
userAssignedIdentities: { '${identity.id}': {} }
}
properties: {
managedEnvironmentId: containerAppsEnvironment.id
configuration: {
ingress: {
external: true
targetPort: 3000
transport: 'auto'
}
registries: [
{
server: '${containerRegistryName}.azurecr.io'
identity: identity.id
}
]
secrets: union([
],
map(secrets, secret => {
name: secret.secretRef
value: secret.value
}))
}
template: {
containers: [
{
image: fetchLatestImage.outputs.?containers[?0].?image ?? 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
name: 'main'
env: union([
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: applicationInsights.properties.ConnectionString
}
{
name: 'BACKEND_BASE_URL'
value: apiUrls[0]
}
{
name: 'PORT'
value: '3000'
}
],
env,
map(secrets, secret => {
name: secret.name
secretRef: secret.secretRef
}))
resources: {
cpu: json('1.0')
memory: '2.0Gi'
}
}
]
scale: {
minReplicas: 1
maxReplicas: 10
}
}
}
}
output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
output name string = app.name
output uri string = 'https://${app.properties.configuration.ingress.fqdn}'
output id string = app.id

View File

@ -1,135 +0,0 @@
targetScope = 'subscription'
@minLength(1)
@maxLength(64)
@description('Name of the environment that can be used as part of naming resource convention')
param environmentName string
@minLength(1)
@description('Primary location for all resources')
param location string
param backendExists bool
@secure()
param backendDefinition object
param frontendExists bool
@secure()
param frontendDefinition object
@description('Id of the user or app to assign application roles')
param principalId string
// Tags that should be applied to all resources.
//
// Note that 'azd-service-name' tags should be applied separately to service host resources.
// Example usage:
// tags: union(tags, { 'azd-service-name': <service name in azure.yaml> })
var tags = {
'azd-env-name': environmentName
}
var abbrs = loadJsonContent('./abbreviations.json')
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' = {
name: 'rg-${environmentName}'
location: location
tags: tags
}
module monitoring './shared/monitoring.bicep' = {
name: 'monitoring'
params: {
location: location
tags: tags
logAnalyticsName: '${abbrs.operationalInsightsWorkspaces}${resourceToken}'
applicationInsightsName: '${abbrs.insightsComponents}${resourceToken}'
}
scope: rg
}
module dashboard './shared/dashboard-web.bicep' = {
name: 'dashboard'
params: {
name: '${abbrs.portalDashboards}${resourceToken}'
applicationInsightsName: monitoring.outputs.applicationInsightsName
location: location
tags: tags
}
scope: rg
}
module registry './shared/registry.bicep' = {
name: 'registry'
params: {
location: location
tags: tags
name: '${abbrs.containerRegistryRegistries}${resourceToken}'
}
scope: rg
}
module keyVault './shared/keyvault.bicep' = {
name: 'keyvault'
params: {
location: location
tags: tags
name: '${abbrs.keyVaultVaults}${resourceToken}'
principalId: principalId
}
scope: rg
}
module appsEnv './shared/apps-env.bicep' = {
name: 'apps-env'
params: {
name: '${abbrs.appManagedEnvironments}${resourceToken}'
location: location
tags: tags
applicationInsightsName: monitoring.outputs.applicationInsightsName
logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName
}
scope: rg
}
module backend './app/backend.bicep' = {
name: 'backend'
params: {
name: '${abbrs.appContainerApps}backend-${resourceToken}'
location: location
tags: tags
identityName: '${abbrs.managedIdentityUserAssignedIdentities}backend-${resourceToken}'
applicationInsightsName: monitoring.outputs.applicationInsightsName
containerAppsEnvironmentName: appsEnv.outputs.name
containerRegistryName: registry.outputs.name
exists: backendExists
appDefinition: backendDefinition
allowedOrigins: [
'https://${abbrs.appContainerApps}frontend-${resourceToken}.${appsEnv.outputs.domain}'
]
}
scope: rg
}
module frontend './app/frontend.bicep' = {
name: 'frontend'
params: {
name: '${abbrs.appContainerApps}frontend-${resourceToken}'
location: location
tags: tags
identityName: '${abbrs.managedIdentityUserAssignedIdentities}frontend-${resourceToken}'
applicationInsightsName: monitoring.outputs.applicationInsightsName
containerAppsEnvironmentName: appsEnv.outputs.name
containerRegistryName: registry.outputs.name
exists: frontendExists
appDefinition: frontendDefinition
apiUrls: [
backend.outputs.uri
]
}
scope: rg
}
output AZURE_CONTAINER_REGISTRY_ENDPOINT string = registry.outputs.loginServer
output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name
output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint

View File

@ -1,59 +0,0 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": {
"value": "${AZURE_ENV_NAME}"
},
"location": {
"value": "${AZURE_LOCATION}"
},
"backendExists": {
"value": "${SERVICE_BACKEND_RESOURCE_EXISTS=false}"
},
"backendDefinition": {
"value": {
"settings": [
{
"name": "",
"value": "${VAR}",
"_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
"_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR} to use the value of 'VAR' from the current environment."
},
{
"name": "",
"value": "${VAR_S}",
"secret": true,
"_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
"_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR_S} to use the value of 'VAR_S' from the current environment."
}
]
}
},
"frontendExists": {
"value": "${SERVICE_FRONTEND_RESOURCE_EXISTS=false}"
},
"frontendDefinition": {
"value": {
"settings": [
{
"name": "",
"value": "${VAR}",
"_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
"_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR} to use the value of 'VAR' from the current environment."
},
{
"name": "",
"value": "${VAR_S}",
"secret": true,
"_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
"_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR_S} to use the value of 'VAR_S' from the current environment."
}
]
}
},
"principalId": {
"value": "${AZURE_PRINCIPAL_ID}"
}
}
}

View File

@ -1,8 +0,0 @@
param exists bool
param name string
resource existingApp 'Microsoft.App/containerApps@2023-05-02-preview' existing = if (exists) {
name: name
}
output containers array = exists ? existingApp.properties.template.containers : []

View File

@ -1,33 +0,0 @@
param name string
param location string = resourceGroup().location
param tags object = {}
param logAnalyticsWorkspaceName string
param applicationInsightsName string = ''
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-10-01' = {
name: name
location: location
tags: tags
properties: {
appLogsConfiguration: {
destination: 'log-analytics'
logAnalyticsConfiguration: {
customerId: logAnalyticsWorkspace.properties.customerId
sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
}
}
daprAIConnectionString: applicationInsights.properties.ConnectionString
}
}
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
name: logAnalyticsWorkspaceName
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
name: applicationInsightsName
}
output name string = containerAppsEnvironment.name
output domain string = containerAppsEnvironment.properties.defaultDomain

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +0,0 @@
param name string
param location string = resourceGroup().location
param tags object = {}
@description('Service principal that should be granted read access to the KeyVault. If unset, no service principal is granted access by default')
param principalId string = ''
var defaultAccessPolicies = !empty(principalId) ? [
{
objectId: principalId
permissions: { secrets: [ 'get', 'list' ] }
tenantId: subscription().tenantId
}
] : []
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
name: name
location: location
tags: tags
properties: {
tenantId: subscription().tenantId
sku: { family: 'A', name: 'standard' }
enabledForTemplateDeployment: true
accessPolicies: union(defaultAccessPolicies, [
// define access policies here
])
}
}
output endpoint string = keyVault.properties.vaultUri
output name string = keyVault.name

View File

@ -1,34 +0,0 @@
param logAnalyticsName string
param applicationInsightsName string
param location string = resourceGroup().location
param tags object = {}
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = {
name: logAnalyticsName
location: location
tags: tags
properties: any({
retentionInDays: 30
features: {
searchVersion: 1
}
sku: {
name: 'PerGB2018'
}
})
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
name: applicationInsightsName
location: location
tags: tags
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalytics.id
}
}
output applicationInsightsName string = applicationInsights.name
output logAnalyticsWorkspaceId string = logAnalytics.id
output logAnalyticsWorkspaceName string = logAnalytics.name

View File

@ -1,36 +0,0 @@
param name string
param location string = resourceGroup().location
param tags object = {}
param adminUserEnabled bool = true
param anonymousPullEnabled bool = false
param dataEndpointEnabled bool = false
param encryption object = {
status: 'disabled'
}
param networkRuleBypassOptions string = 'AzureServices'
param publicNetworkAccess string = 'Enabled'
param sku object = {
name: 'Standard'
}
param zoneRedundancy string = 'Disabled'
// 2023-01-01-preview needed for anonymousPullEnabled
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = {
name: name
location: location
tags: tags
sku: sku
properties: {
adminUserEnabled: adminUserEnabled
anonymousPullEnabled: anonymousPullEnabled
dataEndpointEnabled: dataEndpointEnabled
encryption: encryption
networkRuleBypassOptions: networkRuleBypassOptions
publicNetworkAccess: publicNetworkAccess
zoneRedundancy: zoneRedundancy
}
}
output loginServer string = containerRegistry.properties.loginServer
output name string = containerRegistry.name

View File

@ -1,96 +0,0 @@
# Next Steps after `azd init`
## Table of Contents
1. [Next Steps](#next-steps)
2. [What was added](#what-was-added)
3. [Billing](#billing)
4. [Troubleshooting](#troubleshooting)
## Next Steps
### Define environment variables for running services
1. Modify or add environment variables to configure the running application. Environment variables can be configured by updating the `settings` node(s) for each service in [main.parameters.json](./infra/main.parameters.json).
2. For services using a database, environment variables have been pre-configured under the `env` node in the following files to allow connection to the database. Modify the name of these variables as needed to match your application.
- [app/backend.bicep](./infra/app/backend.bicep)
- [app/frontend.bicep](./infra/app/frontend.bicep)
3. For services using Redis, environment variables will not show up under `env` explicitly, but are available as: `REDIS_ENDPOINT`, `REDIS_HOST`, `REDIS_PASSWORD`, and `REDIS_PORT`.
### Provision infrastructure and deploy application code
Run `azd up` to provision your infrastructure and deploy to Azure in one step (or run `azd provision` then `azd deploy` to accomplish the tasks separately). Visit the service endpoints listed to see your application up-and-running!
To troubleshoot any issues, see [troubleshooting](#troubleshooting).
### Configure CI/CD pipeline
1. Create a workflow pipeline file locally. The following starters are available:
- [Deploy with GitHub Actions](https://github.com/Azure-Samples/azd-starter-bicep/blob/main/.github/workflows/azure-dev.yml)
- [Deploy with Azure Pipelines](https://github.com/Azure-Samples/azd-starter-bicep/blob/main/.azdo/pipelines/azure-dev.yml)
2. Run `azd pipeline config -e <environment name>` to configure the deployment pipeline to connect securely to Azure. An environment name is specified here to configure the pipeline with a different environment for isolation purposes. Run `azd env list` and `azd env set` to reselect the default environment after this step.
## What was added
### Infrastructure configuration
To describe the infrastructure and application, `azure.yaml` along with Infrastructure as Code files using Bicep were added with the following directory structure:
```yaml
- azure.yaml # azd project configuration
- infra/ # Infrastructure as Code (bicep) files
- main.bicep # main deployment module
- app/ # Application resource modules
- shared/ # Shared resource modules
- modules/ # Library modules
```
Each bicep file declares resources to be provisioned. The resources are provisioned when running `azd up` or `azd provision`.
- [app/backend.bicep](./infra/app/backend.bicep) - Azure Container Apps resources to host the 'backend' service.
- [app/frontend.bicep](./infra/app/frontend.bicep) - Azure Container Apps resources to host the 'frontend' service.
- [shared/keyvault.bicep](./infra/shared/keyvault.bicep) - Azure KeyVault to store secrets.
- [shared/monitoring.bicep](./infra/shared/monitoring.bicep) - Azure Log Analytics workspace and Application Insights to log and store instrumentation logs.
- [shared/registry.bicep](./infra/shared/registry.bicep) - Azure Container Registry to store docker images.
More information about [Bicep](https://aka.ms/bicep) language.
### Build from source (no Dockerfile)
#### Build with Buildpacks using Oryx
If your project does not contain a Dockerfile, we will use [Buildpacks](https://buildpacks.io/) using [Oryx](https://github.com/microsoft/Oryx/blob/main/doc/README.md) to create an image for the services in `azure.yaml` and get your containerized app onto Azure.
To produce and run the docker image locally:
1. Run `azd package` to build the image.
2. Copy the *Image Tag* shown.
3. Run `docker run -it <Image Tag>` to run the image locally.
#### Exposed port
Oryx will automatically set `PORT` to a default value of `80`. Additionally, it will auto-configure supported web servers such as `gunicorn` and `ASP .NET Core` to listen to the target `PORT`. If your application already listens to the port specified by the `PORT` variable, the application will work out-of-the-box. Otherwise, you may need to perform one of the steps below:
1. Update your application code or configuration to listen to the port specified by the `PORT` variable
1. (Alternatively) Search for `targetPort` in a .bicep file under the `infra/app` folder, and update the variable to match the port used by the application.
## Billing
Visit the *Cost Management + Billing* page in Azure Portal to track current spend. For more information about how you're billed, and how you can monitor the costs incurred in your Azure subscriptions, visit [billing overview](https://learn.microsoft.com/azure/developer/intro/azure-developer-billing).
## Troubleshooting
Q: I visited the service endpoint listed, and I'm seeing a blank or error page.
A: Your service may have failed to start or misconfigured. To investigate further:
1. Click on the resource group link shown to visit Azure Portal.
2. Navigate to the specific Azure Container App resource for the service.
3. Select *Monitoring -> Log stream* under the navigation pane.
4. Observe the log output to identify any errors.
5. If there are no errors, ensure that the ingress target port matches the port that your service listens on:
1. Under *Settings -> Ingress*, ensure the *Target port* matches the desired port.
2. After modifying this setting, also update the `targetPort` setting in the .bicep file for the service under `infra/app`.
6. If logs are written to disk, examine the local logs or debug the application by using the *Console* to connect to a shell within the running container.
For additional information about setting up your `azd` project, visit our official [docs](https://learn.microsoft.com/azure/developer/azure-developer-cli/make-azd-compatible?pivots=azd-convert).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 905 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -1,13 +0,0 @@
if(Test-Path src/backend/appsettings.json) {
Write-Verbose "appsettings.json already exists" -Verbose
} else {
Copy-Item src/backend/appsettings.local.template.json src/backend/appsettings.json
Write-Verbose "appsettings.json created" -Verbose
}
if((Get-Content .\src\backend\appsettings.local.template.json -Raw | Select-String "<mandatory>") -ne $null) {
Write-Error "Please update the appsettings.json file with the correct values" -ErrorAction Stop
}
$backendProc = Start-Process powershell -ArgumentList '-NoExit', '-Command cd src/backend/; dotnet run'
$frontendProc = Start-Process powershell -ArgumentList '-NoExit', '-Command cd src/frontend/; npm run dev'

View File

@ -1,16 +0,0 @@
#!/bin/bash
if [ -f src/backend/appsettings.json ]; then
echo "appsettings.json already exists"
else
cp src/backend/appsettings.local.template.json src/backend/appsettings.json
echo "appsettings.json created"
fi
if grep -q "<mandatory>" src/backend/appsettings.local.template.json; then
echo "Please update the appsettings.json file with the correct values" >&2
exit 1
fi
gnome-terminal -- bash -c "cd src/backend/ && dotnet run; exec bash"
gnome-terminal -- bash -c "cd src/frontend/ && npm run dev; exec bash"

View File

@ -1,485 +0,0 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from `dotnet new gitignore`
# dotenv files
.env
appsettings.json
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
.idea
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# Vim temporary swap files
*.swp

View File

@ -1,79 +0,0 @@
using Marketing.Events;
using Marketing.Options;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Orleans.Runtime;
namespace Marketing.Agents;
[ImplicitStreamSubscription(Consts.OrleansNamespace)]
public class CommunityManager : AiAgent<CommunityManagerState>
{
protected override string Namespace => Consts.OrleansNamespace;
private readonly ILogger<GraphicDesigner> _logger;
public CommunityManager([PersistentState("state", "messages")] IPersistentState<AgentState<CommunityManagerState>> state, Kernel kernel, ISemanticTextMemory memory, ILogger<GraphicDesigner> logger)
: base(state, memory, kernel)
{
_logger = logger;
}
public override async Task HandleEvent(Event item)
{
if (item?.Type is null)
{
throw new ArgumentNullException(nameof(item));
}
switch (item.Type)
{
case nameof(EventTypes.UserConnected):
// The user reconnected, let's send the last message if we have one
var lastMessage = _state.State.History.LastOrDefault()?.Message;
if (lastMessage == null)
{
return;
}
await SendDesignedCreatedEvent(lastMessage, item.Data["UserId"]);
break;
case nameof(EventTypes.ArticleCreated):
{
var article = item.Data["article"];
_logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.ArticleCreated)}. Article: {{Article}}", article);
var context = new KernelArguments { ["input"] = AppendChatHistory(article) };
string socialMediaPost = await CallFunction(CommunityManagerPrompts.WritePost, context);
_state.State.Data.WrittenSocialMediaPost = socialMediaPost;
await SendDesignedCreatedEvent(socialMediaPost, item.Data["UserId"]);
break;
}
default:
break;
}
}
private async Task SendDesignedCreatedEvent(string socialMediaPost, string userId)
{
await PublishEvent(new Event
{
Namespace = this.GetPrimaryKeyString(),
Type = nameof(EventTypes.SocialMediaPostCreated),
Data = new Dictionary<string, string>
{
["UserId"] = userId,
[nameof(socialMediaPost)] = socialMediaPost,
}
});
}
public Task<string> GetArticle()
{
return Task.FromResult(_state.State.Data.WrittenSocialMediaPost);
}
}

View File

@ -1,11 +0,0 @@
namespace Marketing.Agents;
public static class CommunityManagerPrompts
{
public const string WritePost = """
You are a Marketing community manager writer.
Write a tweet to promote what it is described bellow.
The tweet cannot be longer than 280 characters
Input: {{$input}}
""";
}

View File

@ -1,8 +0,0 @@
namespace Marketing.Agents;
[GenerateSerializer]
public class CommunityManagerState
{
[Id(0)]
public string WrittenSocialMediaPost { get; set; } = "";
}

View File

@ -1,8 +0,0 @@
namespace Marketing.Agents;
[GenerateSerializer]
public class GraphicDesignerState
{
[Id(0)]
public string ImageUrl { get; set; } = "";
}

View File

@ -1,74 +0,0 @@
using Marketing.Events;
using Marketing.Options;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.TextToImage;
using Orleans.Runtime;
namespace Marketing.Agents;
[ImplicitStreamSubscription(Consts.OrleansNamespace)]
public class GraphicDesigner : AiAgent<GraphicDesignerState>
{
protected override string Namespace => Consts.OrleansNamespace;
private readonly ILogger<GraphicDesigner> _logger;
private readonly IConfiguration _configuration;
public GraphicDesigner([PersistentState("state", "messages")] IPersistentState<AgentState<GraphicDesignerState>> state, Kernel kernel, ISemanticTextMemory memory, ILogger<GraphicDesigner> logger, IConfiguration configuration)
: base(state, memory, kernel)
{
_logger = logger;
_configuration = configuration;
}
public override async Task HandleEvent(Event item)
{
ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(EventTypes.UserConnected):
// The user reconnected, let's send the last message if we have one
var lastMessage = _state.State.History.LastOrDefault()?.Message;
if (lastMessage == null)
{
return;
}
await SendDesignedCreatedEvent(lastMessage, item.Data["UserId"]);
break;
case nameof(EventTypes.ArticleCreated):
//TODO
_logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.ArticleCreated)}.");
var article = item.Data["article"];
var dallEService = _kernel.GetRequiredService<ITextToImageService>();
var imageUri = await dallEService.GenerateImageAsync(article, 1024, 1024);
_state.State.Data.ImageUrl = imageUri;
await SendDesignedCreatedEvent(imageUri, item.Data["UserId"]);
break;
default:
break;
}
}
private async Task SendDesignedCreatedEvent(string imageUri, string userId)
{
await PublishEvent(new Event
{
Namespace = this.GetPrimaryKeyString(),
Type = nameof(EventTypes.GraphicDesignCreated),
Data = new Dictionary<string, string> {
{ "UserId", userId },
{ nameof(imageUri), imageUri}
}
});
}
}

View File

@ -1,11 +0,0 @@
namespace Marketing.Agents;
public static class GraphicDesignerPrompts
{
public const string GenerateImage = """
You are a Marketing community manager graphic designer.
Bellow is a campaign that you need to create a image for.
Create an image of maximum 500x500 pixels that could be use in social medias as a marketing image.
Input: {{$input}}
""";
}

View File

@ -1,48 +0,0 @@
using Marketing.Events;
using Marketing.Options;
using Marketing.SignalRHub;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
namespace Marketing.Agents;
[ImplicitStreamSubscription(Consts.OrleansNamespace)]
public class SignalRAgent : Agent
{
protected override string Namespace => Consts.OrleansNamespace;
private readonly ILogger<SignalRAgent> _logger;
private readonly ISignalRService _signalRClient;
public SignalRAgent(ILogger<SignalRAgent> logger, ISignalRService signalRClient)
{
_logger = logger;
_signalRClient = signalRClient;
}
public override async Task HandleEvent(Event item)
{
ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(EventTypes.ArticleCreated):
var writtenArticle = item.Data["article"];
await _signalRClient.SendMessageToSpecificClient(item.Data["UserId"], writtenArticle, AgentTypes.Chat);
break;
case nameof(EventTypes.GraphicDesignCreated):
var imageUrl = item.Data["imageUri"];
await _signalRClient.SendMessageToSpecificClient(item.Data["UserId"], imageUrl, AgentTypes.GraphicDesigner);
break;
case nameof(EventTypes.SocialMediaPostCreated):
var post = item.Data["socialMediaPost"];
await _signalRClient.SendMessageToSpecificClient(item.Data["UserId"], post, AgentTypes.CommunityManager);
break;
default:
break;
}
}
}

View File

@ -1,5 +0,0 @@
namespace Marketing.Agents;
public interface IWriter : IGrainWithStringKey
{
Task<string> GetArticle();
}

View File

@ -1,75 +0,0 @@
using Marketing.Events;
using Marketing.Options;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Orleans.Runtime;
namespace Marketing.Agents;
[ImplicitStreamSubscription(Consts.OrleansNamespace)]
public class Writer : AiAgent<WriterState>, IWriter
{
protected override string Namespace => Consts.OrleansNamespace;
private readonly ILogger<GraphicDesigner> _logger;
public Writer([PersistentState("state", "messages")] IPersistentState<AgentState<WriterState>> state, Kernel kernel, ISemanticTextMemory memory, ILogger<GraphicDesigner> logger)
: base(state, memory, kernel)
{
_logger = logger;
}
public override async Task HandleEvent(Event item)
{
ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(EventTypes.UserConnected):
// The user reconnected, let's send the last message if we have one
var lastMessage = _state.State.History.LastOrDefault()?.Message;
if (lastMessage == null)
{
return;
}
await SendDesignedCreatedEvent(lastMessage, item.Data["UserId"]);
break;
case nameof(EventTypes.UserChatInput):
{
var userMessage = item.Data["userMessage"];
_logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.UserChatInput)}. UserMessage: {{UserMessage}}", userMessage);
var context = new KernelArguments { ["input"] = AppendChatHistory(userMessage) };
string newArticle = await CallFunction(WriterPrompts.Write, context);
await SendDesignedCreatedEvent(newArticle, item.Data["UserId"]);
break;
}
default:
break;
}
}
private async Task SendDesignedCreatedEvent(string article, string userId)
{
await PublishEvent(new Event
{
Namespace = this.GetPrimaryKeyString(),
Type = nameof(EventTypes.ArticleCreated),
Data = new Dictionary<string, string> {
{ "UserId", userId },
{ nameof(article), article },
}
});
}
public Task<string> GetArticle()
{
return Task.FromResult(_state.State.Data.WrittenArticle!);
}
}

View File

@ -1,12 +0,0 @@
namespace Marketing.Agents;
public static class WriterPrompts
{
public const string Write = """
You are a Marketing writer.
Write up to three paragraphs for a campaign to promote what it is described bellow.
Bellow are a series of inputs from the user that you can use to create the campaign.
If the input talks about twitter or images, dismiss it and return the same as before.
Input: {{$input}}
""";
}

View File

@ -1,8 +0,0 @@
namespace Marketing.Agents;
[GenerateSerializer]
public class WriterState
{
[Id(0)]
public string? WrittenArticle { get; set; }
}

View File

@ -1,63 +0,0 @@
using Marketing.Agents;
using Marketing.Events;
using Marketing.Options;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AspNetCore.Mvc;
using Orleans.Runtime;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace Marketing.Controller;
[GenerateSerializer]
public class Asd
{
[Id(0)]
public required string Name { get; set; }
}
[Route("api/[controller]")]
[ApiController]
public class Articles : ControllerBase
{
private readonly IClusterClient _client;
public Articles(IClusterClient client)
{
_client = client;
}
// GET api/<Post>/5
[HttpGet("{id}")]
public async Task<string> Get(string id)
{
var grain = _client.GetGrain<IWriter>(id);
string article = await grain.GetArticle();
return article;
}
// PUT api/<Post>/5
[HttpPut("{UserId}")]
public async Task<string> Put(string UserId, [FromBody] string userMessage)
{
ArgumentNullException.ThrowIfNull(UserId);
var streamProvider = _client.GetStreamProvider("StreamProvider");
var streamId = StreamId.Create(Consts.OrleansNamespace, UserId);
var stream = streamProvider.GetStream<Event>(streamId);
var data = new Dictionary<string, string>
{
{ nameof(UserId), UserId.ToString() },
{ nameof(userMessage), userMessage },
};
await stream.OnNextAsync(new Event
{
Namespace = UserId,
Type = nameof(EventTypes.UserChatInput),
Data = data
});
return $"Task {UserId} accepted";
}
}

View File

@ -1,23 +0,0 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 5274
EXPOSE 11111
EXPOSE 30000
ENV ASPNETCORE_URLS=http://+:5274
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG configuration=Release
COPY . .
RUN dotnet restore "samples/marketing/src/backend/Marketing.csproj"
WORKDIR "samples/marketing/src/backend"
RUN dotnet build "Marketing.csproj" -c $configuration -o /app/build
FROM build AS publish
ARG configuration=Release
RUN dotnet publish "Marketing.csproj" -c $configuration -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Marketing.dll"]

View File

@ -1,10 +0,0 @@
namespace Marketing.Events;
public enum EventTypes
{
UserChatInput,
ArticleCreated,
UserConnected,
GraphicDesignCreated,
SocialMediaPostCreated,
}

View File

@ -1,51 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<ItemGroup>
<Compile Remove="Services\**" />
<Content Remove="Services\**" />
<EmbeddedResource Remove="Services\**" />
<None Remove="Services\**" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ServerGarbageCollection>true</ServerGarbageCollection>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
<UserSecretsId>c073c86e-8483-4956-942f-331fd09172d4</UserSecretsId>
<AnalysisMode>All</AnalysisMode>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Octokit.Webhooks.AspNetCore" />
<PackageReference Include="Octokit" />
<PackageReference Include="Microsoft.SemanticKernel" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Qdrant" />
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Memory" />
<PackageReference Include="Microsoft.Orleans.Server" />
<PackageReference Include="Microsoft.Orleans.Sdk" />
<PackageReference Include="Microsoft.Orleans.Runtime" />
<PackageReference Include="Microsoft.Orleans.Persistence.Cosmos" />
<PackageReference Include="Microsoft.Orleans.Clustering.Cosmos" />
<PackageReference Include="Microsoft.Orleans.Reminders.Cosmos" />
<PackageReference Include="Microsoft.Orleans.Streaming.EventHubs" />
<PackageReference Include="Microsoft.Orleans.Reminders" />
<PackageReference Include="Microsoft.Orleans.Streaming" />
<PackageReference Include="OrleansDashboard" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" />
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Microsoft.Extensions.Azure" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
<PackageReference Include="Azure.ResourceManager.ContainerInstance" />
<PackageReference Include="Azure.Storage.Files.Shares" />
<PackageReference Include="Azure.Data.Tables" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.AI.Agents.Orleans\Microsoft.AI.Agents.Orleans.csproj" />
</ItemGroup>
</Project>

View File

@ -1,37 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing", "Marketing.csproj", "{491A6E2F-A0D0-4723-80A2-B0F60091E39D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.Agents.Orleans", "..\..\..\..\src\Microsoft.AI.Agents.Orleans\Microsoft.AI.Agents.Orleans.csproj", "{4C246005-605C-4F7F-8D67-839A5640587A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.Agents", "..\..\..\..\src\Microsoft.AI.Agents\Microsoft.AI.Agents.csproj", "{5CB1E5D6-33BF-4E09-BDE3-29F8A24F2158}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{491A6E2F-A0D0-4723-80A2-B0F60091E39D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{491A6E2F-A0D0-4723-80A2-B0F60091E39D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{491A6E2F-A0D0-4723-80A2-B0F60091E39D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{491A6E2F-A0D0-4723-80A2-B0F60091E39D}.Release|Any CPU.Build.0 = Release|Any CPU
{4C246005-605C-4F7F-8D67-839A5640587A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4C246005-605C-4F7F-8D67-839A5640587A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C246005-605C-4F7F-8D67-839A5640587A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C246005-605C-4F7F-8D67-839A5640587A}.Release|Any CPU.Build.0 = Release|Any CPU
{5CB1E5D6-33BF-4E09-BDE3-29F8A24F2158}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5CB1E5D6-33BF-4E09-BDE3-29F8A24F2158}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5CB1E5D6-33BF-4E09-BDE3-29F8A24F2158}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5CB1E5D6-33BF-4E09-BDE3-29F8A24F2158}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A6288EF9-5DDB-45A9-AA9A-EB9FDB18D82F}
EndGlobalSection
EndGlobal

View File

@ -1,6 +0,0 @@
namespace Marketing.Options;
public static class Consts
{
public const string OrleansNamespace = "default";
}

View File

@ -1,30 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Marketing.Options;
public class OpenAIOptions
{
// Embeddings
[Required]
public required string EmbeddingsEndpoint { get; set; }
[Required]
public required string EmbeddingsApiKey { get; set; }
[Required]
public required string EmbeddingsDeploymentOrModelId { get; set; }
// Chat
[Required]
public required string ChatEndpoint { get; set; }
[Required]
public required string ChatApiKey { get; set; }
[Required]
public required string ChatDeploymentOrModelId { get; set; }
// TextToImage
[Required]
public required string ImageEndpoint { get; set; }
[Required]
public required string ImageApiKey { get; set; }
// When using OpenAI, this is not required.
public required string ImageDeploymentOrModelId { get; set; }
}

View File

@ -1,10 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Marketing.Options;
public class QdrantOptions
{
[Required]
public required string Endpoint { get; set; }
[Required]
public required int VectorSize { get; set; }
}

View File

@ -1,160 +0,0 @@
using System.Text.Json;
using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.Options;
using Microsoft.SemanticKernel;
using Microsoft.Extensions.Http.Resilience;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Marketing.SignalRHub;
using Marketing.Options;
using Orleans.Serialization;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient(CreateKernel);
builder.Services.AddTransient(CreateMemory);
builder.Services.AddHttpClient();
builder.Services.AddControllers();
builder.Services.AddApplicationInsightsTelemetry();
builder.Services.AddSwaggerGen();
builder.Services.AddSignalR();
builder.Services.AddSingleton<ISignalRService, SignalRService>();
// Allow any CORS origin if in DEV
const string AllowDebugOriginPolicy = "AllowDebugOrigin";
if (builder.Environment.IsDevelopment())
{
builder.Services.AddCors(options =>
{
options.AddPolicy(AllowDebugOriginPolicy, builder =>
{
builder
.WithOrigins("http://localhost:3000") // client url
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
}
builder.Services.AddOptions<OpenAIOptions>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection(nameof(OpenAIOptions)).Bind(settings);
})
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Services.AddOptions<QdrantOptions>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection(nameof(QdrantOptions)).Bind(settings);
})
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Host.UseOrleans(siloBuilder =>
{
siloBuilder.UseLocalhostClustering()
.AddMemoryStreams("StreamProvider")
.AddMemoryGrainStorage("PubSubStore")
.AddMemoryGrainStorage("messages");
siloBuilder.UseInMemoryReminderService();
siloBuilder.UseDashboard(x => x.HostSelf = true);
});
builder.Services.Configure<JsonSerializerOptions>(options =>
{
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
var app = builder.Build();
app.UseRouting();
app.UseCors(AllowDebugOriginPolicy);
app.MapControllers();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.Map("/dashboard", x => x.UseOrleansDashboard());
app.MapHub<ArticleHub>("/articlehub");
app.Run();
static ISemanticTextMemory CreateMemory(IServiceProvider provider)
{
OpenAIOptions openAiConfig = provider.GetRequiredService<IOptions<OpenAIOptions>>().Value;
QdrantOptions qdrantConfig = provider.GetRequiredService<IOptions<QdrantOptions>>().Value;
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.SetMinimumLevel(LogLevel.Debug)
.AddConsole()
.AddDebug();
});
var memoryBuilder = new MemoryBuilder();
return memoryBuilder.WithLoggerFactory(loggerFactory)
.WithQdrantMemoryStore(qdrantConfig.Endpoint, qdrantConfig.VectorSize)
.WithAzureOpenAITextEmbeddingGeneration(openAiConfig.EmbeddingsDeploymentOrModelId, openAiConfig.EmbeddingsEndpoint, openAiConfig.EmbeddingsApiKey)
.Build();
}
static Kernel CreateKernel(IServiceProvider provider)
{
OpenAIOptions openAiConfig = provider.GetRequiredService<IOptions<OpenAIOptions>>().Value;
var clientOptions = new OpenAIClientOptions();
clientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(5);
var builder = Kernel.CreateBuilder();
builder.Services.AddLogging(c => c.AddConsole().AddDebug().SetMinimumLevel(LogLevel.Debug));
// Chat
var openAIClient = new OpenAIClient(new Uri(openAiConfig.ChatEndpoint), new AzureKeyCredential(openAiConfig.ChatApiKey), clientOptions);
if (openAiConfig.ChatEndpoint.Contains(".azure", StringComparison.OrdinalIgnoreCase))
{
builder.Services.AddAzureOpenAIChatCompletion(openAiConfig.ChatDeploymentOrModelId, openAIClient);
}
else
{
builder.Services.AddOpenAIChatCompletion(openAiConfig.ChatDeploymentOrModelId, openAIClient);
}
// Text to Image
openAIClient = new OpenAIClient(new Uri(openAiConfig.ImageEndpoint), new AzureKeyCredential(openAiConfig.ImageApiKey), clientOptions);
if (openAiConfig.ImageEndpoint.Contains(".azure", StringComparison.OrdinalIgnoreCase))
{
ArgumentException.ThrowIfNullOrEmpty(nameof(openAiConfig.ImageDeploymentOrModelId), openAiConfig.ImageDeploymentOrModelId);
builder.Services.AddAzureOpenAITextToImage(openAiConfig.ImageDeploymentOrModelId, openAIClient);
}
else
{
builder.Services.AddOpenAITextToImage(openAiConfig.ImageApiKey);
}
// Embeddings
openAIClient = new OpenAIClient(new Uri(openAiConfig.EmbeddingsEndpoint), new AzureKeyCredential(openAiConfig.EmbeddingsApiKey), clientOptions);
if (openAiConfig.EmbeddingsEndpoint.Contains(".azure", StringComparison.OrdinalIgnoreCase))
{
builder.Services.AddAzureOpenAITextEmbeddingGeneration(openAiConfig.EmbeddingsDeploymentOrModelId, openAIClient);
}
else
{
builder.Services.AddOpenAITextEmbeddingGeneration(openAiConfig.EmbeddingsDeploymentOrModelId, openAIClient);
}
builder.Services.ConfigureHttpClientDefaults(c =>
{
c.AddStandardResilienceHandler().Configure(o =>
{
o.Retry.MaxRetryAttempts = 5;
o.Retry.BackoffType = Polly.DelayBackoffType.Exponential;
});
});
return builder.Build();
}

View File

@ -1,33 +0,0 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:59668",
"sslPort": 44354
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5244",
"launchUrl": "dashboard",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7227;http://localhost:5244",
"launchUrl": "dashboard",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
}
}

View File

@ -1,11 +0,0 @@
namespace Marketing.SignalRHub;
public enum AgentTypes
{
Chat,
CommunityManager,
GraphicDesigner,
Auditor,
Accountant,
Librarian
}

View File

@ -1,78 +0,0 @@
namespace Marketing.SignalRHub;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AspNetCore.SignalR;
using Orleans.Runtime;
using Marketing.Options;
using Marketing.Events;
public class ArticleHub : Hub<IArticleHub>
{
public override async Task OnConnectedAsync()
{
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
SignalRConnectionsDB.ConnectionIdByUser.TryRemove(Context.ConnectionId, out _);
await base.OnDisconnectedAsync(exception);
}
/// <summary>
/// This method is called when a new message from the client arrives.
/// </summary>
/// <param name="frontEndMessage"></param>
/// <param name="clusterClient"></param>
/// <returns></returns>
public async Task ProcessMessage(FrontEndMessage frontEndMessage, IClusterClient clusterClient)
{
ArgumentNullException.ThrowIfNull(frontEndMessage);
var streamProvider = clusterClient.GetStreamProvider("StreamProvider");
var streamId = StreamId.Create(Consts.OrleansNamespace, frontEndMessage.UserId);
var stream = streamProvider.GetStream<Event>(streamId);
var data = new Dictionary<string, string>
{
{ "UserId", frontEndMessage.UserId },
{ "userMessage", frontEndMessage.Message},
};
await stream.OnNextAsync(new Event
{
Namespace = frontEndMessage.UserId,
Type = nameof(EventTypes.UserChatInput),
Data = data
});
}
public async Task ConnectToAgent(string UserId, IClusterClient clusterClient)
{
var frontEndMessage = new FrontEndMessage()
{
UserId = UserId,
Message = "Connected to agents",
Agent = AgentTypes.Chat.ToString()
};
SignalRConnectionsDB.ConnectionIdByUser.AddOrUpdate(UserId, Context.ConnectionId, (key, oldValue) => Context.ConnectionId);
// Notify the agents that a new user got connected.
var streamProvider = clusterClient.GetStreamProvider("StreamProvider");
var streamId = StreamId.Create(Consts.OrleansNamespace, frontEndMessage.UserId);
var stream = streamProvider.GetStream<Event>(streamId);
var data = new Dictionary<string, string>
{
{ "UserId", frontEndMessage.UserId },
{ "userMessage", frontEndMessage.Message},
};
await stream.OnNextAsync(new Event
{
Namespace = frontEndMessage.UserId,
Type = nameof(EventTypes.UserConnected),
Data = data
});
}
}

View File

@ -1,8 +0,0 @@
namespace Marketing.SignalRHub;
public class FrontEndMessage
{
public required string UserId { get; set; }
public required string Message { get; set; }
public required string Agent { get; set; }
}

View File

@ -1,10 +0,0 @@
namespace Marketing.SignalRHub;
public interface IArticleHub
{
public Task ConnectToAgent(string UserId);
public Task ChatMessage(FrontEndMessage frontEndMessage, IClusterClient clusterClient);
public Task SendMessageToSpecificClient(string userId, string message);
}

View File

@ -1,5 +0,0 @@
namespace Marketing.SignalRHub;
public interface ISignalRService
{
Task SendMessageToSpecificClient(string userId, string message, AgentTypes agentType);
}

View File

@ -1,9 +0,0 @@
using System.Collections.Concurrent;
namespace Marketing.SignalRHub;
public static class SignalRConnectionsDB
{
public static ConcurrentDictionary<string, string> ConnectionIdByUser { get; } = new ConcurrentDictionary<string, string>();
public static ConcurrentDictionary<string, string> AllConnections { get; } = new ConcurrentDictionary<string, string>();
}

View File

@ -1,24 +0,0 @@
using Microsoft.AspNetCore.SignalR;
namespace Marketing.SignalRHub;
public class SignalRService : ISignalRService
{
private readonly IHubContext<ArticleHub> _hubContext;
public SignalRService(IHubContext<ArticleHub> hubContext)
{
_hubContext = hubContext;
}
public async Task SendMessageToSpecificClient(string userId, string message, AgentTypes agentType)
{
var connectionId = SignalRConnectionsDB.ConnectionIdByUser[userId];
var frontEndMessage = new FrontEndMessage()
{
UserId = userId,
Message = message,
Agent = agentType.ToString()
};
await _hubContext.Clients.Client(connectionId).SendAsync("ReceiveMessage", frontEndMessage);
}
}

View File

@ -1,34 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Information",
"Microsoft.AspNetCore.SignalR": "Debug",
"Microsoft.AspNetCore.Http.Connections": "Debug"
}
},
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=<mandatory>;IngestionEndpoint=https://<mandatory>.applicationinsights.azure.com/"
},
"AllowedHosts": "*",
"OpenAIOptions": {
"ChatDeploymentOrModelId": "gpt-4-32",
"ChatEndpoint": "https://<mandatory>.openai.azure.com/",
"ChatApiKey": "<mandatory>",
"EmbeddingsDeploymentOrModelId": "text-embedding-ada-002",
"EmbeddingsEndpoint": "https://<mandatory>.openai.azure.com/",
"EmbeddingsApiKey": "<mandatory>",
"ImageDeploymentOrModelId":"dalle3",
"ImageEndpoint": "https://<mandatory>.openai.azure.com/",
"ImageApiKey": "<mandatory>"
},
"QdrantOptions": {
"Endpoint": "http://qdrant:6333",
"VectorSize": "1536"
}
}

View File

@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}

View File

@ -1,38 +0,0 @@
/.infra/
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@ -1,2 +0,0 @@
legacy-peer-deps=true
strict-peer-dependencies=false

View File

@ -1,43 +0,0 @@
FROM refinedev/node:18 AS base
FROM base AS deps
RUN apk add --no-cache libc6-compat
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
FROM base AS builder
COPY --from=deps /app/refine/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
ENV NODE_ENV production
COPY --from=builder /app/refine/public ./public
RUN mkdir .next
RUN chown refine:nodejs .next
COPY --from=builder --chown=refine:nodejs /app/refine/.next/standalone ./
COPY --from=builder --chown=refine:nodejs /app/refine/.next/static ./.next/static
USER refine
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
CMD ["node", "server.js"]

View File

@ -1,11 +0,0 @@
## TODO: describe the frontend app
## How I started
```
npm -i
```
## How to run it
```shell
npm run dev
```

View File

@ -1,17 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
images: {
domains: ['dalleproduse.blob.core.windows.net'],
remotePatterns: [
{
protocol: 'https',
hostname: 'dalleproduse.blob.core.windows.net',
port: '**',
pathname: '**',
},
],
},
};
export default nextConfig;

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +0,0 @@
{
"name": "Learning",
"version": "0.1.0",
"private": true,
"engines": {
"node": ">=18.0.0"
},
"scripts": {
"dev": "cross-env NODE_OPTIONS=--max_old_space_size=4096 refine dev",
"build": "refine build",
"start": "refine start",
"lint": "eslint '**/*.{js,jsx,ts,tsx}'",
"refine": "refine"
},
"dependencies": {
"@emotion/react": "^11.8.2",
"@emotion/styled": "^11.8.1",
"@fontsource/roboto": "^4.5.8",
"@microsoft/signalr": "^8.0.0",
"@mui/icons-material": "^5.8.3",
"@mui/lab": "^5.0.0-alpha.85",
"@mui/material": "^5.8.6",
"@mui/x-data-grid": "^6.6.0",
"@refinedev/cli": "^2.16.21",
"@refinedev/core": "^4.47.1",
"@refinedev/devtools": "^1.1.32",
"@refinedev/kbar": "^1.3.6",
"@refinedev/mui": "^5.14.4",
"@refinedev/nextjs-router": "^6.0.0",
"@refinedev/react-hook-form": "^4.8.14",
"@refinedev/simple-rest": "^5.0.1",
"js-cookie": "^3.0.5",
"next": "14.1.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-flippy": "^1.1.0"
},
"devDependencies": {
"@types/js-cookie": "^3.0.6",
"@types/node": "^18.0.0",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@typescript-eslint/parser": "5.48.0",
"cross-env": "^7.0.3",
"eslint": "^8",
"eslint-config-next": "14.1.0",
"typescript": "^4.7.4"
},
"refine": {
"projectId": "Z0Y3hf-6BZFyJ-VWX5Qq"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,23 +0,0 @@
import { AuthPage } from "@components/auth-page";
import { authProviderServer } from "@providers/auth-provider";
import { redirect } from "next/navigation";
export default async function ForgotPassword() {
const data = await getData();
if (data.authenticated) {
redirect(data?.redirectTo || "/");
}
return <AuthPage type="forgotPassword" />;
}
async function getData() {
const { authenticated, redirectTo, error } = await authProviderServer.check();
return {
authenticated,
redirectTo,
error,
};
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -1,68 +0,0 @@
import { DevtoolsProvider } from "@providers/devtools";
import { Refine } from "@refinedev/core";
import { RefineKbar, RefineKbarProvider } from "@refinedev/kbar";
import { RefineSnackbarProvider, notificationProvider } from "@refinedev/mui";
import routerProvider from "@refinedev/nextjs-router";
import { Metadata } from "next";
import { cookies } from "next/headers";
import React, { Suspense } from "react";
import { ColorModeContextProvider } from "@contexts/color-mode";
import { authProvider } from "@providers/auth-provider";
import { dataProvider } from "@providers/data-provider";
export const metadata: Metadata = {
title: "Refine",
description: "Generated by create refine app",
icons: {
icon: "/favicon.ico",
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = cookies();
const theme = cookieStore.get("theme");
const defaultMode = theme?.value === "dark" ? "dark" : "light";
return (
<html lang="en">
<body>
<Suspense>
<RefineKbarProvider>
<ColorModeContextProvider defaultMode={defaultMode}>
<RefineSnackbarProvider>
<DevtoolsProvider>
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider}
notificationProvider={notificationProvider}
authProvider={authProvider}
resources={[
{
name: "marketing-app",
list: "/marketing",
}
]}
options={{
syncWithLocation: true,
warnWhenUnsavedChanges: true,
useNewQueryKeys: true,
projectId: "Z0Y3hf-6BZFyJ-VWX5Qq",
}}
>
{children}
<RefineKbar />
</Refine>
</DevtoolsProvider>
</RefineSnackbarProvider>
</ColorModeContextProvider>
</RefineKbarProvider>
</Suspense>
</body>
</html>
);
}

View File

@ -1,23 +0,0 @@
import { AuthPage } from "@components/auth-page";
import { authProviderServer } from "@providers/auth-provider";
import { redirect } from "next/navigation";
export default async function Login() {
const data = await getData();
if (data.authenticated) {
redirect(data?.redirectTo || "/");
}
return <AuthPage type="login" />;
}
async function getData() {
const { authenticated, redirectTo, error } = await authProviderServer.check();
return {
authenticated,
redirectTo,
error,
};
}

View File

@ -1,147 +0,0 @@
"use client";
import * as React from 'react';
import List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Public from '@mui/icons-material/Public';
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
import HandshakeTwoToneIcon from '@mui/icons-material/HandshakeTwoTone';
import WorkspacePremiumTwoToneIcon from '@mui/icons-material/WorkspacePremiumTwoTone';
import { styled } from '@mui/material/styles';
import LightbulbIcon from '@mui/icons-material/Lightbulb';
import { Button, Container, Grid, TextField } from '@mui/material';
const data = [
{ icon: <HandshakeTwoToneIcon />, label: 'Bank vs Mrs Peters - Settled - chf 1.5M - 1 year' },
{ icon: <WorkspacePremiumTwoToneIcon />, label: 'Bank vs Mr Pertussi - Won - chf0 - 4 years' },
{ icon: <Public />, label: 'Bank vs Governnent - Public Case - chf 3.7M - 10 years' },
];
const FireNav = styled(List)<{ component?: React.ElementType }>({
'& .MuiListItemButton-root': {
paddingLeft: 24,
paddingRight: 24,
},
'& .MuiListItemIcon-root': {
minWidth: 0,
marginRight: 16,
},
'& .MuiSvgIcon-root': {
fontSize: 20,
},
});
type Message = {
sender: string;
text: any;
};
type ChatProps = {
messages: Message[];
setMessages: (messages: Message[]) => void;
sendMessage: (message: string, agent: string) => void;
};
export default function Chat({ messages, setMessages, sendMessage }: ChatProps) {
const [open, setOpen] = React.useState(true);
const [message, setMessage] = React.useState<string>('');
const handleSend = (message:string) => {
setMessages([...messages, { sender: 'user', text: message }]);
sendMessage(message, "chat");
};
return (
<FireNav component="nav" disablePadding>
<ListItemButton
alignItems="flex-start"
onClick={() => setOpen(!open)}
sx={{
px: 3,
pt: 2.5,
pb: open ? 0 : 2.5,
'&:hover, &:focus': { '& #arrowdownicon': { opacity: open ? 1 : 0 } },
}}
>
<ListItemIcon sx={{ my: 0, opacity: 1, class: "menuicon" }}>
<LightbulbIcon />
</ListItemIcon>
<ListItemText
primary="Chat"
primaryTypographyProps={{
fontSize: 15,
fontWeight: 'medium',
lineHeight: '20px',
mb: '2px',
}}
secondary="What would you like the campaing to be about?"
secondaryTypographyProps={{
noWrap: true,
fontSize: 12,
lineHeight: '16px',
color: open ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)',
}}
sx={{ my: 0 }}
/>
<KeyboardArrowDown
id="arrowdownicon"
sx={{
mr: -1,
opacity: 0,
transform: open ? 'rotate(-180deg)' : 'rotate(0)',
transition: '0.2s',
}}
/>
</ListItemButton>
{open && (
<div style={{ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 50px)' }}>
<Container maxWidth={false} style={{ overflowY: 'auto', flex: '1 0 auto' }}>
<div style={{ margin: '0 auto' }}>
{messages.map((message, index) => (
<div key={index} style={{
margin: '10px',
padding: '10px',
borderRadius: '10px',
backgroundColor: message.sender === 'user' ? '#d1e7dd' : '#d4e2d4',
alignSelf: message.sender === 'user' ? 'flex-end' : 'flex-start',
maxWidth: '80%',
wordWrap: 'break-word'
}}>
<strong>{message.sender}:</strong> {message.text}
</div>
))}
</div>
</Container>
<Container maxWidth={false} style={{ height: '150px' }}>
<Grid container spacing={1} alignItems="flex-end">
<Grid item xs={11}>
<TextField
value={message}
onChange={(e) => setMessage(e.target.value)}
fullWidth
inputProps={{ style: { height: 'auto' } }}
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSend((e.target as HTMLInputElement).value);
setMessage('');
}
}}
/>
</Grid>
<Grid item xs={1}>
<Button style={{ height: '100%' }} onClick={() => {
handleSend(message);
setMessage('');
}}>
Send
</Button>
</Grid>
</Grid>
</Container>
</div>
)}
</FireNav>
);
}

View File

@ -1,131 +0,0 @@
"use client";
import * as React from 'react';
import Box from '@mui/material/Box';
import List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
import { styled } from '@mui/material/styles';
import AppShortcut from '@mui/icons-material/AttachMoney';
import LoopIcon from '@mui/icons-material/Loop';
import { Card, CardContent, Typography } from '@mui/material';
import Image from 'next/image';
const FireNav = styled(List)<{ component?: React.ElementType }>({
'& .MuiListItemButton-root': {
paddingLeft: 24,
paddingRight: 24,
},
'& .MuiListItemIcon-root': {
minWidth: 0,
marginRight: 16,
},
'& .MuiSvgIcon-root': {
fontSize: 20,
},
});
type CommunityManagerProps = {
article: string;
open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
imgUrl: string;
};
export default function CommunityManager({ article, open, setOpen, imgUrl }: CommunityManagerProps) {
console.log(`[CommunityManager] Rendering. Url: '${imgUrl}'`);
return (
<Box sx={{ display: 'flex' }}>
<FireNav component="nav" disablePadding>
<Box
sx={{
//bgcolor: open ? 'rgba(71, 98, 130, 0.2)' : null,
//pb: open ? 2 : 0,
}}
>
<ListItemButton
alignItems="flex-start"
onClick={() => setOpen(!open)}
sx={{
px: 3,
pt: 2.5,
pb: open ? 0 : 2.5,
'&:hover, &:focus': { '& #arrowdownicon': { opacity: open ? 1 : 0 } },
}}
>
<ListItemIcon sx={{ my: 0, opacity: 1, class: "menuicon" }}>
<AppShortcut />
</ListItemIcon>
<ListItemText
primary="Social Media posts"
primaryTypographyProps={{
fontSize: 15,
fontWeight: 'medium',
lineHeight: '20px',
mb: '2px',
}}
secondary="Posts in social media"
secondaryTypographyProps={{
noWrap: true,
fontSize: 12,
lineHeight: '16px',
color: open ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)',
}}
sx={{ my: 0 }}
/>
<KeyboardArrowDown
id="arrowdownicon"
sx={{
mr: -1,
opacity: 0,
transform: open ? 'rotate(-180deg)' : 'rotate(0)',
transition: '0.2s',
}}
/>
</ListItemButton>
{open && (
article === '' || article === null ? (
<Box>
<LoopIcon
sx={{
animation: "spin 2s linear infinite",
"@keyframes spin": {
"0%": {
transform: "rotate(360deg)",
},
"100%": {
transform: "rotate(0deg)",
},
},
}}
/>
</Box>
) : (
<Card>
<CardContent>
<Typography variant="h5" component="div">
Social media posts on X
</Typography>
<p>{article}</p>
{imgUrl && (
<div style={{ width: '100%', height: '500px', position: 'relative' }}>
<Image
layout='fill'
objectFit='cover'
src={imgUrl}
alt="Graphic designer is working on an image ..."
/>
</div>
)}
</CardContent>
</Card>
)
)}
</Box>
</FireNav>
</Box>
);
}

View File

@ -1,111 +0,0 @@
"use client";
import * as React from 'react';
import Box from '@mui/material/Box';
import List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
import Public from '@mui/icons-material/Public';
import HandshakeTwoToneIcon from '@mui/icons-material/HandshakeTwoTone';
import WorkspacePremiumTwoToneIcon from '@mui/icons-material/WorkspacePremiumTwoTone';
import GavelIcon from '@mui/icons-material/Gavel';
import { styled } from '@mui/material/styles';
import { green, pink } from '@mui/material/colors';
import AttachMoneyIcon from '@mui/icons-material/AttachMoney';
const data = [
{ icon: <HandshakeTwoToneIcon sx={{ color: green[500] }} />, label: 'IPA discount form 9-9 - eur 15k' },
{ icon: <GavelIcon sx={{ color: green[500] }} />, label: '2x1 brewery birthday - eur 250k' },
{ icon: <HandshakeTwoToneIcon sx={{ color: green[500] }} />, label: 'Summer day 1 - CHF 180k' },
{ icon: <HandshakeTwoToneIcon sx={{ color: pink[500] }} />, label: 'Worldcup promo 1.5M' },
];
const FireNav = styled(List)<{ component?: React.ElementType }>({
'& .MuiListItemButton-root': {
paddingLeft: 24,
paddingRight: 24,
},
'& .MuiListItemIcon-root': {
minWidth: 0,
marginRight: 16,
},
'& .MuiSvgIcon-root': {
fontSize: 20,
},
});
export default function CostList() {
const [open, setOpen] = React.useState(false);
console.log(`[Marketing] Rendering.`);
return (
<Box sx={{ display: 'flex' }}>
<FireNav component="nav" disablePadding>
<Box
sx={{
//bgcolor: open ? 'rgba(71, 98, 130, 0.2)' : null,
//pb: open ? 2 : 0,
}}
>
<ListItemButton
alignItems="flex-start"
onClick={() => setOpen(!open)}
sx={{
px: 3,
pt: 2.5,
pb: open ? 0 : 2.5,
'&:hover, &:focus': { '& #arrowdownicon': { opacity: open ? 1 : 0 } },
}}
>
<ListItemIcon sx={{ my: 0, opacity: 1, class: "menuicon"}}>
<AttachMoneyIcon/>
</ListItemIcon>
<ListItemText
primary="Economy of similar cases"
primaryTypographyProps={{
fontSize: 15,
fontWeight: 'medium',
lineHeight: '20px',
mb: '2px',
}}
secondary="Cost of similar cases in the past"
secondaryTypographyProps={{
noWrap: true,
fontSize: 12,
lineHeight: '16px',
color: open ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)',
}}
sx={{ my: 0 }}
/>
<KeyboardArrowDown
id="arrowdownicon"
sx={{
mr: -1,
opacity: 0,
transform: open ? 'rotate(-180deg)' : 'rotate(0)',
transition: '0.2s',
}}
/>
</ListItemButton>
{open &&
data.map((item) => (
<ListItemButton
key={item.label}
sx={{ py: 0, minHeight: 32, color: 'rgba(255,255,255,.8)' }}
>
<ListItemIcon sx={{ color: 'inherit' }}>
{item.icon}
</ListItemIcon>
<ListItemText
primary={item.label}
primaryTypographyProps={{ fontSize: 14, fontWeight: 'medium' }}
/>
</ListItemButton>
))}
</Box>
</FireNav>
</Box>
);
}

View File

@ -1,170 +0,0 @@
"use client";
import * as React from 'react';
import Box from '@mui/material/Box';
import List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
import Public from '@mui/icons-material/Public';
import HandshakeTwoToneIcon from '@mui/icons-material/HandshakeTwoTone';
import WorkspacePremiumTwoToneIcon from '@mui/icons-material/WorkspacePremiumTwoTone';
import { styled } from '@mui/material/styles';
import FolderIcon from '@mui/icons-material/Folder';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';
import Collapse from '@mui/material/Collapse';
import FilePresentIcon from '@mui/icons-material/FilePresent';
const data = [
{ icon: <HandshakeTwoToneIcon />, label: 'Internal guidance for marketing campaigns' },
{ icon: <WorkspacePremiumTwoToneIcon />, label: 'Belgium law on marketing of alcohol' },
{ icon: <Public />, label: 'something else' },
];
const FireNav = styled(List)<{ component?: React.ElementType }>({
'& .MuiListItemButton-root': {
paddingLeft: 24,
paddingRight: 24,
},
'& .MuiListItemIcon-root': {
minWidth: 0,
marginRight: 16,
},
'& .MuiSvgIcon-root': {
fontSize: 20,
},
});
export default function RelevantDocumentList() {
const [open, setOpen] = React.useState(false);
console.log(`[Marketing] Rendering.`);
const [courtCasesOpen, setCourtCasesOpen] = React.useState(true);
const courtCasesOpenHandleClick = () => {
setCourtCasesOpen(!courtCasesOpen);
};
const [lawOpen, setLawOpen] = React.useState(true);
const LawOpenHandleClick = () => {
setLawOpen(!lawOpen);
};
return (
<Box sx={{ display: 'flex' }}>
<FireNav component="nav" disablePadding>
<Box
sx={{
bgcolor: open ? 'rgba(71, 98, 130, 0.2)' : null,
pb: open ? 2 : 0,
}}
>
<ListItemButton
alignItems="flex-start"
onClick={() => setOpen(!open)}
sx={{
px: 3,
pt: 2.5,
pb: open ? 0 : 2.5,
'&:hover, &:focus': { '& #arrowdownicon': { opacity: open ? 1 : 0 } },
}}
>
<ListItemIcon sx={{ my: 0, opacity: 1, class: "menuicon" }}>
<FilePresentIcon />
</ListItemIcon>
<ListItemText
primary="Relevant files"
primaryTypographyProps={{
fontSize: 15,
fontWeight: 'medium',
lineHeight: '20px',
mb: '2px',
}}
secondary="Files that might be relevant to your case."
secondaryTypographyProps={{
noWrap: true,
fontSize: 12,
lineHeight: '16px',
color: open ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)',
}}
sx={{ my: 0 }}
/>
<KeyboardArrowDown
id="arrowdownicon"
sx={{
mr: -1,
opacity: 0,
transform: open ? 'rotate(-180deg)' : 'rotate(0)',
transition: '0.2s',
}}
/>
</ListItemButton>
{open && (
<Box>
<List
sx={{ bgcolor: 'background.paper', textAlign: 'left' }}
component="nav"
aria-labelledby="nested-list-subheader"
>
{/* Court cases */}
<List>
<ListItemButton onClick={courtCasesOpenHandleClick}>
<ListItemIcon>
<FolderIcon />
</ListItemIcon>
<ListItemText primary="Internal docs" />
{courtCasesOpen ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={courtCasesOpen} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItemButton>
<ListItemIcon sx={{ pl: 4 }}>
<img src="/static/icons/docs.png" height={20} width={20} />
</ListItemIcon>
<ListItemText primary="Marketing campaings general guidelines" />
</ListItemButton>
<ListItemButton>
<ListItemIcon sx={{ pl: 4 }}>
<img src="/static/icons/pdf.png" height={20} width={20} />
</ListItemIcon>
<ListItemText primary="Marketing regulations in Belgium" />
</ListItemButton>
</List>
</Collapse>
</List>
{/* Laws */}
<List>
<ListItemButton onClick={LawOpenHandleClick}>
<ListItemIcon>
<FolderIcon />
</ListItemIcon>
<ListItemText primary="Public " />
{lawOpen ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={lawOpen} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItemButton>
<ListItemIcon sx={{ pl: 4 }}>
<img src="/static/icons/edge.png" height={20} width={20} />
</ListItemIcon>
<ListItemText primary="Worldwide discount" />
</ListItemButton>
<ListItemButton>
<ListItemIcon sx={{ pl: 4 }}>
<img src="/static/icons/edge.png" height={20} width={20} />
</ListItemIcon>
<ListItemText primary="Color week - T-Shitrs 2022" />
</ListItemButton>
</List>
</Collapse>
</List>
</List>
</Box>
)}
</Box>
</FireNav>
</Box>
);
}

View File

@ -1,187 +0,0 @@
"use client";
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import React, { useRef, useState } from 'react';
import { styled, ThemeProvider, createTheme } from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import Divider from '@mui/material/Divider';
import StakeholderList from './stakeholders/stakeholders';
import CostList from './costs/cost';
import RelevantDocumentList from './docs/docs';
import Chat from './chat/chat';
import CommunityManager from './community-manager/community-manager';
import { Container, Grid } from '@mui/material';
import { HubConnectionBuilder, HubConnection, LogLevel } from '@microsoft/signalr';
type SignalRMessage = {
userId: string;
message: string;
agent: string;
};
export default function Marketing() {
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(0),
textAlign: 'center',
color: theme.palette.text.secondary,
}));
// Add this style
const Background = styled('div')({
backgroundImage: `url(/static/background1.webp)`,
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
height: '100vh',
});
const [connection, setConnection] = React.useState<HubConnection>();
const userId = 'Carlos';
//Chat component state
const [messages, setMessages] = React.useState<{ sender: string; text: any; }[]>([]);
//Community manager state
const [ article, setArticle ] = useState<string>('');
const [ imgUrl, setImgUrl ] = useState<string>('');
const [ communityManagerOpen, setCommunityManagerOpen ] = useState<boolean>(false);
const createSignalRConnection = async (userId: string) => {
try {
// initi the connection
const connection = new HubConnectionBuilder()
.withUrl(`http://localhost:5244/articlehub`)
.configureLogging(LogLevel.Information)
.build();
//setup handler
connection.on('ReceiveMessage', (message: SignalRMessage) => {
console.log(`[MainPage][${message.userId}] Received message from ${message.agent}: ${message.message}`);
if (message.agent === 'Chat') {
const newMessage = { sender: 'agent', text: message.message };
setMessages(prevMessages => [...prevMessages, newMessage]);
}
if (message.agent === 'CommunityManager') {
setArticle(message.message);
}
if (message.agent === 'GraphicDesigner') {
setImgUrl(message.message);
}
});
connection.onclose(async () => {
console.log(`[MainPage] Connection closed.`);
try {
await connection.start();
console.log(`Connection ID: ${connection.connectionId}`);
await connection.invoke('ConnectToAgent', userId);
console.log(`[MainPage] Connection re-established.`);
} catch (error) {
console.error(error);
}
});
await connection.start();
console.log(`Connection ID: ${connection.connectionId}`);
await connection.invoke('ConnectToAgent', userId);
setConnection(connection);
console.log(`[MainPage] Connection established.`);
} catch (error) {
console.error(error);
}
};
const setMessagesInUI = async (messages: { sender: string; text: any; }[]) => {
await setMessages(messages);
}
const sendMessage = async (message: string, agent: string) => {
if (connection) {
const frontEndMessage:SignalRMessage = {
userId: userId,
message: message,
agent: agent
};
console.log(`[MainPage][${{agent}}] Sending message: ${message}`);
await connection.invoke('ProcessMessage', frontEndMessage);
console.log(`[MainPage][${{agent}}] message sent`);
} else {
console.error(`[MainPage] Connection not established.`);
}
}
React.useEffect(() => {
createSignalRConnection(userId);
}, []);
console.log(`[Marketing] Rendering.`);
return (
<Background>
<Container maxWidth="xl" disableGutters >
<Grid container spacing={3}>
<Grid item xs={6}>
<Paper elevation={0} style={{ border: '1px solid #000' }}>
<Chat messages={messages} setMessages={setMessagesInUI} sendMessage={sendMessage}/>
</Paper>
</Grid>
<Grid item xs={6}>
<Stack spacing={0}>
<ThemeProvider
theme={createTheme({
components: {
MuiListItemButton: {
defaultProps: {
disableTouchRipple: true,
},
},
},
palette: {
mode: 'dark',
// primary: { main: 'rgb(102, 157, 246)' },
// background: { paper: 'rgb(5, 30, 52)' },
primary: { main: '#006BD6' },
background: { paper: 'grey' },
},
})}
>
<Item>
<Paper elevation={0}>
<StakeholderList />
</Paper>
<Divider />
</Item>
<Item>
<Paper elevation={0}>
<CostList />
</Paper>
<Divider />
</Item>
<Item>
<Paper elevation={0}>
<RelevantDocumentList />
</Paper>
<Divider />
</Item>
<Item>
<Paper elevation={0}>
<CommunityManager article={article} open={communityManagerOpen} setOpen={setCommunityManagerOpen} imgUrl={imgUrl}/>
</Paper>
<Divider />
</Item>
</ThemeProvider>
</Stack>
</Grid>
</Grid>
</Container>
</Background>
);
}

View File

@ -1,218 +0,0 @@
"use client";
import React, { useRef, useState } from 'react';
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import { styled } from '@mui/material/styles';
import { Avatar, Box, Container } from '@mui/material';
import Badge, { BadgeProps } from '@mui/material/Badge';
import { Typography } from '@mui/material';
import List from '@mui/material/List';
import ListItemAvatar from '@mui/material/ListItemAvatar';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
import PersonIcon from '@mui/icons-material/Person';
import ListItemIcon from '@mui/material/ListItemIcon';
import { Title } from '@refinedev/mui';
const GreenStyledBadge = styled(Badge)(({ theme }) => ({
'& .MuiBadge-badge': {
backgroundColor: '#44b700',
color: '#44b700',
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
'&::after': {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
borderRadius: '50%',
animation: 'ripple 1.2s infinite ease-in-out',
border: '1px solid currentColor',
content: '""',
},
},
'@keyframes ripple': {
'0%': {
transform: 'scale(.8)',
opacity: 1,
},
'100%': {
transform: 'scale(2.4)',
opacity: 0,
},
},
}));
const RedStyledBadge = styled(Badge)(({ theme }) => ({
'& .MuiBadge-badge': {
backgroundColor: 'red',
color: '#44b700',
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
'&::after': {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
borderRadius: '50%',
animation: 'ripple 1.2s infinite ease-in-out',
border: '1px solid currentColor',
content: '""',
},
},
'@keyframes ripple': {
'0%': {
transform: 'scale(.8)',
opacity: 1,
},
'100%': {
transform: 'scale(2.4)',
opacity: 0,
},
},
}));
export default function StakeholderList() {
const [open, setOpen] = React.useState(false);
console.log(`[Marketing] Rendering.`);
return (
<Box sx={{ display: 'flex' }}>
<List sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}>
<ListItemButton
alignItems="flex-start"
onClick={() => setOpen(!open)}
sx={{
px: 3,
pt: 2.5,
pb: open ? 0 : 2.5,
'&:hover, &:focus': { '& #arrowdownicon': { opacity: open ? 1 : 0 } },
}}
>
<ListItemIcon sx={{ my: 0, opacity: 1, class: "menuicon" }}>
<PersonIcon />
</ListItemIcon>
<ListItemText
primary="Auditor"
primaryTypographyProps={{
fontSize: 15,
fontWeight: 'medium',
lineHeight: '20px',
mb: '2px',
}}
secondary="Auditing rules"
secondaryTypographyProps={{
noWrap: true,
fontSize: 12,
lineHeight: '16px',
color: open ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)',
}}
sx={{ my: 0 }}
/>
<KeyboardArrowDown
id="arrowdownicon"
sx={{
mr: -1,
opacity: 0,
transform: open ? 'rotate(-180deg)' : 'rotate(0)',
transition: '0.2s',
}}
/>
</ListItemButton>
{open && (
<Box>
<ListItemButton alignItems="flex-start">
<ListItemAvatar>
<Avatar alt="Language check" src="/static/check.png" />
</ListItemAvatar>
<ListItemText
primary="Language check"
/>
</ListItemButton>
<ListItemButton alignItems="flex-start">
<ListItemAvatar>
<Avatar alt="Financial check" src="/static/check.png" />
</ListItemAvatar>
<ListItemText
primary="Financial check"
/>
</ListItemButton>
<ListItemButton alignItems="flex-start">
<ListItemAvatar>
<Avatar alt="Auto approval" src="/static/check.png" />
</ListItemAvatar>
<ListItemText
primary="Auto approval"
/>
</ListItemButton>
</Box>
)}
{open && (
<p>Questions? These are relevant stakeholders for you:</p>
)}
{open && (
<Box>
<ListItemButton alignItems="flex-start">
<ListItemAvatar>
<RedStyledBadge
overlap="circular"
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
variant="dot"
>
<Avatar alt="Lawrence Law" src="/static/face2.jpg" />
</RedStyledBadge>
</ListItemAvatar>
<ListItemText
primary="Lina Maria"
secondary={
<React.Fragment>
<Typography
sx={{ display: 'inline' }}
component="span"
variant="body2"
color="text.primary"
>
General Attorney
</Typography>
</React.Fragment>
}
/>
</ListItemButton>
<ListItemButton alignItems="flex-start">
<ListItemAvatar>
<GreenStyledBadge
overlap="circular"
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
variant="dot"
>
<Avatar alt="Remy Sharp" src="/static/face.jpg" />
</GreenStyledBadge>
</ListItemAvatar>
<ListItemText
primary="Lawrence Gevaert"
secondary={
<React.Fragment>
<Typography
sx={{ display: 'inline' }}
component="span"
variant="body2"
color="text.primary"
>
Marketing Manager
</Typography>
</React.Fragment>
}
/>
</ListItemButton>
</Box>
)}
</List>
</Box>
);
}

View File

@ -1,15 +0,0 @@
"use client";
import { Authenticated } from "@refinedev/core";
import { ErrorComponent } from "@refinedev/mui";
import { Suspense } from "react";
export default function NotFound() {
return (
<Suspense>
<Authenticated key="not-found">
<ErrorComponent />
</Authenticated>
</Suspense>
);
}

View File

@ -1,16 +0,0 @@
"use client";
import { Suspense } from "react";
import { Authenticated } from "@refinedev/core";
import { NavigateToResource } from "@refinedev/nextjs-router";
export default function IndexPage() {
return (
<Suspense>
<Authenticated key="home-page">
<NavigateToResource />
</Authenticated>
</Suspense>
);
}

View File

@ -1,23 +0,0 @@
import { AuthPage } from "@components/auth-page";
import { authProviderServer } from "@providers/auth-provider";
import { redirect } from "next/navigation";
export default async function Register() {
const data = await getData();
if (data.authenticated) {
redirect(data?.redirectTo || "/");
}
return <AuthPage type="register" />;
}
async function getData() {
const { authenticated, redirectTo, error } = await authProviderServer.check();
return {
authenticated,
redirectTo,
error,
};
}

View File

@ -1,14 +0,0 @@
"use client";
import type { AuthPageProps } from "@refinedev/core";
import { AuthPage as AuthPageBase } from "@refinedev/mui";
export const AuthPage = (props: AuthPageProps) => {
return (
<AuthPageBase
{...props}
formProps={{
defaultValues: { email: "demo@refine.dev", password: "demodemo" },
}}
/>
);
};

View File

@ -1,80 +0,0 @@
import { ColorModeContext } from "@contexts/color-mode";
import DarkModeOutlined from "@mui/icons-material/DarkModeOutlined";
import LightModeOutlined from "@mui/icons-material/LightModeOutlined";
import AppBar from "@mui/material/AppBar";
import Avatar from "@mui/material/Avatar";
import IconButton from "@mui/material/IconButton";
import Stack from "@mui/material/Stack";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";
import { useGetIdentity } from "@refinedev/core";
import { HamburgerMenu, RefineThemedLayoutV2HeaderProps } from "@refinedev/mui";
import React, { useContext } from "react";
type IUser = {
id: number;
name: string;
avatar: string;
};
export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
sticky = true,
}) => {
const { mode, setMode } = useContext(ColorModeContext);
const { data: user } = useGetIdentity<IUser>();
return (
<AppBar position={sticky ? "sticky" : "relative"}>
<Toolbar>
<Stack
direction="row"
width="100%"
justifyContent="flex-end"
alignItems="center"
>
<HamburgerMenu />
<Stack
direction="row"
width="100%"
justifyContent="flex-end"
alignItems="center"
>
<IconButton
color="inherit"
onClick={() => {
setMode();
}}
>
{mode === "dark" ? <LightModeOutlined /> : <DarkModeOutlined />}
</IconButton>
{(user?.avatar || user?.name) && (
<Stack
direction="row"
gap="16px"
alignItems="center"
justifyContent="center"
>
{user?.name && (
<Typography
sx={{
display: {
xs: "none",
sm: "inline-block",
},
}}
variant="subtitle2"
>
{user?.name}
</Typography>
)}
<Avatar src={user?.avatar} alt={user?.name} />
</Stack>
)}
</Stack>
</Stack>
</Toolbar>
</AppBar>
);
};

View File

@ -1,11 +0,0 @@
"use client";
import { Header } from "@components/header";
import { ThemedLayoutV2 } from "@refinedev/mui";
import React from "react";
export const ThemedLayout = ({ children }: React.PropsWithChildren) => {
return (
<ThemedLayoutV2 Header={() => <Header sticky />}>{children}</ThemedLayoutV2>
);
};

View File

@ -1,72 +0,0 @@
"use client";
import CssBaseline from "@mui/material/CssBaseline";
import GlobalStyles from "@mui/material/GlobalStyles";
import { ThemeProvider } from "@mui/material/styles";
import useMediaQuery from "@mui/material/useMediaQuery";
import { RefineThemes } from "@refinedev/mui";
import Cookies from "js-cookie";
import React, {
PropsWithChildren,
createContext,
useEffect,
useState,
} from "react";
type ColorModeContextType = {
mode: string;
setMode: () => void;
};
export const ColorModeContext = createContext<ColorModeContextType>(
{} as ColorModeContextType
);
type ColorModeContextProviderProps = {
defaultMode?: string;
};
export const ColorModeContextProvider: React.FC<
PropsWithChildren<ColorModeContextProviderProps>
> = ({ children, defaultMode }) => {
const [isMounted, setIsMounted] = useState(false);
const [mode, setMode] = useState(defaultMode || "light");
useEffect(() => {
setIsMounted(true);
}, []);
const systemTheme = useMediaQuery(`(prefers-color-scheme: dark)`);
useEffect(() => {
if (isMounted) {
const theme = Cookies.get("theme") || (systemTheme ? "dark" : "light");
setMode(theme);
}
}, [isMounted, systemTheme]);
const toggleTheme = () => {
const nextTheme = mode === "light" ? "dark" : "light";
setMode(nextTheme);
Cookies.set("theme", nextTheme);
};
return (
<ColorModeContext.Provider
value={{
setMode: toggleTheme,
mode,
}}
>
<ThemeProvider
// you can change the theme colors here. example: mode === "light" ? RefineThemes.Magenta : RefineThemes.MagentaDark
theme={mode === "light" ? RefineThemes.Blue : RefineThemes.BlueDark}
>
<CssBaseline />
<GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
{children}
</ThemeProvider>
</ColorModeContext.Provider>
);
};

View File

@ -1,15 +0,0 @@
import "@refinedev/mui";
export interface CustomTheme {
// Add custom variables here like below:
// status: {
// danger: string;
// };
}
declare module "@refinedev/mui" {
interface Theme extends import("@refinedev/mui").Theme, CustomTheme {}
interface ThemeOptions
extends import("@refinedev/mui").ThemeOptions,
CustomTheme {}
}

View File

@ -1,21 +0,0 @@
import { AuthBindings } from "@refinedev/core";
import { cookies } from "next/headers";
export const authProviderServer: Pick<AuthBindings, "check"> = {
check: async () => {
const cookieStore = cookies();
const auth = cookieStore.get("auth");
if (auth) {
return {
authenticated: true,
};
}
return {
authenticated: false,
logout: true,
redirectTo: "/login",
};
},
};

View File

@ -1,91 +0,0 @@
"use client";
import { AuthBindings } from "@refinedev/core";
import Cookies from "js-cookie";
const mockUsers = [
{
name: "John Doe",
email: "johndoe@mail.com",
roles: ["admin"],
avatar: "https://i.pravatar.cc/150?img=1",
},
{
name: "Jane Doe",
email: "janedoe@mail.com",
roles: ["editor"],
avatar: "https://i.pravatar.cc/150?img=1",
},
];
export const authProvider: AuthBindings = {
login: async ({ email, username, password, remember }) => {
// Suppose we actually send a request to the back end here.
const user = mockUsers[0];
if (user) {
Cookies.set("auth", JSON.stringify(user), {
expires: 30, // 30 days
path: "/",
});
return {
success: true,
redirectTo: "/",
};
}
return {
success: false,
error: {
name: "LoginError",
message: "Invalid username or password",
},
};
},
logout: async () => {
Cookies.remove("auth", { path: "/" });
return {
success: true,
redirectTo: "/login",
};
},
check: async () => {
const auth = Cookies.get("auth");
if (auth) {
return {
authenticated: true,
};
}
return {
authenticated: false,
logout: true,
redirectTo: "/login",
};
},
getPermissions: async () => {
const auth = Cookies.get("auth");
if (auth) {
const parsedUser = JSON.parse(auth);
return parsedUser.roles;
}
return null;
},
getIdentity: async () => {
const auth = Cookies.get("auth");
if (auth) {
const parsedUser = JSON.parse(auth);
return parsedUser;
}
return null;
},
onError: async (error) => {
if (error.response?.status === 401) {
return {
logout: true,
};
}
return { error };
},
};

View File

@ -1,2 +0,0 @@
export * from "./auth-provider";
export * from "./auth-provider.server";

View File

@ -1,7 +0,0 @@
"use client";
import dataProviderSimpleRest from "@refinedev/simple-rest";
const API_URL = "https://api.fake-rest.refine.dev";
export const dataProvider = dataProviderSimpleRest(API_URL);

Some files were not shown because too many files have changed in this diff Show More