Skip to content

Commit 22ca5a8

Browse files
committed
dotnet new aichatweb --provider azureopenai --aspire
1 parent 2c22918 commit 22ca5a8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+17905
-0
lines changed

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# AI Chat with Custom Data
2+
3+
This project is an AI chat application that demonstrates how to chat with custom data using an AI language model. Please note that this template is currently in an early preview stage. If you have feedback, please take a [brief survey](https://aka.ms/dotnet-chat-templatePreview2-survey).
4+
5+
>[!NOTE]
6+
> Before running this project you need to configure the API keys or endpoints for the providers you have chosen. See below for details specific to your choices.
7+
8+
### Known Issues
9+
10+
#### Errors running Ollama or Docker
11+
12+
A recent incompatibility was found between Ollama and Docker Desktop. This issue results in runtime errors when connecting to Ollama, and the workaround for that can lead to Docker not working for Aspire projects.
13+
14+
This incompatibility can be addressed by upgrading to Docker Desktop 4.41.1. See [ollama/ollama#9509](https://github.com/ollama/ollama/issues/9509#issuecomment-2842461831) for more information and a link to install the version of Docker Desktop with the fix.
15+
16+
# Configure the AI Model Provider
17+
18+
## Using Azure OpenAI
19+
20+
To use Azure OpenAI, you will need an Azure account and an Azure OpenAI Service resource. For detailed instructions, see the [Azure OpenAI Service documentation](https://learn.microsoft.com/azure/ai-services/openai/how-to/create-resource).
21+
22+
### 1. Create an Azure OpenAI Service Resource
23+
[Create an Azure OpenAI Service resource](https://learn.microsoft.com/azure/ai-services/openai/how-to/create-resource?pivots=web-portal).
24+
25+
### 2. Deploy the Models
26+
Deploy the `gpt-4o-mini` and `text-embedding-3-small` models to your Azure OpenAI Service resource. When creating those deployments, give them the same names as the models (`gpt-4o-mini` and `text-embedding-3-small`). See the Azure OpenAI documentation to learn how to [Deploy a model](https://learn.microsoft.com/azure/ai-services/openai/how-to/create-resource?pivots=web-portal#deploy-a-model).
27+
28+
### 3. Configure API Key and Endpoint
29+
Configure your Azure OpenAI API key and endpoint for this project, using .NET User Secrets:
30+
1. In the Azure Portal, navigate to your Azure OpenAI resource.
31+
2. Copy the "Endpoint" URL and "Key 1" from the "Keys and Endpoint" section.
32+
3. From the command line, configure your API key and endpoint for this project using .NET User Secrets by running the following commands:
33+
34+
```sh
35+
cd imageGeneratorSample.AppHost
36+
dotnet user-secrets set ConnectionStrings:openai "Endpoint=https://YOUR-DEPLOYMENT-NAME.openai.azure.com;Key=YOUR-API-KEY"
37+
```
38+
39+
Make sure to replace `YOUR-API-KEY` and `YOUR-DEPLOYMENT-NAME` with your actual Azure OpenAI key and endpoint. Make sure your endpoint URL is formatted like https://YOUR-DEPLOYMENT-NAME.openai.azure.com/ (do not include any path after .openai.azure.com/).
40+
41+
# Running the application
42+
43+
## Using Visual Studio
44+
45+
1. Open the `.sln` file in Visual Studio.
46+
2. Press `Ctrl+F5` or click the "Start" button in the toolbar to run the project.
47+
48+
## Using Visual Studio Code
49+
50+
1. Open the project folder in Visual Studio Code.
51+
2. Install the [C# Dev Kit extension](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit) for Visual Studio Code.
52+
3. Once installed, Open the `Program.cs` file in the imageGeneratorSample.AppHost project.
53+
4. Run the project by clicking the "Run" button in the Debug view.
54+
55+
## Trust the localhost certificate
56+
57+
Several .NET Aspire templates include ASP.NET Core projects that are configured to use HTTPS by default. If this is the first time you're running the project, an exception might occur when loading the Aspire dashboard. This error can be resolved by trusting the self-signed development certificate with the .NET CLI.
58+
59+
See [Troubleshoot untrusted localhost certificate in .NET Aspire](https://learn.microsoft.com/dotnet/aspire/troubleshooting/untrusted-localhost-certificate) for more information.
60+
61+
# Updating JavaScript dependencies
62+
63+
This template leverages JavaScript libraries to provide essential functionality. These libraries are located in the wwwroot/lib folder of the imageGeneratorSample.Web project. For instructions on updating each dependency, please refer to the README.md file in each respective folder.
64+
65+
# Learn More
66+
To learn more about development with .NET and AI, check out the following links:
67+
68+
* [AI for .NET Developers](https://learn.microsoft.com/dotnet/ai/)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
var builder = DistributedApplication.CreateBuilder(args);
2+
3+
// You will need to set the connection string to your own value
4+
// You can do this using Visual Studio's "Manage User Secrets" UI, or on the command line:
5+
// cd this-project-directory
6+
// dotnet user-secrets set ConnectionStrings:openai "Endpoint=https://YOUR-DEPLOYMENT-NAME.openai.azure.com;Key=YOUR-API-KEY"
7+
var openai = builder.AddConnectionString("openai");
8+
9+
var webApp = builder.AddProject<Projects.imageGeneratorSample_Web>("aichatweb-app");
10+
webApp.WithReference(openai);
11+
12+
builder.Build().Run();
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning",
6+
"Aspire.Hosting.Dcp": "Warning"
7+
}
8+
}
9+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<Sdk Name="Aspire.AppHost.Sdk" Version="9.3.0" />
4+
5+
<PropertyGroup>
6+
<OutputType>Exe</OutputType>
7+
<TargetFramework>net9.0</TargetFramework>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<Nullable>enable</Nullable>
10+
<IsAspireHost>true</IsAspireHost>
11+
<UserSecretsId>0904292d-ae00-447d-8974-6bcb736af7dd</UserSecretsId>
12+
</PropertyGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.3.0" />
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
<ProjectReference Include="..\imageGeneratorSample.Web\imageGeneratorSample.Web.csproj" />
20+
</ItemGroup>
21+
22+
</Project>
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Diagnostics.HealthChecks;
5+
using Microsoft.Extensions.Logging;
6+
using Microsoft.Extensions.ServiceDiscovery;
7+
using OpenTelemetry;
8+
using OpenTelemetry.Metrics;
9+
using OpenTelemetry.Trace;
10+
11+
namespace Microsoft.Extensions.Hosting;
12+
13+
// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
14+
// This project should be referenced by each service project in your solution.
15+
// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
16+
public static class Extensions
17+
{
18+
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
19+
{
20+
builder.ConfigureOpenTelemetry();
21+
22+
builder.AddDefaultHealthChecks();
23+
24+
builder.Services.AddServiceDiscovery();
25+
26+
builder.Services.ConfigureHttpClientDefaults(http =>
27+
{
28+
#pragma warning disable EXTEXP0001 // RemoveAllResilienceHandlers is experimental
29+
http.RemoveAllResilienceHandlers();
30+
#pragma warning restore EXTEXP0001
31+
32+
// Turn on resilience by default
33+
http.AddStandardResilienceHandler();
34+
35+
// Turn on service discovery by default
36+
http.AddServiceDiscovery();
37+
});
38+
39+
// Uncomment the following to restrict the allowed schemes for service discovery.
40+
// builder.Services.Configure<ServiceDiscoveryOptions>(options =>
41+
// {
42+
// options.AllowedSchemes = ["https"];
43+
// });
44+
45+
return builder;
46+
}
47+
48+
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
49+
{
50+
builder.Logging.AddOpenTelemetry(logging =>
51+
{
52+
logging.IncludeFormattedMessage = true;
53+
logging.IncludeScopes = true;
54+
});
55+
56+
builder.Services.AddOpenTelemetry()
57+
.WithMetrics(metrics =>
58+
{
59+
metrics.AddAspNetCoreInstrumentation()
60+
.AddHttpClientInstrumentation()
61+
.AddRuntimeInstrumentation()
62+
.AddMeter("Experimental.Microsoft.Extensions.AI");
63+
})
64+
.WithTracing(tracing =>
65+
{
66+
tracing.AddSource(builder.Environment.ApplicationName)
67+
.AddAspNetCoreInstrumentation()
68+
// Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
69+
//.AddGrpcClientInstrumentation()
70+
.AddHttpClientInstrumentation()
71+
.AddSource("Experimental.Microsoft.Extensions.AI");
72+
});
73+
74+
builder.AddOpenTelemetryExporters();
75+
76+
return builder;
77+
}
78+
79+
private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
80+
{
81+
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
82+
83+
if (useOtlpExporter)
84+
{
85+
builder.Services.AddOpenTelemetry().UseOtlpExporter();
86+
}
87+
88+
// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
89+
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
90+
//{
91+
// builder.Services.AddOpenTelemetry()
92+
// .UseAzureMonitor();
93+
//}
94+
95+
return builder;
96+
}
97+
98+
public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
99+
{
100+
builder.Services.AddHealthChecks()
101+
// Add a default liveness check to ensure app is responsive
102+
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
103+
104+
return builder;
105+
}
106+
107+
public static WebApplication MapDefaultEndpoints(this WebApplication app)
108+
{
109+
// Adding health checks endpoints to applications in non-development environments has security implications.
110+
// See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
111+
if (app.Environment.IsDevelopment())
112+
{
113+
// All health checks must pass for app to be considered ready to accept traffic after starting
114+
app.MapHealthChecks("/health");
115+
116+
// Only health checks tagged with the "live" tag must pass for app to be considered alive
117+
app.MapHealthChecks("/alive", new HealthCheckOptions
118+
{
119+
Predicate = r => r.Tags.Contains("live")
120+
});
121+
}
122+
123+
return app;
124+
}
125+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<IsAspireSharedProject>true</IsAspireSharedProject>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
12+
13+
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.9.0-preview.1.25411.3" />
14+
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="9.3.0" />
15+
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
16+
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
17+
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
18+
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
19+
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.12.0" />
20+
</ItemGroup>
21+
22+
</Project>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<base href="/" />
8+
<link rel="stylesheet" href="@Assets["app.css"]" />
9+
<link rel="stylesheet" href="@Assets["imageGeneratorSample.Web.styles.css"]" />
10+
<ImportMap />
11+
<HeadOutlet @rendermode="@renderMode" />
12+
</head>
13+
14+
<body>
15+
<Routes @rendermode="@renderMode" />
16+
<script src="@Assets["app.js"]" type="module"></script>
17+
<script src="@Assets["_framework/blazor.web.js"]"></script>
18+
</body>
19+
20+
</html>
21+
22+
@code {
23+
private readonly IComponentRenderMode renderMode = new InteractiveServerRenderMode(prerender: false);
24+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/* Used under CC0 license */
2+
3+
.lds-ellipsis {
4+
color: #666;
5+
animation: fade-in 1s;
6+
}
7+
8+
@keyframes fade-in {
9+
0% {
10+
opacity: 0;
11+
}
12+
13+
100% {
14+
opacity: 1;
15+
}
16+
}
17+
18+
.lds-ellipsis,
19+
.lds-ellipsis div {
20+
box-sizing: border-box;
21+
}
22+
23+
.lds-ellipsis {
24+
margin: auto;
25+
display: block;
26+
position: relative;
27+
width: 80px;
28+
height: 80px;
29+
}
30+
31+
.lds-ellipsis div {
32+
position: absolute;
33+
top: 33.33333px;
34+
width: 10px;
35+
height: 10px;
36+
border-radius: 50%;
37+
background: currentColor;
38+
animation-timing-function: cubic-bezier(0, 1, 1, 0);
39+
}
40+
41+
.lds-ellipsis div:nth-child(1) {
42+
left: 8px;
43+
animation: lds-ellipsis1 0.6s infinite;
44+
}
45+
46+
.lds-ellipsis div:nth-child(2) {
47+
left: 8px;
48+
animation: lds-ellipsis2 0.6s infinite;
49+
}
50+
51+
.lds-ellipsis div:nth-child(3) {
52+
left: 32px;
53+
animation: lds-ellipsis2 0.6s infinite;
54+
}
55+
56+
.lds-ellipsis div:nth-child(4) {
57+
left: 56px;
58+
animation: lds-ellipsis3 0.6s infinite;
59+
}
60+
61+
@keyframes lds-ellipsis1 {
62+
0% {
63+
transform: scale(0);
64+
}
65+
66+
100% {
67+
transform: scale(1);
68+
}
69+
}
70+
71+
@keyframes lds-ellipsis3 {
72+
0% {
73+
transform: scale(1);
74+
}
75+
76+
100% {
77+
transform: scale(0);
78+
}
79+
}
80+
81+
@keyframes lds-ellipsis2 {
82+
0% {
83+
transform: translate(0, 0);
84+
}
85+
86+
100% {
87+
transform: translate(24px, 0);
88+
}
89+
}

0 commit comments

Comments
 (0)