diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Api/AIChat.AOAI.Api.csproj b/samples/AIChat.AOAI/AIChat.AOAI.Api/AIChat.AOAI.Api.csproj
new file mode 100644
index 00000000..cdd199ab
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Api/AIChat.AOAI.Api.csproj
@@ -0,0 +1,21 @@
+
+
+ net8.0
+ enable
+ true
+ preview
+ $(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;SKEXP0050;OPENAI001;SKEXP0010
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Api/Controllers/Generated/ChatController.cs b/samples/AIChat.AOAI/AIChat.AOAI.Api/Controllers/Generated/ChatController.cs
new file mode 100644
index 00000000..e85cf09e
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Api/Controllers/Generated/ChatController.cs
@@ -0,0 +1,47 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Api.Controllers;
+
+///
+/// Provides the Web API functionality.
+///
+[Consumes(System.Net.Mime.MediaTypeNames.Application.Json)]
+[Produces(System.Net.Mime.MediaTypeNames.Application.Json)]
+public partial class ChatController : ControllerBase
+{
+ private readonly WebApi _webApi;
+ private readonly IChatManager _manager;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ /// The .
+ public ChatController(WebApi webApi, IChatManager manager)
+ { _webApi = webApi.ThrowIfNull(); _manager = manager.ThrowIfNull(); ChatControllerCtor(); }
+
+ partial void ChatControllerCtor(); // Enables additional functionality to be added to the constructor.
+
+ ///
+ /// Creates a new Chat.
+ ///
+ /// The created Chat.
+ [HttpPost("chats", Name="Chat_Create")]
+ [AcceptsBody(typeof(Common.Entities.Chat))]
+ [ProducesResponseType(typeof(Common.Entities.Chat), (int)HttpStatusCode.Created)]
+ public Task Create()
+ => _webApi.PostWithResultAsync(Request, p => _manager.CreateAsync(p.Value!), statusCode: HttpStatusCode.Created);
+
+ ///
+ /// Chat Completion.
+ ///
+ /// The Message.
+ /// A resultant ChatCompletionResponse.
+ [HttpPost("chats/completion", Name="Chat_ChatCompletion")]
+ [ProducesResponseType(typeof(Common.Entities.ChatCompletionResponse), (int)HttpStatusCode.OK)]
+ [ProducesResponseType((int)HttpStatusCode.NoContent)]
+ public Task ChatCompletion(string? message)
+ => _webApi.PostWithResultAsync(Request, p => _manager.ChatCompletionAsync(message), alternateStatusCode: HttpStatusCode.NoContent, operationType: CoreEx.OperationType.Unspecified);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Api/Controllers/Generated/ReferenceDataController.cs b/samples/AIChat.AOAI/AIChat.AOAI.Api/Controllers/Generated/ReferenceDataController.cs
new file mode 100644
index 00000000..f0a83ce7
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Api/Controllers/Generated/ReferenceDataController.cs
@@ -0,0 +1,45 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+using CommonRefDataNamespace = AIChat.AOAI.Common.Entities;
+
+namespace AIChat.AOAI.Api.Controllers;
+
+///
+/// Provides the ReferenceData Web API functionality.
+///
+[Consumes(System.Net.Mime.MediaTypeNames.Application.Json)]
+[Produces(System.Net.Mime.MediaTypeNames.Application.Json)]
+public partial class ReferenceDataController : ControllerBase
+{
+ private readonly ReferenceDataContentWebApi _webApi;
+ private readonly ReferenceDataOrchestrator _orchestrator;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ /// The .
+ public ReferenceDataController(ReferenceDataContentWebApi webApi, ReferenceDataOrchestrator orchestrator)
+ { _webApi = webApi.ThrowIfNull(); _orchestrator = orchestrator.ThrowIfNull(); }
+
+ ///
+ /// Gets all of the 'Role' reference data items that match the specified criteria.
+ ///
+ /// The reference data code list.
+ /// The reference data text (including wildcards).
+ /// The 'Role' array.
+ [HttpGet("ref/roles", Name="ReferenceData_RoleGetAll")]
+ [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)]
+ public Task RoleGetAll([FromQuery] IEnumerable? codes = default, string? text = default)
+ => _webApi.GetAsync(Request, p => _orchestrator.GetWithFilterAsync(codes, text, p.RequestOptions.IncludeInactive));
+
+ ///
+ /// Gets the reference data entries for the specified entities and codes from the query string; e.g: ref?entity=codeX,codeY&entity2=codeZ&entity3
+ ///
+ /// The 'ReferenceDataMultiDictionary'.
+ [HttpGet("ref", Name="ReferenceData_GetNamed")]
+ [ProducesResponseType(typeof(CoreEx.RefData.ReferenceDataMultiDictionary), (int)HttpStatusCode.OK)]
+ public Task GetNamed() => _webApi.GetAsync(Request, p => _orchestrator.GetNamedAsync(p.RequestOptions));
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Api/GlobalUsings.cs b/samples/AIChat.AOAI/AIChat.AOAI.Api/GlobalUsings.cs
new file mode 100644
index 00000000..473a1b50
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Api/GlobalUsings.cs
@@ -0,0 +1,23 @@
+global using Azure.Monitor.OpenTelemetry.AspNetCore;
+global using CoreEx;
+global using CoreEx.AspNetCore.WebApis;
+global using CoreEx.AspNetCore.HealthChecks;
+global using CoreEx.Entities;
+global using CoreEx.Events;
+global using CoreEx.Http;
+global using CoreEx.RefData;
+global using CoreEx.Validation;
+global using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+global using Microsoft.AspNetCore.Mvc;
+global using Microsoft.Extensions.Caching.Memory;
+global using Microsoft.OpenApi.Models;
+global using OpenTelemetry.Trace;
+global using System.Net;
+global using System.Reflection;
+global using AIChat.AOAI.Api;
+global using AIChat.AOAI.Business;
+global using AIChat.AOAI.Business.Data;
+global using AIChat.AOAI.Business.Entities;
+//global using AIChat.AOAI.Business.Validation;
+global using RefDataNamespace = AIChat.AOAI.Business.Entities;
+global using AzCosmos = Microsoft.Azure.Cosmos;
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Api/Program.cs b/samples/AIChat.AOAI/AIChat.AOAI.Api/Program.cs
new file mode 100644
index 00000000..88e824d4
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Api/Program.cs
@@ -0,0 +1,27 @@
+using Microsoft.SemanticKernel;
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Configuration.AddEnvironmentVariables("AOAI_");
+
+var startup = new Startup();
+startup.ConfigureServices(builder.Services, builder.Environment);
+
+// Add the semantic kernel
+builder.Services.AddKernel()
+ .AddAzureOpenAIChatCompletion(
+ builder.Configuration["AzureOpenAI::ModelName"] ?? string.Empty,
+ builder.Configuration["AzureOpenAI::Endpoint"] ?? string.Empty,
+ builder.Configuration["AzureOpenAI::APIKey"] ?? string.Empty
+ );
+
+builder.Services.AddTransient((serviceProvider) => {
+ return new Kernel(serviceProvider);
+});
+
+if (!builder.Environment.IsDevelopment())
+ builder.Services.AddOpenTelemetry().UseAzureMonitor().WithTracing(b => b.AddSource("CoreEx.*", "AIChat.AOAI.*"));
+
+var app = builder.Build();
+startup.Configure(app);
+
+app.Run();
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Api/Startup.cs b/samples/AIChat.AOAI/AIChat.AOAI.Api/Startup.cs
new file mode 100644
index 00000000..06e02455
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Api/Startup.cs
@@ -0,0 +1,110 @@
+using Microsoft.SemanticKernel;
+
+namespace AIChat.AOAI.Api;
+
+///
+/// Represents the startup class.
+///
+public class Startup
+{
+ ///
+ /// The configure services method called by the runtime; use this method to add services to the container.
+ ///
+ /// The .
+ public void ConfigureServices(IServiceCollection services, IWebHostEnvironment env)
+ {
+ // Add the core services.
+ services.AddSettings()
+ .AddExecutionContext()
+ .AddJsonSerializer()
+ .AddReferenceDataOrchestrator()
+ .AddReferenceDataContentWebApi()
+ .AddWebApi()
+ .AddJsonMergePatch()
+ .AddRequestCache()
+ .AddValidationTextProvider()
+ .AddValidators()
+ .AddMappers()
+ .AddSingleton();
+
+ // Add the cosmos database.
+ services.AddSingleton(sp =>
+ {
+ var settings = sp.GetRequiredService();
+ var cco = new AzCosmos.CosmosClientOptions { SerializerOptions = new AzCosmos.CosmosSerializationOptions { PropertyNamingPolicy = AzCosmos.CosmosPropertyNamingPolicy.CamelCase, IgnoreNullValues = true } };
+ return new AzCosmos.CosmosClient(settings.CosmosConnectionString, cco);
+ }).AddCosmosDb(sp =>
+ {
+ var settings = sp.GetRequiredService();
+ return new AOAICosmosDb(sp.GetRequiredService().GetDatabase(settings.CosmosDatabaseId), sp.GetRequiredService());
+ });
+
+ // Add the generated reference data services.
+ services.AddGeneratedReferenceDataManagerServices()
+ .AddGeneratedReferenceDataDataSvcServices()
+ .AddGeneratedReferenceDataDataServices();
+
+ // Add the generated entity services.
+ services.AddGeneratedManagerServices()
+ .AddGeneratedDataSvcServices()
+ .AddGeneratedDataServices();
+
+ // Add the event publishing; this will need to be updated from the null publisher to the actual as appropriate.
+ services.AddEventDataFormatter()
+ .AddCloudEventSerializer()
+ .AddNullEventPublisher();
+
+ // Add controllers.
+ services.AddControllers();
+
+ // Add health checks.
+ services.AddHealthChecks();
+
+ // Add Azure monitor open telemetry.
+ if (!env.IsDevelopment())
+ services.AddOpenTelemetry().UseAzureMonitor().WithTracing(b => b.AddSource("CoreEx.*", "AIChat.AOAI.*", "Microsoft.EntityFrameworkCore.*", "EntityFrameworkCore.*"));
+
+ // Add the swagger capabilities.
+ services.AddSwaggerGen(options =>
+ {
+ options.SwaggerDoc("v1", new OpenApiInfo { Title = "AIChat.AOAI API", Version = "v1" });
+ options.OperationFilter(); // Needed to support AcceptsBodyAttribute where body parameter not explicitly defined.
+ options.OperationFilter(); // Needed to support PagingAttribute where PagingArgs parameter not explicitly defined.
+ options.OperationFilter(); // Needed to support QueryAttribute where QueryArgs parameter not explicitly defined.
+ });
+ }
+
+ ///
+ /// The configure method called by the runtime; use this method to configure the HTTP request pipeline.
+ ///
+ /// The .
+ public void Configure(IApplicationBuilder app)
+ {
+ // Handle any unhandled exceptions.
+ app.UseWebApiExceptionHandler();
+
+ // Authenticate the user.
+ //app.UseAuthentication();
+
+ // Add Swagger as an endpoint and to serve the swagger-ui to the pipeline.
+ app.UseSwagger();
+ app.UseSwaggerUI(c =>
+ {
+ c.RoutePrefix = ""; // Default as the root/home page.
+ c.SwaggerEndpoint("/swagger/v1/swagger.json", "AIChat.AOAI");
+ });
+
+ // Add execution context set up to the pipeline.
+ app.UseExecutionContext();
+ app.UseReferenceDataOrchestrator();
+
+ // Add health checks.
+ app.UseHealthChecks("/health");
+ app.UseHealthChecks("/health/detailed", new HealthCheckOptions { ResponseWriter = HealthReportStatusWriter.WriteJsonResults }); // Secure with permissions / or remove given data returned.
+
+ // Use controllers.
+ app.UseRouting();
+ //app.UseAuthorization();
+ app.UseEndpoints(endpoints => endpoints.MapControllers());
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Api/appsettings.json b/samples/AIChat.AOAI/AIChat.AOAI.Api/appsettings.json
new file mode 100644
index 00000000..db156771
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Api/appsettings.json
@@ -0,0 +1,27 @@
+{
+ "AllowedHosts": "*",
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information"
+ }
+ },
+ // Set using environment variables: 'AOAI_CosmosConnectionString' and 'AOAI_CosmosDatabaseId'.
+ "CosmosConnectionString": "AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==;AccountEndpoint=https://localhost:8081",
+ "CosmosDatabaseId": "Chats",
+ "Invokers": {
+ "Default": {
+ "TracingEnabled": true,
+ "LoggingEnabled": true
+ },
+ "CoreEx.Validation.ValidationInvoker": {
+ "TracingEnabled": false,
+ "LoggingEnabled": false
+ }
+ },
+ "AzureOpenAI:": {
+ "ModelName": "",
+ "Endpoint": "https://.openai.azure.com/",
+ "APIKey": ""
+ },
+ "IncludeExceptionInResult": true
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/AIChat.AOAI.Business.csproj b/samples/AIChat.AOAI/AIChat.AOAI.Business/AIChat.AOAI.Business.csproj
new file mode 100644
index 00000000..4128a037
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/AIChat.AOAI.Business.csproj
@@ -0,0 +1,19 @@
+
+
+ net8.0
+ enable
+ enable
+ preview
+ $(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;SKEXP0050;OPENAI001;SKEXP0010
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/AOAISettings.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/AOAISettings.cs
new file mode 100644
index 00000000..16c891e0
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/AOAISettings.cs
@@ -0,0 +1,19 @@
+namespace AIChat.AOAI.Business;
+
+///
+/// Provides the settings.
+///
+/// The .
+public class AOAISettings(IConfiguration configuration) : SettingsBase(configuration, ["AOAI/", "Common/"])
+{
+
+ ///
+ /// Gets the CosmosDB connection string.
+ ///
+ public string CosmosConnectionString => GetRequiredValue();
+
+ ///
+ /// Gtes the CosmosDB database identifier.
+ ///
+ public string CosmosDatabaseId => GetRequiredValue();
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/AOAICosmosDb.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/AOAICosmosDb.cs
new file mode 100644
index 00000000..833c2fc4
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/AOAICosmosDb.cs
@@ -0,0 +1,33 @@
+using Microsoft.Azure.Cosmos;
+
+namespace AIChat.AOAI.Business.Data;
+
+///
+/// Provides the AIChat.AOAI CosmosDb client.
+///
+/// The .
+/// The .
+public class AOAICosmosDb : CosmosDb
+{
+ ///
+ /// Gets the Person container identifier.
+ ///
+ public const string ChatContainerId = "Chats";
+
+ private readonly Lazy> _chats;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The CosmosDb .
+ /// The .
+ public AOAICosmosDb(Database database, IMapper mapper) : base(database, mapper)
+ {
+ _chats = new(() => Container(ChatContainerId));
+ }
+
+ ///
+ /// Exposes entity from the container.
+ ///
+ public CosmosDbContainer Chats => _chats.Value;
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/ChatData.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/ChatData.cs
new file mode 100644
index 00000000..2dbe1448
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/ChatData.cs
@@ -0,0 +1,47 @@
+using Microsoft.Azure.Cosmos;
+
+namespace AIChat.AOAI.Business.Data
+{
+ public partial class ChatData
+ {
+ partial void ChatDataCtor()
+ {
+ }
+
+ ///
+ /// Performs the query filtering
+ ///
+ ///
+ ///
+ private async Task> GetChatCompletionsOnImplementationAsync(int completions)
+ {
+ try
+ {
+ string query = """
+ SELECT TOP @completions *
+ FROM c
+ ORDER BY c._ts DESC
+ """;
+
+ var queryDef = new QueryDefinition(query)
+ .WithParameter("@completions", completions);
+ using FeedIterator resultSet = _cosmos.Chats.Container.GetItemQueryIterator(queryDef);
+ List chats = [];
+ while (resultSet.HasMoreResults)
+ {
+ FeedResponse response = await resultSet.ReadNextAsync();
+ chats.AddRange(response);
+ }
+
+ var result = new ChatCollectionResult(chats);
+
+ return Result.Ok(result);
+ }
+ catch (Exception ex)
+ {
+ return Result.Fail(ex.Message);
+ }
+
+ }
+ }
+}
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ChatData.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ChatData.cs
new file mode 100644
index 00000000..958042a7
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ChatData.cs
@@ -0,0 +1,71 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.Data;
+
+///
+/// Provides the data access.
+///
+public partial class ChatData : IChatData
+{
+ private readonly AOAICosmosDb _cosmos;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ public ChatData(AOAICosmosDb cosmos)
+ { _cosmos = cosmos.ThrowIfNull(); ChatDataCtor(); }
+
+ partial void ChatDataCtor(); // Enables additional functionality to be added to the constructor.
+
+ ///
+ public Task> CreateAsync(Chat value)
+ => _cosmos.Chats.CreateWithResultAsync(value);
+
+ ///
+ public Task> GetChatCompletionsAsync(int completions) => GetChatCompletionsOnImplementationAsync(completions);
+
+ ///
+ /// Provides the to Cosmos mapping.
+ ///
+ public partial class EntityToModelCosmosMapper : Mapper
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EntityToModelCosmosMapper()
+ {
+ Map((s, d) => d.Id = TypeToStringConverter.Default.ConvertToDestination(s.Id), OperationTypes.Any, s => s.Id == default, d => d.Id = default);
+ Map((s, d) => d.Role = s.RoleSid, OperationTypes.Any, s => s.RoleSid == default, d => d.Role = default);
+ Map((s, d) => d.Prompt = s.Prompt, OperationTypes.Any, s => s.Prompt == default, d => d.Prompt = default);
+ Map((s, d) => d.ETag = s.ETag, OperationTypes.Any, s => s.ETag == default, d => d.ETag = default);
+ Map((o, s, d) => d.ChangeLog = o.Map(s.ChangeLog, d.ChangeLog), OperationTypes.Any, s => s.ChangeLog == default, d => d.ChangeLog = default);
+ EntityToModelCosmosMapperCtor();
+ }
+
+ partial void EntityToModelCosmosMapperCtor(); // Enables the constructor to be extended.
+ }
+
+ ///
+ /// Provides the Cosmos to mapping.
+ ///
+ public partial class ModelToEntityCosmosMapper : Mapper
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ModelToEntityCosmosMapper()
+ {
+ Map((s, d) => d.Id = (Guid)TypeToStringConverter.Default.ConvertToSource(s.Id!), OperationTypes.Any, s => s.Id == default, d => d.Id = default);
+ Map((s, d) => d.RoleSid = (string?)s.Role!, OperationTypes.Any, s => s.Role == default, d => d.RoleSid = default);
+ Map((s, d) => d.Prompt = (string?)s.Prompt!, OperationTypes.Any, s => s.Prompt == default, d => d.Prompt = default);
+ Map((s, d) => d.ETag = (string?)s.ETag!, OperationTypes.Any, s => s.ETag == default, d => d.ETag = default);
+ Map((o, s, d) => d.ChangeLog = o.Map(s.ChangeLog, d.ChangeLog), OperationTypes.Any, s => s.ChangeLog == default, d => d.ChangeLog = default);
+ ModelToEntityCosmosMapperCtor();
+ }
+
+ partial void ModelToEntityCosmosMapperCtor(); // Enables the constructor to be extended.
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/IChatData.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/IChatData.cs
new file mode 100644
index 00000000..c5d14208
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/IChatData.cs
@@ -0,0 +1,25 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.Data;
+
+///
+/// Defines the data access.
+///
+public partial interface IChatData
+{
+ ///
+ /// Creates a new .
+ ///
+ /// The .
+ /// The created .
+ Task> CreateAsync(Chat value);
+
+ ///
+ /// Get Chat Completions.
+ ///
+ /// The Completions.
+ /// A resultant .
+ Task> GetChatCompletionsAsync(int completions);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/IReferenceDataData.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/IReferenceDataData.cs
new file mode 100644
index 00000000..bc5b01b2
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/IReferenceDataData.cs
@@ -0,0 +1,17 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.Data;
+
+///
+/// Provides the ReferenceData data access.
+///
+public partial interface IReferenceDataData
+{
+ ///
+ /// Gets all the items.
+ ///
+ /// The .
+ Task> RoleGetAllAsync();
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ReferenceDataData.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ReferenceDataData.cs
new file mode 100644
index 00000000..f4e796cc
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ReferenceDataData.cs
@@ -0,0 +1,70 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.Data;
+
+///
+/// Provides the ReferenceData data access.
+///
+public partial class ReferenceDataData : IReferenceDataData
+{
+ private readonly AOAICosmosDb _cosmos;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ public ReferenceDataData(AOAICosmosDb cosmos)
+ { _cosmos = cosmos.ThrowIfNull(); ReferenceDataDataCtor(); }
+
+ partial void ReferenceDataDataCtor(); // Enables additional functionality to be added to the constructor.
+
+ ///
+ public Task> RoleGetAllAsync()
+ => DataInvoker.Current.InvokeAsync(this, (_, ct) => _cosmos.ValueContainer("RefData").Query().SelectQueryWithResultAsync(ct));
+
+ ///
+ /// Provides the to Entity Framework mapping.
+ ///
+ public partial class RoleToModelCosmosMapper : Mapper
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RoleToModelCosmosMapper()
+ {
+ Map((s, d) => d.Id = s.Id, OperationTypes.Any, s => s.Id == default, d => d.Id = default);
+ Map((s, d) => d.Code = s.Code, OperationTypes.Any, s => s.Code == default, d => d.Code = default);
+ Map((s, d) => d.Text = s.Text, OperationTypes.Any, s => s.Text == default, d => d.Text = default);
+ Map((s, d) => d.IsActive = s.IsActive, OperationTypes.Any, s => s.IsActive == default, d => d.IsActive = default);
+ Map((s, d) => d.SortOrder = s.SortOrder, OperationTypes.Any, s => s.SortOrder == default, d => d.SortOrder = default);
+ Map((s, d) => d.ETag = s.ETag, OperationTypes.Any, s => s.ETag == default, d => d.ETag = default);
+ RoleToModelCosmosMapperCtor();
+ }
+
+ partial void RoleToModelCosmosMapperCtor(); // Enables the constructor to be extended.
+ }
+
+ ///
+ /// Provides the Entity Framework to mapping.
+ ///
+ public partial class ModelToRoleCosmosMapper : Mapper
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ModelToRoleCosmosMapper()
+ {
+ Map((s, d) => d.Id = (Guid)s.Id!, OperationTypes.Any, s => s.Id == default, d => d.Id = default);
+ Map((s, d) => d.Code = (string?)s.Code!, OperationTypes.Any, s => s.Code == default, d => d.Code = default);
+ Map((s, d) => d.Text = (string?)s.Text!, OperationTypes.Any, s => s.Text == default, d => d.Text = default);
+ Map((s, d) => d.IsActive = (bool)s.IsActive!, OperationTypes.Any, s => s.IsActive == default, d => d.IsActive = default);
+ Map((s, d) => d.SortOrder = (int)s.SortOrder!, OperationTypes.Any, s => s.SortOrder == default, d => d.SortOrder = default);
+ Map((s, d) => d.ETag = (string?)s.ETag!, OperationTypes.Any, s => s.ETag == default, d => d.ETag = default);
+ ModelToRoleCosmosMapperCtor();
+ }
+
+ partial void ModelToRoleCosmosMapperCtor(); // Enables the constructor to be extended.
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ReferenceDataServiceCollectionExtensions.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ReferenceDataServiceCollectionExtensions.cs
new file mode 100644
index 00000000..8ba2a130
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ReferenceDataServiceCollectionExtensions.cs
@@ -0,0 +1,21 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// Provides the generated Data-layer services.
+///
+public static partial class ReferenceDataServiceCollectionsExtension
+{
+ ///
+ /// Adds the generated Data-layer services.
+ ///
+ /// The .
+ /// The .
+ public static IServiceCollection AddGeneratedReferenceDataDataServices(this IServiceCollection services)
+ {
+ return services.AddScoped();
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ServiceCollectionExtensions.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ServiceCollectionExtensions.cs
new file mode 100644
index 00000000..b6f971b1
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Generated/ServiceCollectionExtensions.cs
@@ -0,0 +1,21 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// Provides the generated Data-layer services.
+///
+public static partial class ServiceCollectionsExtension
+{
+ ///
+ /// Adds the generated Data-layer services.
+ ///
+ /// The .
+ /// The .
+ public static IServiceCollection AddGeneratedDataServices(this IServiceCollection services)
+ {
+ return services.AddScoped();
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Model/Generated/Chat.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Model/Generated/Chat.cs
new file mode 100644
index 00000000..3f0e5e8e
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Model/Generated/Chat.cs
@@ -0,0 +1,63 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.Data.Model;
+
+///
+/// Represents the Chat model for data persistence model.
+///
+public partial class Chat : IIdentifier, IETag, IChangeLog
+{
+ ///
+ /// Gets or sets the identifier.
+ ///
+ public string? Id { get; set; }
+
+ ///
+ /// Gets or sets the Role.
+ ///
+ public string? Role { get; set; }
+
+ ///
+ /// Gets or sets the Prompt.
+ ///
+ public string? Prompt { get; set; }
+
+ ///
+ /// Gets or sets the Completion.
+ ///
+ public string? Completion { get; set; }
+
+ ///
+ /// Gets or sets the Completion Tokens.
+ ///
+ public int CompletionTokens { get; set; }
+
+ ///
+ /// Gets or sets the Prompt Tokens.
+ ///
+ public int PromptTokens { get; set; }
+
+ ///
+ /// Gets or sets the Total Tokens.
+ ///
+ public int TotalTokens { get; set; }
+
+ ///
+ /// Gets or sets the Model.
+ ///
+ public string? Model { get; set; }
+
+ ///
+ /// Gets or sets the ETag.
+ ///
+ [Newtonsoft.Json.JsonProperty("_etag")]
+ [JsonPropertyName("_etag")]
+ public string? ETag { get; set; }
+
+ ///
+ /// Gets or sets the Change Log.
+ ///
+ public ChangeLog? ChangeLog { get; set; }
+}
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Model/Generated/Role.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Model/Generated/Role.cs
new file mode 100644
index 00000000..25dafb46
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Data/Model/Generated/Role.cs
@@ -0,0 +1,15 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.Data.Model;
+
+///
+/// Represents the Role model.
+///
+public partial class Role : ReferenceDataBase { }
+
+///
+/// Represents the collection.
+///
+public partial class RoleCollection : List { }
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ChatDataSvc.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ChatDataSvc.cs
new file mode 100644
index 00000000..8f72f4d8
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ChatDataSvc.cs
@@ -0,0 +1,37 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.DataSvc;
+
+///
+/// Provides the data repository services.
+///
+public partial class ChatDataSvc : IChatDataSvc
+{
+ private readonly IChatData _data;
+ private readonly IEventPublisher _events;
+ private readonly IRequestCache _cache;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ /// The .
+ /// The .
+ public ChatDataSvc(IChatData data, IEventPublisher events, IRequestCache cache)
+ { _data = data.ThrowIfNull(); _events = events.ThrowIfNull(); _cache = cache.ThrowIfNull(); ChatDataSvcCtor(); }
+
+ partial void ChatDataSvcCtor(); // Enables additional functionality to be added to the constructor.
+
+ ///
+ public Task> CreateAsync(Chat value) => DataSvcInvoker.Current.InvokeAsync(this, (_, __) =>
+ {
+ return Result.GoAsync(_data.CreateAsync(value))
+ .Then(r => _events.PublishValueEvent(r, new Uri($"aichat/aoai/chat/{r.Id}", UriKind.Relative), $"AIChat.AOAI.Chat", "Created"))
+ .CacheSet(_cache);
+ }, new InvokerArgs { EventPublisher = _events });
+
+ ///
+ public Task> GetChatCompletionsAsync(int completions) => _data.GetChatCompletionsAsync(completions);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/IChatDataSvc.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/IChatDataSvc.cs
new file mode 100644
index 00000000..27bf27f6
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/IChatDataSvc.cs
@@ -0,0 +1,25 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.DataSvc;
+
+///
+/// Defines the data repository services.
+///
+public partial interface IChatDataSvc
+{
+ ///
+ /// Creates a new .
+ ///
+ /// The .
+ /// The created .
+ Task> CreateAsync(Chat value);
+
+ ///
+ /// Get Chat Completions.
+ ///
+ /// The Completions.
+ /// A resultant .
+ Task> GetChatCompletionsAsync(int completions);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/IReferenceDataDataSvc.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/IReferenceDataDataSvc.cs
new file mode 100644
index 00000000..0a0949a3
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/IReferenceDataDataSvc.cs
@@ -0,0 +1,18 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.DataSvc;
+
+///
+/// Provides the ReferenceData data services.
+///
+public partial interface IReferenceDataDataSvc
+{
+ ///
+ /// Gets the for the specified .
+ ///
+ /// The .
+ /// The corresponding .
+ Task> GetAsync(Type type);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ReferenceDataDataSvc.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ReferenceDataDataSvc.cs
new file mode 100644
index 00000000..b8c15fed
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ReferenceDataDataSvc.cs
@@ -0,0 +1,28 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.DataSvc;
+
+///
+/// Provides the ReferenceData data services.
+///
+public partial class ReferenceDataDataSvc : IReferenceDataDataSvc
+{
+ private readonly IReferenceDataData _data;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ public ReferenceDataDataSvc(IReferenceDataData data) { _data = data ?? throw new ArgumentNullException(nameof(data)); ReferenceDataDataSvcCtor(); }
+
+ partial void ReferenceDataDataSvcCtor(); // Enables the ReferenceDataDataSvc constructor to be extended.
+
+ ///
+ public Task> GetAsync(Type type) => type switch
+ {
+ Type _ when type == typeof(RefDataNamespace.Role) => Result.GoAsync(_data.RoleGetAllAsync()).ThenAs(v => (IReferenceDataCollection)v),
+ _ => throw new InvalidOperationException($"Type {type.FullName} is not a known {nameof(IReferenceData)}.")
+ };
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ReferenceDataServiceCollectionExtensions.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ReferenceDataServiceCollectionExtensions.cs
new file mode 100644
index 00000000..73d0a7c1
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ReferenceDataServiceCollectionExtensions.cs
@@ -0,0 +1,21 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// Provides the generated DataSvc-layer services.
+///
+public static partial class ReferenceDataServiceCollectionsExtension
+{
+ ///
+ /// Adds the generated DataSvc-layer services.
+ ///
+ /// The .
+ /// The .
+ public static IServiceCollection AddGeneratedReferenceDataDataSvcServices(this IServiceCollection services)
+ {
+ return services.AddScoped();
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ServiceCollectionExtensions.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ServiceCollectionExtensions.cs
new file mode 100644
index 00000000..c0ff5919
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/DataSvc/Generated/ServiceCollectionExtensions.cs
@@ -0,0 +1,21 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// Provides the generated DataSvc-layer services.
+///
+public static partial class ServiceCollectionsExtension
+{
+ ///
+ /// Adds the generated DataSvc-layer services.
+ ///
+ /// The .
+ /// The .
+ public static IServiceCollection AddGeneratedDataSvcServices(this IServiceCollection services)
+ {
+ return services.AddScoped();
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Entities/Generated/Chat.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Entities/Generated/Chat.cs
new file mode 100644
index 00000000..7532089b
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Entities/Generated/Chat.cs
@@ -0,0 +1,107 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.Entities;
+
+///
+/// Represents the Chat entity.
+///
+public partial class Chat : EntityBase, IIdentifier, IETag, IChangeLogEx
+{
+ private Guid _id;
+ private string? _roleSid;
+ private string? _prompt;
+ private string? _etag;
+ private ChangeLogEx? _changeLog;
+
+ ///
+ /// Gets or sets the identifier.
+ ///
+ public Guid Id { get => _id; set => SetValue(ref _id, value); }
+
+ ///
+ /// Gets or sets the using the underlying Serialization Identifier (SID).
+ ///
+ [JsonPropertyName("role")]
+ public string? RoleSid { get => _roleSid; set => SetValue(ref _roleSid, value, propertyName: nameof(Role)); }
+
+ ///
+ /// Gets the corresponding text (read-only where selected).
+ ///
+ public string? RoleText => RefDataNamespace.Role.GetRefDataText(_roleSid);
+
+ ///
+ /// Gets or sets the Role (see ).
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ [JsonIgnore]
+ public RefDataNamespace.Role? Role { get => _roleSid; set => SetValue(ref _roleSid, value); }
+
+ ///
+ /// Gets or sets the Prompt.
+ ///
+ public string? Prompt { get => _prompt; set => SetValue(ref _prompt, value); }
+
+ ///
+ /// Gets or sets the ETag.
+ ///
+ [JsonPropertyName("etag")]
+ public string? ETag { get => _etag; set => SetValue(ref _etag, value); }
+
+ ///
+ /// Gets or sets the Change Log (see ).
+ ///
+ public ChangeLogEx? ChangeLog { get => _changeLog; set => SetValue(ref _changeLog, value); }
+
+ ///
+ protected override IEnumerable GetPropertyValues()
+ {
+ yield return CreateProperty(nameof(Id), Id, v => Id = v);
+ yield return CreateProperty(nameof(Role), RoleSid, v => RoleSid = v);
+ yield return CreateProperty(nameof(Prompt), Prompt, v => Prompt = v);
+ yield return CreateProperty(nameof(ETag), ETag, v => ETag = v);
+ yield return CreateProperty(nameof(ChangeLog), ChangeLog, v => ChangeLog = v);
+ }
+}
+
+///
+/// Represents the collection.
+///
+public partial class ChatCollection : EntityBaseCollection
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ChatCollection() { }
+
+ ///
+ /// Initializes a new instance of the class with to add.
+ ///
+ /// The items to add.
+ public ChatCollection(IEnumerable items) => AddRange(items);
+}
+
+///
+/// Represents the collection result.
+///
+public class ChatCollectionResult : EntityCollectionResult
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ChatCollectionResult() { }
+
+ ///
+ /// Initializes a new instance of the class with .
+ ///
+ /// The .
+ public ChatCollectionResult(PagingArgs? paging) : base(paging) { }
+
+ ///
+ /// Initializes a new instance of the class with to add.
+ ///
+ /// The items to add.
+ /// The optional .
+ public ChatCollectionResult(IEnumerable items, PagingArgs? paging = null) : base(paging) => Items.AddRange(items);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Entities/Generated/ChatCompletionResponse.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Entities/Generated/ChatCompletionResponse.cs
new file mode 100644
index 00000000..9b395738
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Entities/Generated/ChatCompletionResponse.cs
@@ -0,0 +1,24 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.Entities;
+
+///
+/// Represents the Completion Response entity.
+///
+public partial class ChatCompletionResponse : EntityBase
+{
+ private string? _message;
+
+ ///
+ /// Gets or sets the Message.
+ ///
+ public string? Message { get => _message; set => SetValue(ref _message, value); }
+
+ ///
+ protected override IEnumerable GetPropertyValues()
+ {
+ yield return CreateProperty(nameof(Message), Message, v => Message = v);
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Entities/Generated/Role.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Entities/Generated/Role.cs
new file mode 100644
index 00000000..0bdb164c
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Entities/Generated/Role.cs
@@ -0,0 +1,43 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business.Entities;
+
+///
+/// Represents the Role entity.
+///
+public partial class Role : ReferenceDataBaseEx
+{
+ ///
+ /// An implicit cast from an to .
+ ///
+ /// The .
+ /// The corresponding .
+ public static implicit operator Role?(Guid id) => ConvertFromId(id);
+
+ ///
+ /// An implicit cast from a to .
+ ///
+ /// The .
+ /// The corresponding .
+ [return: NotNullIfNotNull(nameof(code))]
+ public static implicit operator Role?(string? code) => ConvertFromCode(code);
+}
+
+///
+/// Represents the collection.
+///
+public partial class RoleCollection : ReferenceDataCollectionBase
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RoleCollection() { }
+
+ ///
+ /// Initializes a new instance of the class with to add.
+ ///
+ /// The items to add.
+ public RoleCollection(IEnumerable items) => AddRange(items);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ChatManager.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ChatManager.cs
new file mode 100644
index 00000000..003fe50f
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ChatManager.cs
@@ -0,0 +1,41 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business;
+
+///
+/// Provides the business functionality.
+///
+public partial class ChatManager : IChatManager
+{
+ private readonly IChatDataSvc _dataService;
+ private readonly Kernel _kernel;
+ private readonly IIdentifierGenerator _identifierGenerator;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ /// The .
+ /// The .
+ public ChatManager(IChatDataSvc dataService, Kernel kernel, IIdentifierGenerator identifierGenerator)
+ { _dataService = dataService.ThrowIfNull(); _kernel = kernel.ThrowIfNull(); _identifierGenerator = identifierGenerator.ThrowIfNull(); ChatManagerCtor(); }
+
+ partial void ChatManagerCtor(); // Enables additional functionality to be added to the constructor.
+
+ ///
+ public Task> CreateAsync(Chat value) => ManagerInvoker.Current.InvokeAsync(this, (_, ct) =>
+ {
+ return Result.Go(value).Required()
+ .AdjustsAsync(async v => v.Id = await _identifierGenerator.GenerateIdentifierAsync().ConfigureAwait(false))
+ .ThenAsAsync(v => _dataService.CreateAsync(v));
+ }, InvokerArgs.Create);
+
+ ///
+ public Task> ChatCompletionAsync(string? message) => ManagerInvoker.Current.InvokeAsync(this, (_, ct) =>
+ {
+ return Result.Go()
+ .ThenAsAsync(() => ChatCompletionOnImplementationAsync(message));
+ }, InvokerArgs.Unspecified);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/IChatManager.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/IChatManager.cs
new file mode 100644
index 00000000..e4a610a7
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/IChatManager.cs
@@ -0,0 +1,25 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business;
+
+///
+/// Defines the business functionality.
+///
+public partial interface IChatManager
+{
+ ///
+ /// Creates a new .
+ ///
+ /// The .
+ /// The created .
+ Task> CreateAsync(Chat value);
+
+ ///
+ /// Chat Completion.
+ ///
+ /// The Message.
+ /// A resultant .
+ Task> ChatCompletionAsync(string? message);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ReferenceDataProvider.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ReferenceDataProvider.cs
new file mode 100644
index 00000000..e4baf5fe
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ReferenceDataProvider.cs
@@ -0,0 +1,30 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Business;
+
+///
+/// Provides the implementation using the corresponding data services.
+///
+public partial class ReferenceDataProvider : IReferenceDataProvider
+{
+ private readonly IReferenceDataDataSvc _dataService;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ public ReferenceDataProvider(IReferenceDataDataSvc dataService) { _dataService = dataService.ThrowIfNull(); ReferenceDataProviderCtor(); }
+
+ partial void ReferenceDataProviderCtor(); // Enables the ReferenceDataProvider constructor to be extended.
+
+ ///
+ public Type[] Types =>
+ [
+ typeof(RefDataNamespace.Role)
+ ];
+
+ ///
+ public Task> GetAsync(Type type, CancellationToken cancellationToken = default) => _dataService.GetAsync(type);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ReferenceDataServiceCollectionExtensions.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ReferenceDataServiceCollectionExtensions.cs
new file mode 100644
index 00000000..2fc5acb4
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ReferenceDataServiceCollectionExtensions.cs
@@ -0,0 +1,19 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// Provides the generated Manager-layer services.
+///
+public static partial class ReferenceDataServiceCollectionsExtension
+{
+ ///
+ /// Adds the generated Manager-layer services.
+ ///
+ /// The .
+ /// The .
+ public static IServiceCollection AddGeneratedReferenceDataManagerServices(this IServiceCollection services)
+ => services.AddScoped();
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ServiceCollectionExtensions.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ServiceCollectionExtensions.cs
new file mode 100644
index 00000000..3eefe5d9
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Generated/ServiceCollectionExtensions.cs
@@ -0,0 +1,21 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// Provides the generated Manager-layer services.
+///
+public static partial class ServiceCollectionsExtension
+{
+ ///
+ /// Adds the generated Manager-layer services.
+ ///
+ /// The .
+ /// The .
+ public static IServiceCollection AddGeneratedManagerServices(this IServiceCollection services)
+ {
+ return services.AddScoped();
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/GlobalUsings.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/GlobalUsings.cs
new file mode 100644
index 00000000..e11dfacb
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/GlobalUsings.cs
@@ -0,0 +1,40 @@
+global using CoreEx;
+global using CoreEx.Caching;
+global using CoreEx.Configuration;
+global using CoreEx.Cosmos;
+global using CoreEx.Data.Querying;
+global using CoreEx.Entities;
+global using CoreEx.Entities.Extended;
+global using CoreEx.Events;
+global using CoreEx.Http;
+global using CoreEx.Http.Extended;
+global using CoreEx.Invokers;
+global using CoreEx.Json;
+global using CoreEx.Mapping;
+global using CoreEx.Mapping.Converters;
+global using CoreEx.RefData;
+global using CoreEx.RefData.Extended;
+global using CoreEx.Results;
+global using CoreEx.Validation;
+global using CoreEx.Validation.Rules;
+global using AzCosmos = Microsoft.Azure.Cosmos;
+global using Microsoft.Extensions.Configuration;
+global using Microsoft.Extensions.Logging;
+global using System;
+global using System.Collections.Generic;
+global using System.Diagnostics;
+global using System.Diagnostics.CodeAnalysis;
+global using System.Linq;
+global using System.Text.Json.Serialization;
+global using System.Text.RegularExpressions;
+global using System.Net.Http;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using AIChat.AOAI.Business;
+global using AIChat.AOAI.Business.Entities;
+global using AIChat.AOAI.Business.Data;
+global using AIChat.AOAI.Business.DataSvc;
+//global using AIChat.AOAI.Business.Validation;
+global using RefDataNamespace = AIChat.AOAI.Business.Entities;
+global using ExecutionContext = CoreEx.ExecutionContext;
+global using Microsoft.SemanticKernel;
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Business/Manager/ChatManager.cs b/samples/AIChat.AOAI/AIChat.AOAI.Business/Manager/ChatManager.cs
new file mode 100644
index 00000000..4b570783
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Business/Manager/ChatManager.cs
@@ -0,0 +1,112 @@
+using Microsoft.SemanticKernel.ChatCompletion;
+
+namespace AIChat.AOAI.Business
+{
+ public partial class ChatManager
+ {
+ ///
+ /// Chat completion
+ ///
+ ///
+ ///
+ private async Task> ChatCompletionOnImplementationAsync(string? message)
+ {
+ ChatCompletionResponse result = new();
+ try
+ {
+ var histories = await _dataService.GetChatCompletionsAsync(3);
+ if (histories.IsFailure)
+ {
+ histories = new();
+ }
+ var generateMessage = await GenerateCompletion(message ?? string.Empty, histories);
+
+ await _dataService.CreateAsync(
+ new()
+ {
+ RoleSid = "User",
+ Prompt = message,
+ }
+ );
+
+ await _dataService.CreateAsync(
+ new()
+ {
+ RoleSid = "Assistant",
+ Prompt = generateMessage,
+ }
+ );
+
+ result.Message = generateMessage;
+ return Result.Ok(result);
+ }
+ catch (Exception ex)
+ {
+ return Result.Fail(ex.Message);
+ }
+ }
+
+ ///
+ /// Generate completion
+ ///
+ ///
+ ///
+ private async Task GenerateCompletion(string message, ChatCollectionResult? histories)
+ {
+ string systemPrompt = """
+ You are an intelligent assistant.You are designed to provide helpful answers to user questions.
+ You are friendly, and informative and can be lighthearted. Be concise in your response, but still friendly.
+ """;
+
+ // Create the completion
+ var chatCompletionService = _kernel.GetRequiredService();
+ ChatHistory history = [];
+ history.Add(
+ new()
+ {
+ Role = AuthorRole.System,
+ Content = systemPrompt,
+ }
+ );
+
+ if (histories != null && histories.Items.Count > 0)
+ {
+ foreach (var item in histories.Items)
+ {
+ if (item == null) continue;
+
+ history.Add(
+ new()
+ {
+ Role = item.Role?.Code switch
+ {
+ "User" => AuthorRole.User,
+ "System" => AuthorRole.System,
+ "Assistant" => AuthorRole.Assistant,
+ _ => AuthorRole.System,
+ },
+ Items = [
+ new TextContent { Text = item.Prompt }
+ ]
+ }
+ );
+ }
+ }
+
+ history.Add(
+ new()
+ {
+ Role = AuthorRole.User,
+ AuthorName = CoreEx.ExecutionContext.Current.UserName,
+ Items = [
+ new TextContent { Text = message },
+ ]
+ }
+ );
+
+ var response = await chatCompletionService.GetChatMessageContentAsync(chatHistory: history, kernel: _kernel);
+
+ return response.Content ?? string.Empty;
+ }
+ }
+}
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/AIChat.AOAI.CodeGen.csproj b/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/AIChat.AOAI.CodeGen.csproj
new file mode 100644
index 00000000..990cef75
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/AIChat.AOAI.CodeGen.csproj
@@ -0,0 +1,12 @@
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ preview
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/Program.cs b/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/Program.cs
new file mode 100644
index 00000000..f6b8aad0
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/Program.cs
@@ -0,0 +1,15 @@
+namespace AIChat.AOAI.CodeGen;
+
+///
+/// Represents the code generation program (capability).
+///
+[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+public static class Program
+{
+ ///
+ /// Main startup.
+ ///
+ /// The startup arguments.
+ /// The status code whereby zero indicates success.
+ public static Task Main(string[] args) => Beef.CodeGen.CodeGenConsole.Create("AIChat", "AOAI").Supports(entity: true, refData: true, dataModel: true).RunAsync(args);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/datamodel.beef-5.yaml b/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/datamodel.beef-5.yaml
new file mode 100644
index 00000000..2dadc1ac
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/datamodel.beef-5.yaml
@@ -0,0 +1,13 @@
+# Defines the internal model (persisted in Cosmos).
+jsonSerializer: Newtonsoft
+etagJsonName: _etag
+entities:
+- { name: Chat, text: Chat model for data persistence,
+ properties: [
+ { name: Id, text: '{{Chat}} identifier', type: string, primaryKey: true },
+ { name: Role },
+ { name: Prompt },
+ { name: ETag, },
+ { name: ChangeLog, type: ChangeLog }
+ ]
+ }
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/entity.beef-5.yaml b/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/entity.beef-5.yaml
new file mode 100644
index 00000000..e9c3aa59
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/entity.beef-5.yaml
@@ -0,0 +1,70 @@
+cosmosType: AOAICosmosDb
+eventSubjectRoot: AIChat
+eventActionFormat: PastTense
+eventSourceRoot: AIChat/AOAI
+eventSourceKind: Relative
+webApiAutoLocation: true
+autoImplement: Cosmos
+refDataText: true
+entities:
+ # The following is an example Entity with CRUD and Query operations defined accessing a Cosmos DB.
+
+- {
+ name: Chat,
+ collection: true,
+ collectionResult: true,
+ identifierGenerator: true,
+ webApiRoutePrefix: chats,
+ behavior: c,
+ cosmosContainerId: Chats,
+ cosmosModel: Model.Chat,
+ managerCtorParams: [
+ "Kernel",
+ ],
+ properties: [
+ { name: Id, type: Guid, primaryKey: true, dataConverter: 'TypeToStringConverter' },
+ { name: Role, type: ^Role },
+ { name: Prompt, type: string },
+ { name: ETag, type: string, jsonDataModelName: _etag },
+ { name: ChangeLog, type: ChangeLog }
+ ],
+ operations: [
+ {
+ name: GetChatCompletions,
+ type: Custom,
+ returnType: ChatCollectionResult,
+ paging: true,
+ eventPublish: None,
+ excludeWebApi: true,
+ excludeWebApiAgent: true,
+ excludeIManager: true,
+ excludeManager: true,
+ parameters: [
+ { name: Completions, type: int }
+ ]
+ },
+ {
+ name: ChatCompletion,
+ type: Custom,
+ webApiRoute: completion,
+ returnType: ChatCompletionResponse,
+ eventPublish: None,
+ excludeIDataSvc: true,
+ excludeDataSvc: true,
+ excludeIData: true,
+ excludeData: true,
+ managerCustom: true,
+ parameters: [
+ { name: Message, type: string }
+ ]
+ }
+ ]
+ }
+
+- {
+ name: ChatCompletionResponse,
+ excludeAll: true,
+ properties: [
+ { name: Message, type: string }
+ ]
+ }
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/refdata.beef-5.yaml b/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/refdata.beef-5.yaml
new file mode 100644
index 00000000..c7452b47
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.CodeGen/refdata.beef-5.yaml
@@ -0,0 +1,7 @@
+webApiRoutePrefix: ref
+cosmosName: AOAICosmosDb
+refDataType: Guid
+autoImplement: Cosmos
+entities:
+ # The following is an example read-only reference data Entity accessing Cosmos DB.
+ - { name: Role, cosmosContainerId: RefData, cosmosValueContainer: true, cosmosModel: Model.Role, dataModel: true }
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Common/AIChat.AOAI.Common.csproj b/samples/AIChat.AOAI/AIChat.AOAI.Common/AIChat.AOAI.Common.csproj
new file mode 100644
index 00000000..57b3fe3d
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Common/AIChat.AOAI.Common.csproj
@@ -0,0 +1,11 @@
+
+
+ netstandard2.1
+ enable
+ Preview
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/ChatAgent.cs b/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/ChatAgent.cs
new file mode 100644
index 00000000..cbfa5606
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/ChatAgent.cs
@@ -0,0 +1,22 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Common.Agents;
+
+///
+/// Provides the HTTP agent.
+///
+/// The underlying .
+/// The optional .
+/// The optional .
+public partial class ChatAgent(HttpClient client, IJsonSerializer? jsonSerializer = null, CoreEx.ExecutionContext? executionContext = null) : TypedHttpClientBase(client, jsonSerializer, executionContext), IChatAgent
+{
+ ///
+ public Task> CreateAsync(Chat value, HttpRequestOptions? requestOptions = null, CancellationToken cancellationToken = default)
+ => PostAsync("chats", value, requestOptions, cancellationToken: cancellationToken);
+
+ ///
+ public Task> ChatCompletionAsync(string? message, HttpRequestOptions? requestOptions = null, CancellationToken cancellationToken = default)
+ => PostAsync("chats/completion", requestOptions, [new HttpArg("message", message)], cancellationToken);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/IChatAgent.cs b/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/IChatAgent.cs
new file mode 100644
index 00000000..081ad7b3
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/IChatAgent.cs
@@ -0,0 +1,29 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Common.Agents;
+
+///
+/// Defines the HTTP agent.
+///
+public partial interface IChatAgent
+{
+ ///
+ /// Creates a new .
+ ///
+ /// The .
+ /// The optional .
+ /// The .
+ /// A .
+ Task> CreateAsync(Chat value, HttpRequestOptions? requestOptions = null, CancellationToken cancellationToken = default);
+
+ ///
+ /// Chat Completion.
+ ///
+ /// The Message.
+ /// The optional .
+ /// The .
+ /// A .
+ Task> ChatCompletionAsync(string? message, HttpRequestOptions? requestOptions = null, CancellationToken cancellationToken = default);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/IReferenceDataAgent.cs b/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/IReferenceDataAgent.cs
new file mode 100644
index 00000000..a2079266
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/IReferenceDataAgent.cs
@@ -0,0 +1,30 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Common.Agents;
+
+///
+/// Defines the ReferenceData HTTP agent.
+///
+public partial interface IReferenceDataAgent
+{
+ ///
+ /// Gets all of the items that match the filter arguments.
+ ///
+ /// The optional arguments.
+ /// The optional .
+ /// The .
+ /// A .
+ Task> RoleGetAllAsync(ReferenceDataFilter? args = null, HttpRequestOptions? requestOptions = null, CancellationToken cancellationToken = default);
+
+ ///
+ /// Gets the reference data entries for the specified entities and codes from the query string; e.g: ref?entity=codeX,codeY&entity2=codeZ&entity3
+ ///
+ /// The optional list of reference data names.
+ /// The optional .
+ /// The .
+ /// A .
+ /// The reference data objects will need to be manually extracted from the corresponding response content.
+ Task GetNamedAsync(string[] names, HttpRequestOptions? requestOptions = null, CancellationToken cancellationToken = default);
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/ReferenceDataAgent.cs b/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/ReferenceDataAgent.cs
new file mode 100644
index 00000000..8b39e425
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Common/Agents/Generated/ReferenceDataAgent.cs
@@ -0,0 +1,28 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Common.Agents;
+
+///
+/// Provides the ReferenceData HTTP agent.
+///
+/// The underlying .
+/// The optional .
+/// The optional .
+public partial class ReferenceDataAgent(HttpClient client, IJsonSerializer? jsonSerializer = null, CoreEx.ExecutionContext? executionContext = null) : TypedHttpClientBase(client, jsonSerializer, executionContext), IReferenceDataAgent
+{
+ ///
+ public Task> RoleGetAllAsync(ReferenceDataFilter? args = null, HttpRequestOptions? requestOptions = null, CancellationToken cancellationToken = default)
+ => GetAsync("ref/roles", requestOptions, [new HttpArg("args", args, HttpArgType.FromUriUseProperties)], cancellationToken);
+
+ ///
+ public Task GetNamedAsync(string[] names, HttpRequestOptions? requestOptions = null, CancellationToken cancellationToken = default)
+ {
+ var ro = requestOptions ?? new HttpRequestOptions();
+ if (names != null)
+ ro.UrlQueryString += string.Join("&", names);
+
+ return GetAsync("ref", ro, null, cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Common/Entities/Generated/Chat.cs b/samples/AIChat.AOAI/AIChat.AOAI.Common/Entities/Generated/Chat.cs
new file mode 100644
index 00000000..3fa91fd8
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Common/Entities/Generated/Chat.cs
@@ -0,0 +1,71 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Common.Entities;
+
+///
+/// Represents the Chat entity.
+///
+public partial class Chat : IIdentifier, IETag, IChangeLog
+{
+ ///
+ /// Gets or sets the identifier.
+ ///
+ public Guid Id { get; set; }
+
+ ///
+ /// Gets the corresponding Role text (read-only where selected).
+ ///
+ public string? RoleText { get; set; }
+
+ ///
+ /// Gets or sets the Role code.
+ ///
+ public string? Role { get; set; }
+
+ ///
+ /// Gets or sets the Prompt.
+ ///
+ public string? Prompt { get; set; }
+
+ ///
+ /// Gets or sets the ETag.
+ ///
+ [JsonPropertyName("etag")]
+ public string? ETag { get; set; }
+
+ ///
+ /// Gets or sets the Change Log.
+ ///
+ public ChangeLog? ChangeLog { get; set; }
+}
+
+///
+/// Represents the Chat collection.
+///
+public partial class ChatCollection : List { }
+
+///
+/// Represents the Chat collection result.
+///
+public class ChatCollectionResult : CollectionResult
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ChatCollectionResult() { }
+
+ ///
+ /// Initializes a new instance of the class with .
+ ///
+ /// The .
+ public ChatCollectionResult(PagingArgs? paging) : base(paging) { }
+
+ ///
+ /// Initializes a new instance of the class with to add.
+ ///
+ /// The items to add.
+ /// The .
+ public ChatCollectionResult(IEnumerable items, PagingArgs? paging = null) : base(paging) => Items.AddRange(items);
+}
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Common/Entities/Generated/ChatCompletionResponse.cs b/samples/AIChat.AOAI/AIChat.AOAI.Common/Entities/Generated/ChatCompletionResponse.cs
new file mode 100644
index 00000000..b7b12998
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Common/Entities/Generated/ChatCompletionResponse.cs
@@ -0,0 +1,16 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Common.Entities;
+
+///
+/// Represents the Chat Completion Response entity.
+///
+public partial class ChatCompletionResponse
+{
+ ///
+ /// Gets or sets the Message.
+ ///
+ public string? Message { get; set; }
+}
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Common/Entities/Generated/Role.cs b/samples/AIChat.AOAI/AIChat.AOAI.Common/Entities/Generated/Role.cs
new file mode 100644
index 00000000..5d437836
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Common/Entities/Generated/Role.cs
@@ -0,0 +1,15 @@
+/*
+ * This file is automatically generated; any changes will be lost.
+ */
+
+namespace AIChat.AOAI.Common.Entities;
+
+///
+/// Represents the Role entity.
+///
+public partial class Role : ReferenceDataBase { }
+
+///
+/// Represents the Role collection.
+///
+public partial class RoleCollection : List { }
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Common/GlobalUsings.cs b/samples/AIChat.AOAI/AIChat.AOAI.Common/GlobalUsings.cs
new file mode 100644
index 00000000..ee6bad63
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Common/GlobalUsings.cs
@@ -0,0 +1,13 @@
+global using CoreEx.Entities;
+global using CoreEx.Http;
+global using CoreEx.Json;
+global using CoreEx.RefData;
+global using System;
+global using System.Collections.Generic;
+global using System.Diagnostics.CodeAnalysis;
+global using System.Text.Json.Serialization;
+global using System.Net.Http;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using AIChat.AOAI.Common.Entities;
+global using RefDataNamespace = AIChat.AOAI.Common.Entities;
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Test/AIChat.AOAI.Test.csproj b/samples/AIChat.AOAI/AIChat.AOAI.Test/AIChat.AOAI.Test.csproj
new file mode 100644
index 00000000..15838c0d
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Test/AIChat.AOAI.Test.csproj
@@ -0,0 +1,48 @@
+
+
+
+ net8.0
+ false
+ enable
+ enable
+
+
+
+ 1701;1702;CA1707
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Test/Apis/FixtureSetup.cs b/samples/AIChat.AOAI/AIChat.AOAI.Test/Apis/FixtureSetup.cs
new file mode 100644
index 00000000..984d70c1
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Test/Apis/FixtureSetup.cs
@@ -0,0 +1,57 @@
+namespace AIChat.AOAI.Test.Apis;
+
+[SetUpFixture]
+public class FixtureSetUp
+{
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ TestSetUp.Default.EnableExpectedEvents();
+ TestSetUp.Default.ExpectNoEvents();
+
+ TestSetUp.Default.RegisterSetUp(async (count, _, ct) =>
+ {
+ // Setup and load cosmos once only.
+ if (count == 0)
+ {
+ using var test = ApiTester.Create();
+ using var scope = test.Services.CreateScope();
+ var cosmosDb = scope.ServiceProvider.GetRequiredService();
+
+ // Create the Cosmos Db (where not exists).
+ await cosmosDb.Database.Client.CreateDatabaseIfNotExistsAsync(cosmosDb.Database.Id, cancellationToken: ct).ConfigureAwait(false);
+
+ // Create 'Person' container.
+ var cdp = cosmosDb.Database.DefineContainer(cosmosDb.Chats.Container.Id, "/_partitionKey")
+ .WithIndexingPolicy()
+ .WithCompositeIndex()
+ .Path("/lastName", AzCosmos.CompositePathSortOrder.Ascending)
+ .Path("/firstName", AzCosmos.CompositePathSortOrder.Ascending)
+ .Attach()
+ .Attach()
+ .Build();
+
+ var ac = await cosmosDb.Database.ReplaceOrCreateContainerAsync(cdp, cancellationToken: ct).ConfigureAwait(false);
+
+ // Create 'RefData' container.
+ var cdr = cosmosDb.Database.DefineContainer("RefData", "/_partitionKey")
+ .WithUniqueKey()
+ .Path("/type")
+ .Path("/value/code")
+ .Attach()
+ .Build();
+
+ var rdc = await cosmosDb.Database.ReplaceOrCreateContainerAsync(cdr, cancellationToken: ct).ConfigureAwait(false);
+
+ // Import the data.
+ //var jdr = JsonDataReader.ParseYaml("Person.yaml");
+ //await cosmosDb.Chats.ImportBatchAsync(jdr, cancellationToken: ct).ConfigureAwait(false);
+
+ var jdr = JsonDataReader.ParseYaml("RefData.yaml", new JsonDataReaderArgs(new CoreEx.Text.Json.ReferenceDataContentJsonSerializer()));
+ await cosmosDb.ImportValueBatchAsync("RefData", jdr, ReferenceDataOrchestrator.GetAllTypesInNamespace(), cancellationToken: ct).ConfigureAwait(false);
+ }
+
+ return true;
+ });
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Test/Apis/ReferenceDataTest.cs b/samples/AIChat.AOAI/AIChat.AOAI.Test/Apis/ReferenceDataTest.cs
new file mode 100644
index 00000000..f3c1948c
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Test/Apis/ReferenceDataTest.cs
@@ -0,0 +1,28 @@
+using AIChat.AOAI.Common.Entities;
+
+namespace AIChat.AOAI.Test.Apis;
+
+[TestFixture]
+public class ReferenceDataTest : UsingApiTester
+{
+ [OneTimeSetUp]
+ public void OneTimeSetUp() => Assert.That(TestSetUp.Default.SetUp(), Is.True);
+
+ [Test]
+ public void A110_Roles()
+ {
+ Agent()
+ .ExpectStatusCode(HttpStatusCode.OK)
+ .Run(a => a.RoleGetAllAsync())
+ .AssertJsonFromResource("ReferenceData_A110_Genders_Response.json", "id", "etag");
+ }
+
+ [Test]
+ public void B110_GetNamed()
+ {
+ Agent()
+ .ExpectStatusCode(HttpStatusCode.OK)
+ .Run(a => a.GetNamedAsync(["gender"]))
+ .AssertJsonFromResource("ReferenceData_B110_GetNamed_Response.json", "gender.id", "gender.etag");
+ }
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Test/Cosmos/RefData.yaml b/samples/AIChat.AOAI/AIChat.AOAI.Test/Cosmos/RefData.yaml
new file mode 100644
index 00000000..e77608b2
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Test/Cosmos/RefData.yaml
@@ -0,0 +1,5 @@
+Ref:
+ - Role:
+ - USER: User
+ - SYSTEM: System
+ - ASSITANT: Assistant
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Test/GlobalUsings.cs b/samples/AIChat.AOAI/AIChat.AOAI.Test/GlobalUsings.cs
new file mode 100644
index 00000000..740ea703
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Test/GlobalUsings.cs
@@ -0,0 +1,26 @@
+global using CoreEx;
+global using CoreEx.Cosmos;
+global using CoreEx.Cosmos.Batch;
+global using CoreEx.Entities;
+global using CoreEx.Json.Data;
+global using CoreEx.Http;
+global using CoreEx.RefData;
+global using CoreEx.Validation;
+global using AzCosmos = Microsoft.Azure.Cosmos;
+global using Microsoft.Extensions.DependencyInjection;
+global using Moq;
+global using NUnit.Framework;
+global using System;
+global using System.Net;
+global using System.Reflection;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using UnitTestEx;
+global using UnitTestEx.Expectations;
+global using AIChat.AOAI.Api;
+global using AIChat.AOAI.Business;
+global using AIChat.AOAI.Business.Data;
+global using AIChat.AOAI.Business.DataSvc;
+global using AIChat.AOAI.Business.Validation;
+global using AIChat.AOAI.Common.Agents;
+global using HttpRequestOptions = CoreEx.Http.HttpRequestOptions;
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.Test/appsettings.unittest.json b/samples/AIChat.AOAI/AIChat.AOAI.Test/appsettings.unittest.json
new file mode 100644
index 00000000..7a73a41b
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.Test/appsettings.unittest.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/samples/AIChat.AOAI/AIChat.AOAI.sln b/samples/AIChat.AOAI/AIChat.AOAI.sln
new file mode 100644
index 00000000..7df135da
--- /dev/null
+++ b/samples/AIChat.AOAI/AIChat.AOAI.sln
@@ -0,0 +1,106 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.13.35825.156 d17.13
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AIChat.AOAI.Api", "AIChat.AOAI.Api\AIChat.AOAI.Api.csproj", "{FBC7842F-B35C-4813-B3F3-EB4E34583EEC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AIChat.AOAI.Business", "AIChat.AOAI.Business\AIChat.AOAI.Business.csproj", "{F98FFBA3-32CD-4127-B464-A9D18A545D2E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AIChat.AOAI.Common", "AIChat.AOAI.Common\AIChat.AOAI.Common.csproj", "{81860842-8A59-4A45-8C15-84FA45606BD3}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{53EEA045-4195-4A3F-A7C1-13A517B88080}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AIChat.AOAI.Test", "AIChat.AOAI.Test\AIChat.AOAI.Test.csproj", "{27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{A4E363CD-D0BE-4B43-9C80-18EA6CD2FAA6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AIChat.AOAI.CodeGen", "AIChat.AOAI.CodeGen\AIChat.AOAI.CodeGen.csproj", "{A65032E5-F3FC-4963-B135-E462190D0ABC}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ソリューション項目", "ソリューション項目", "{2022CB0F-8BAF-F49E-0C48-8EF4C96EC109}"
+ ProjectSection(SolutionItems) = preProject
+ README.md = README.md
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Debug|x64.Build.0 = Debug|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Debug|x86.Build.0 = Debug|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Release|x64.ActiveCfg = Release|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Release|x64.Build.0 = Release|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Release|x86.ActiveCfg = Release|Any CPU
+ {FBC7842F-B35C-4813-B3F3-EB4E34583EEC}.Release|x86.Build.0 = Release|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Debug|x64.Build.0 = Debug|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Debug|x86.Build.0 = Debug|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Release|x64.ActiveCfg = Release|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Release|x64.Build.0 = Release|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Release|x86.ActiveCfg = Release|Any CPU
+ {F98FFBA3-32CD-4127-B464-A9D18A545D2E}.Release|x86.Build.0 = Release|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Debug|x64.Build.0 = Debug|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Debug|x86.Build.0 = Debug|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Release|x64.ActiveCfg = Release|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Release|x64.Build.0 = Release|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Release|x86.ActiveCfg = Release|Any CPU
+ {81860842-8A59-4A45-8C15-84FA45606BD3}.Release|x86.Build.0 = Release|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Debug|x64.Build.0 = Debug|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Debug|x86.Build.0 = Debug|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Release|Any CPU.Build.0 = Release|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Release|x64.ActiveCfg = Release|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Release|x64.Build.0 = Release|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Release|x86.ActiveCfg = Release|Any CPU
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06}.Release|x86.Build.0 = Release|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Debug|x64.Build.0 = Debug|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Debug|x86.Build.0 = Debug|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Release|x64.ActiveCfg = Release|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Release|x64.Build.0 = Release|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Release|x86.ActiveCfg = Release|Any CPU
+ {A65032E5-F3FC-4963-B135-E462190D0ABC}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {27C4E8CA-5C89-4B72-9D21-55DEB38BCC06} = {53EEA045-4195-4A3F-A7C1-13A517B88080}
+ {A65032E5-F3FC-4963-B135-E462190D0ABC} = {A4E363CD-D0BE-4B43-9C80-18EA6CD2FAA6}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {44AE4366-B3C4-4390-A283-8C2F5D600AC5}
+ EndGlobalSection
+EndGlobal
diff --git a/samples/AIChat.AOAI/README.md b/samples/AIChat.AOAI/README.md
new file mode 100644
index 00000000..f088c9d9
--- /dev/null
+++ b/samples/AIChat.AOAI/README.md
@@ -0,0 +1,114 @@
+Beef x AI
+---
+The purpose of the sample is demonstrate the usage of _Beef_ in a Gen AI scenario.
+
+Use Azure OpenAI to have conversations.
+
+> [!WARNING]
+> The tools you use include a preview.
+> Use of this code should be considered carefully.
+
+Prerequisites
+---
+You need to deploy one of the models from Azure OpenAI Service.
+
+Scope
+---
+| Endpoint | Description |
+| -- | -- |
+| POST /chats | Create a new chat history |
+| POST /chats/completion | generative AI message |
+
+Cosmos DB usage
+---
+- `RefData` - manage role.
+- `Chat` - manage chat history.
+
+Solution skeleton
+---
+```
+dotnet new beef --company AIChat --appname AOAI --datasource Cosmos
+```
+
+Code Generation
+---
+Please copy the .yaml of this project.
+
+Then run the following command.
+```
+dotnet run all
+```
+
+
+Install AI tools
+---
+You need to install the following AI tools.
+```
+dotnet add package Microsoft.SemanticKernel --version 1.41.0
+dotnet add package Aspire.Azure.AI.OpenAI --version 9.1.0-preview.1.25121.10
+dotnet add package Microsoft.SemanticKernel.Connectors.AzureOpenAI --version 1.41.0
+```
+
+Add `global using Microsoft.SemanticKernel;` to `GlobalUsings.cs`
+
+
+Design
+---
+The flow of this sample is as follows.
+```mermaid
+sequenceDiagram
+ participant Cosmos
+ participant AOAI
+ participant Beef
+ participant Client
+
+ Client ->> Beef: Call API
+ Beef ->> Cosmos: Get the top 3 histories
+ Beef ->> Beef: Set system prompt
+ Beef ->> Beef: Set history messages
+ Beef ->> Beef: Set client message
+ Beef ->> AOAI: Call AOAI to generate message
+ Beef ->> Cosmos: Store client message and generative message
+ Beef ->> Client: Reply with the generated message
+```
+
+Startup
+---
+Add the semantic kernel using Azure OpenAI Client.
+```csharp
+// Add the semantic kernel
+builder.Services.AddKernel()
+ .AddAzureOpenAIChatCompletion(
+ builder.Configuration["AzureOpenAI::ModelName"] ?? string.Empty,
+ builder.Configuration["AzureOpenAI::Endpoint"] ?? string.Empty,
+ builder.Configuration["AzureOpenAI::APIKey"] ?? string.Empty
+ );
+
+builder.Services.AddTransient((serviceProvider) => {
+ return new Kernel(serviceProvider);
+});
+```
+
+To use Azure OpenAI Service, add the following to appsettins.json
+```json
+"AzureOpenAI:": {
+ "ModelName": "",
+ "Endpoint": "https://.openai.azure.com/",
+ "APIKey": ""
+},
+```
+
+Validation
+---
+ToDo
+
+Test
+---
+ToDo
+
+
+REFERENCES
+---
+1. [MSLearn - RAG Chatbot](https://learn.microsoft.com/en-us/azure/cosmos-db/gen-ai/rag-chatbot)
+2. [MSLearn - SemanticKernel](https://learn.microsoft.com/ja-jp/semantic-kernel/overview/)
+3. [MSLearn - .NET Aspire](https://learn.microsoft.com/ja-jp/dotnet/aspire/)
\ No newline at end of file