-
Notifications
You must be signed in to change notification settings - Fork 875
Expand file tree
/
Copy pathProgram.cs
More file actions
165 lines (145 loc) · 7.67 KB
/
Copy pathProgram.cs
File metadata and controls
165 lines (145 loc) · 7.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#if (IsOpenAI || IsFoundryLocal || (IsAzureOpenAI && !IsManagedIdentity))
using System.ClientModel;
#elif (IsAzureOpenAI && IsManagedIdentity)
using System.ClientModel.Primitives;
#endif
#if (IsFoundryLocal)
using Microsoft.AI.Foundry.Local;
using Microsoft.Extensions.Logging.Abstractions;
#endif
#if (IsAzureAISearch && !IsManagedIdentity)
using Azure;
#elif (IsManagedIdentity)
using Azure.Identity;
#endif
using Microsoft.Extensions.AI;
#if (IsOllama)
using OllamaSharp;
#elif (IsOpenAI || IsFoundryLocal || IsAzureOpenAI)
using OpenAI;
#endif
using AIChatWeb_CSharp.Web.Components;
using AIChatWeb_CSharp.Web.Services;
using AIChatWeb_CSharp.Web.Services.Ingestion;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
#if (IsOllama)
IChatClient chatClient = new OllamaApiClient(new Uri("http://localhost:11434"),
"llama3.2");
IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator = new OllamaApiClient(new Uri("http://localhost:11434"),
"all-minilm");
#elif (IsFoundryLocal)
var chatAlias = builder.Configuration["FoundryLocal:ChatModel"] ?? "qwen3-4b";
var embeddingAlias = builder.Configuration["FoundryLocal:EmbeddingModel"] ?? "qwen3-embedding-0.6b";
var foundryServiceUrl = builder.Configuration["FoundryLocal:ServiceUrl"] ?? "http://127.0.0.1:5273";
await FoundryLocalManager.CreateAsync(new Configuration
{
AppName = "AIChatWeb-CSharp",
Web = new Configuration.WebService { Urls = foundryServiceUrl }
}, NullLogger.Instance);
var foundryManager = FoundryLocalManager.Instance;
await foundryManager.StartWebServiceAsync();
var foundryCatalog = await foundryManager.GetCatalogAsync();
async Task<string> EnsureFoundryModelAsync(string modelAlias)
{
var model = await foundryCatalog.GetModelAsync(modelAlias)
?? throw new InvalidOperationException(
$"Foundry Local model '{modelAlias}' was not found in the catalog. Run 'foundry model list' to see available models.");
if (!await model.IsCachedAsync())
{
Console.WriteLine($"Foundry Local: downloading model '{modelAlias}' (first run only)...");
await model.DownloadAsync(_ => { });
}
if (!await model.IsLoadedAsync())
{
await model.LoadAsync();
}
return model.Id;
}
var chatModelId = await EnsureFoundryModelAsync(chatAlias);
var embeddingModelId = await EnsureFoundryModelAsync(embeddingAlias);
var foundryEndpointUrl = foundryManager.Urls?.FirstOrDefault() ?? foundryServiceUrl;
var foundryEndpoint = new Uri($"{foundryEndpointUrl.TrimEnd('/')}/v1");
var foundryClient = new OpenAIClient(
new ApiKeyCredential("unused"),
new OpenAIClientOptions { Endpoint = foundryEndpoint });
var chatClient = foundryClient.GetChatClient(chatModelId).AsIChatClient();
var embeddingGenerator = foundryClient.GetEmbeddingClient(embeddingModelId).AsIEmbeddingGenerator();
#elif (IsOpenAI)
// You will need to set the endpoint and key to your own values
// You can do this using Visual Studio's "Manage User Secrets" UI, or on the command line:
// cd this-project-directory
// dotnet user-secrets set OpenAI:Key YOUR-API-KEY
var openAIClient = new OpenAIClient(
new ApiKeyCredential(builder.Configuration["OpenAI:Key"] ?? throw new InvalidOperationException("Missing configuration: OpenAI:Key. See the README for details.")));
#pragma warning disable OPENAI001 // GetResponsesClient() is experimental and subject to change or removal in future updates.
var chatClient = openAIClient.GetResponsesClient().AsIChatClient("gpt-4o-mini");
#pragma warning restore OPENAI001
var embeddingGenerator = openAIClient.GetEmbeddingClient("text-embedding-3-small").AsIEmbeddingGenerator();
#elif (IsAzureAIFoundry)
#elif (IsAzureOpenAI)
// You will need to set the endpoint and key to your own values
// You can do this using Visual Studio's "Manage User Secrets" UI, or on the command line:
// cd this-project-directory
// dotnet user-secrets set AzureOpenAI:Endpoint https://YOUR-DEPLOYMENT-NAME.openai.azure.com
#if (!IsManagedIdentity)
// dotnet user-secrets set AzureOpenAI:Key YOUR-API-KEY
#endif
var azureOpenAIEndpoint = new Uri(new Uri(builder.Configuration["AzureOpenAI:Endpoint"] ?? throw new InvalidOperationException("Missing configuration: AzureOpenAi:Endpoint. See the README for details.")), "/openai/v1");
#if (IsManagedIdentity)
#pragma warning disable OPENAI001 // OpenAIClient(AuthenticationPolicy, OpenAIClientOptions) and GetResponsesClient() are experimental and subject to change or removal in future updates.
var azureOpenAi = new OpenAIClient(
new BearerTokenPolicy(new DefaultAzureCredential(), "https://ai.azure.com/.default"),
new OpenAIClientOptions { Endpoint = azureOpenAIEndpoint });
#elif (!IsManagedIdentity)
var openAIOptions = new OpenAIClientOptions { Endpoint = azureOpenAIEndpoint };
var azureOpenAi = new OpenAIClient(new ApiKeyCredential(builder.Configuration["AzureOpenAI:Key"] ?? throw new InvalidOperationException("Missing configuration: AzureOpenAi:Key. See the README for details.")), openAIOptions);
#pragma warning disable OPENAI001 // GetResponsesClient() is experimental and subject to change or removal in future updates.
#endif
var chatClient = azureOpenAi.GetResponsesClient().AsIChatClient("gpt-4o-mini");
#pragma warning restore OPENAI001
var embeddingGenerator = azureOpenAi.GetEmbeddingClient("text-embedding-3-small").AsIEmbeddingGenerator();
#endif
#if (IsAzureAISearch)
// You will need to set the endpoint and key to your own values
// You can do this using Visual Studio's "Manage User Secrets" UI, or on the command line:
// cd this-project-directory
// dotnet user-secrets set AzureAISearch:Endpoint https://YOUR-DEPLOYMENT-NAME.search.windows.net
#if (!IsManagedIdentity)
// dotnet user-secrets set AzureAISearch:Key YOUR-API-KEY
#endif
var azureAISearchEndpoint = new Uri(builder.Configuration["AzureAISearch:Endpoint"]
?? throw new InvalidOperationException("Missing configuration: AzureAISearch:Endpoint. See the README for details."));
#if (IsManagedIdentity)
var azureAISearchCredential = new DefaultAzureCredential();
#elif (!IsManagedIdentity)
var azureAISearchCredential = new AzureKeyCredential(builder.Configuration["AzureAISearch:Key"]
?? throw new InvalidOperationException("Missing configuration: AzureAISearch:Key. See the README for details."));
#endif
builder.Services.AddAzureAISearchVectorStore(azureAISearchEndpoint, azureAISearchCredential);
builder.Services.AddAzureAISearchCollection<IngestedChunk>(IngestedChunk.CollectionName, azureAISearchEndpoint, azureAISearchCredential);
#elif (IsLocalVectorStore)
var vectorStorePath = Path.Combine(AppContext.BaseDirectory, "vector-store.db");
var vectorStoreConnectionString = $"Data Source={vectorStorePath}";
builder.Services.AddSqliteVectorStore(_ => vectorStoreConnectionString);
builder.Services.AddSqliteCollection<string, IngestedChunk>(IngestedChunk.CollectionName, vectorStoreConnectionString);
#endif
builder.Services.AddSingleton<DataIngestor>();
builder.Services.AddSingleton<SemanticSearch>();
builder.Services.AddKeyedSingleton("ingestion_directory", new DirectoryInfo(Path.Combine(builder.Environment.WebRootPath, "Data")));
builder.Services.AddChatClient(chatClient).UseFunctionInvocation().UseLogging();
builder.Services.AddEmbeddingGenerator(embeddingGenerator);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAntiforgery();
app.UseStaticFiles();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();