Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
2b1db8b
add devcontainer NET8
ferantivero Apr 15, 2025
62d8c5a
streamline: remove sln for visual studio
ferantivero Apr 15, 2025
66408e8
sort usings following .NET conventions
ferantivero Apr 16, 2025
51b20cd
streamline: use primery contructors
ferantivero Apr 16, 2025
27f6079
streamline: dict initialization
ferantivero Apr 16, 2025
843bf70
streamline: use var if can be inferred from context
ferantivero Apr 16, 2025
e1f9348
fix: guard null api configuration values
ferantivero Apr 16, 2025
b97b3fe
streamline: remove dup code
ferantivero Apr 16, 2025
150cda2
fix: stdout headers
ferantivero Apr 16, 2025
dae1fea
refact: add structure and more expresive logging capabs
ferantivero Apr 16, 2025
cff2100
fix: prompt null check
ferantivero Apr 16, 2025
5c6bddf
ai naming convention: replace query var name by prompt
ferantivero Apr 16, 2025
7965ed2
cleanup: address warnings removing dead code
ferantivero Apr 16, 2025
c188a98
cleanup: not currently in use UserSecretsId
ferantivero Apr 17, 2025
749f2fb
update: to System.Text.Json
ferantivero Apr 17, 2025
1648a10
streamline: remove not in use endpoint
ferantivero Apr 17, 2025
d897def
streamline: remove apparently not in use authZ capabs
ferantivero Apr 17, 2025
8d4e641
cleanup: remove unused dev appsettings
ferantivero Apr 17, 2025
e8a2eb5
fix: add expected app settings
ferantivero Apr 17, 2025
bbb4960
configure console log level and structure log entry
ferantivero Apr 17, 2025
fa8d13a
udpdate: use modern async js function with fetch instead of jq
ferantivero Apr 17, 2025
1bef5af
update: remove jq dep
ferantivero Apr 17, 2025
b12129c
cleanup: remove unused style
ferantivero Apr 17, 2025
40b3b47
udpate: use options for settings
ferantivero Apr 21, 2025
c7adc4d
cleanup: use controller placeholder convention for route
ferantivero Apr 21, 2025
d721cf0
cleanup: simplify http client init
ferantivero Apr 21, 2025
49846ab
cleanup: inline content init
ferantivero Apr 21, 2025
cdfecfe
cleanup: post and content stream invoking
ferantivero Apr 21, 2025
2eeff48
streamline: response hanlding
ferantivero Apr 21, 2025
aa6b7b5
improve: logging messages
ferantivero Apr 21, 2025
15b561e
update: register the httpclient
ferantivero Apr 21, 2025
88169c2
improve: naming conventions endpoint for an ai context
ferantivero Apr 21, 2025
69f290d
improve: js naming conventions
ferantivero Apr 21, 2025
37da25f
improve js query elements
ferantivero Apr 21, 2025
1fa0639
improve: add trycatch when sending a prompt
ferantivero Apr 21, 2025
c7ffe60
streamline js sendPrompr function
ferantivero Apr 21, 2025
ae7d588
streamline js addChatMessage
ferantivero Apr 21, 2025
523035e
streamline js formatDate function
ferantivero Apr 21, 2025
e2a9014
cleanup: distilled unused markup element
ferantivero Apr 21, 2025
24c32f3
improve: screen readers read new messages
ferantivero Apr 21, 2025
ea05d66
minors: indent, add semantic html element and more
ferantivero Apr 21, 2025
e8e0fe8
streamline: use file-scoped namespace
ferantivero Apr 21, 2025
7eae87e
streamline: launch profile and settings
ferantivero Apr 21, 2025
745a479
fix: remove message label that slipped through
ferantivero Apr 21, 2025
bddf108
refact: use record for inmuntale dto
ferantivero Apr 21, 2025
124f6f0
improve: prompt guard
ferantivero Apr 21, 2025
871922c
update zip
ferantivero Apr 21, 2025
c58d11d
Address PR Feedback: change app name
ferantivero Apr 22, 2025
64e6969
Address PR Feedback: fix assistant behavior
ferantivero Apr 22, 2025
c47591b
Address PR Feedback: change bot name
ferantivero Apr 22, 2025
030eba5
Address PR Feedback: change logging entry level for troubleshooting p…
ferantivero Apr 22, 2025
78a035e
Address PR Feedback: make naming convetions gpt-agnostic
ferantivero Apr 21, 2025
eabf6ae
Address PR Feedback: support settings hot reload
ferantivero Apr 22, 2025
a42814b
Address PR Feedback: use custom httpclienthanlder validation only in dev
ferantivero Apr 22, 2025
cf8f7c6
update zip after addressing pr feedback
ferantivero Apr 22, 2025
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Azure OpenAI end-to-end basic reference implementation",
"image": "mcr.microsoft.com/devcontainers/dotnet:dev-8.0-jammy",
"runArgs": ["--network=host"],
"remoteUser": "vscode",
"features": {
},
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csdevkit",
"ms-azuretools.vscode-bicep"
],
"settings": {}
}
},
"forwardPorts": []
}
8 changes: 4 additions & 4 deletions infra-as-code/bicep/webapp.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ resource appsettings 'Microsoft.Web/sites/config@2022-09-01' = {
APPINSIGHTS_INSTRUMENTATIONKEY: appInsights.properties.InstrumentationKey
APPLICATIONINSIGHTS_CONNECTION_STRING: appInsights.properties.ConnectionString
ApplicationInsightsAgent_EXTENSION_VERSION: '~2'
chatApiKey: '@Microsoft.KeyVault(SecretUri=${keyVault::chatApiKey.properties.secretUri})'
chatApiEndpoint: chatProject::scoreEndpoint.properties.scoringUri
chatInputName: 'question'
chatOutputName: 'answer'
ChatApiKey: '@Microsoft.KeyVault(SecretUri=${keyVault::chatApiKey.properties.secretUri})'
ChatApiEndpoint: chatProject::scoreEndpoint.properties.scoringUri
ChatInputName: 'question'
ChatOutputName: 'answer'
keyVaultReferenceIdentity: appServiceManagedIdentity.id
}
}
Expand Down
24 changes: 0 additions & 24 deletions website/chatui.sln

This file was deleted.

Binary file modified website/chatui.zip
Binary file not shown.
16 changes: 16 additions & 0 deletions website/chatui/Configuration/ChatApiOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;

namespace chatui.Configuration;

public class ChatApiOptions
{
[Url]
public string ChatApiEndpoint { get; init; } = default!;

[Required]
public string ChatApiKey { get; init; } = default!;

public string ChatInputName { get; init; } = "chat_input";

public string ChatOutputName { get; init; } = "chat_output";
}
68 changes: 68 additions & 0 deletions website/chatui/Controllers/ChatController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Net.Http.Headers;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using chatui.Configuration;
using chatui.Models;

namespace chatui.Controllers;

[ApiController]
[Route("[controller]/[action]")]

public class ChatController(
IHttpClientFactory httpClientFactory,
IOptionsMonitor<ChatApiOptions> options,
ILogger<ChatController> logger) : ControllerBase
{
private readonly HttpClient _client = httpClientFactory.CreateClient("ChatClient");
private readonly IOptionsMonitor<ChatApiOptions> _options = options;
private readonly ILogger<ChatController> _logger = logger;

[HttpPost]
public async Task<IActionResult> Completions([FromBody] string prompt)
{
if (string.IsNullOrWhiteSpace(prompt))
throw new ArgumentException("Prompt cannot be null, empty, or whitespace.", nameof(prompt));

_logger.LogDebug("Prompt received {Prompt}", prompt);

var _config = _options.CurrentValue;

var requestBody = JsonSerializer.Serialize(new Dictionary<string, string>
{
[_config.ChatInputName] = prompt
});

using var request = new HttpRequestMessage(HttpMethod.Post, _config.ChatApiEndpoint)
{
Content = new StringContent(requestBody, System.Text.Encoding.UTF8, "application/json"),
};
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _config.ChatApiKey);

var response = await _client.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();

_logger.LogInformation("HTTP status code: {StatusCode}", response.StatusCode);

if (!response.IsSuccessStatusCode)
{
_logger.LogError("Error response: {Content}", responseContent);

foreach (var (key, value) in response.Headers)
_logger.LogDebug("Header {Key}: {Value}", key, string.Join(", ", value));

foreach (var (key, value) in response.Content.Headers)
_logger.LogDebug("Content-Header {Key}: {Value}", key, string.Join(", ", value));

return BadRequest(responseContent);
}

_logger.LogInformation("Successful response: {Content}", responseContent);

var result = JsonSerializer.Deserialize<Dictionary<string, string>>(responseContent);
var output = result?.GetValueOrDefault(_config.ChatOutputName) ?? string.Empty;

return Ok(new HttpChatResponse(true, output));
}
}
72 changes: 0 additions & 72 deletions website/chatui/Controllers/ChatGPTController.cs

This file was deleted.

11 changes: 5 additions & 6 deletions website/chatui/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using Microsoft.AspNetCore.Mvc;

namespace chatui.Controllers
namespace chatui.Controllers;

public class HomeController : Controller
{
public class HomeController : Controller
public IActionResult Index()
{
public IActionResult Index()
{
return View();
}
return View();
}
}
15 changes: 0 additions & 15 deletions website/chatui/Models/ChatRequest.cs

This file was deleted.

34 changes: 2 additions & 32 deletions website/chatui/Models/ChatResponse.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,3 @@
namespace chatui.Models
{
public class HttpChatGPTResponse
{
public bool Success { get; set; }
public string Data { get; set; }
public string Error { get; set; }
}
namespace chatui.Models;

public class ChatResponse
{
public string id { get; set; }
public string _object { get; set; }
public int created { get; set; }
public Choice[] choices { get; set; }
public Usage usage { get; set; }
}

public class Usage
{
public int prompt_tokens { get; set; }
public int completion_tokens { get; set; }
public int total_tokens { get; set; }
}

public class Choice
{
public int index { get; set; }
public Message message { get; set; }
public string finish_reason { get; set; }
}

}
public record HttpChatResponse(bool Success, string Data);
25 changes: 21 additions & 4 deletions website/chatui/Program.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
using chatui.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();
builder.Services.AddOptions<ChatApiOptions>()
.Bind(builder.Configuration)
.ValidateDataAnnotations()
.ValidateOnStart();

builder.Services.AddHttpClient("ChatClient")
.ConfigurePrimaryHttpMessageHandler(() =>
{
HttpClientHandler handler = new();

if (builder.Environment.IsDevelopment())
{
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true;
}

return handler;
});

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddControllersWithViews();

builder.Services.AddCors(options =>
{
Expand All @@ -21,8 +40,6 @@

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Expand Down
16 changes: 0 additions & 16 deletions website/chatui/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:30684",
"sslPort": 0
}
},
"profiles": {
"http": {
"commandName": "Project",
Expand All @@ -18,14 +10,6 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "Home",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Loading