Skip to content

Commit 5e9fff2

Browse files
authored
Add a sample middle tier implementation for .NET (using ASP.NET Core MVC) (#105)
* initial implementation of a dotnet middle tier * move folder name for consistency
1 parent 88be423 commit 5e9fff2

File tree

12 files changed

+553
-0
lines changed

12 files changed

+553
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Azure.AI.OpenAI" Version="2.1.0-beta.2" />
11+
<PackageReference Include="Azure.Identity" Version="1.13.1" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.11.35431.28
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreMvcRealtimeMiddletier", "AspNetCoreMvcRealtimeMiddletier.csproj", "{B98EE1EC-288D-4EF5-B813-64E8F2FB476C}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{B98EE1EC-288D-4EF5-B813-64E8F2FB476C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{B98EE1EC-288D-4EF5-B813-64E8F2FB476C}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{B98EE1EC-288D-4EF5-B813-64E8F2FB476C}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{B98EE1EC-288D-4EF5-B813-64E8F2FB476C}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {73B1440A-2054-4ED0-AA09-75FA0EC80DF2}
24+
EndGlobalSection
25+
EndGlobal
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System.Text.Json;
2+
using System.Text.Json.Serialization;
3+
4+
namespace AspNetCoreMvcRealtimeMiddletier.ClientMessages;
5+
6+
/// <summary>
7+
/// A base representation of a simplified protocol message communicated between the client frontend and this middle
8+
/// tier implementation.
9+
/// </summary>
10+
/// <param name="type"></param>
11+
[JsonConverter(typeof(ClientMessageJsonConverter))]
12+
public abstract class ClientMessage(string type)
13+
{
14+
[JsonPropertyName("type")]
15+
public string Type { get; set; } = type;
16+
17+
/// <summary>
18+
/// A converter is used to work around limitations with nested polymorphism via [JsonPolymorphic] and [JsonDerivedType].
19+
/// </summary>
20+
private class ClientMessageJsonConverter : JsonConverter<ClientMessage>
21+
{
22+
public override ClientMessage? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
23+
{
24+
using JsonDocument messageDocument = JsonDocument.ParseValue(ref reader);
25+
26+
foreach (JsonProperty property in messageDocument.RootElement.EnumerateObject())
27+
{
28+
if (property.NameEquals("type"u8) && property.Value.GetString() is string typeDiscriminator)
29+
{
30+
if (typeDiscriminator == "user_message")
31+
{
32+
return messageDocument.Deserialize<ClientReceivableUserMessage>();
33+
}
34+
}
35+
}
36+
37+
throw new NotImplementedException();
38+
}
39+
40+
public override void Write(Utf8JsonWriter writer, ClientMessage value, JsonSerializerOptions options)
41+
{
42+
JsonSerializer.Serialize(writer, value, value.GetType(), options);
43+
}
44+
}
45+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace AspNetCoreMvcRealtimeMiddletier.ClientMessages;
4+
5+
/// <summary>
6+
/// The base representation of a simplified protocol message that can sent by the frontend client and received by
7+
/// this middle tier implementation.
8+
/// </summary>
9+
/// <param name="type"></param>
10+
public abstract class ClientReceivableMessage(string type) : ClientMessage(type)
11+
{ }
12+
13+
/// <summary>
14+
/// A user message input, typically associated with text provided by the user.
15+
/// </summary>
16+
/// <param name="text"></param>
17+
public class ClientReceivableUserMessage(string text) : ClientReceivableMessage("user_message")
18+
{
19+
[JsonPropertyName("text")]
20+
public string Text { get; } = text;
21+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace AspNetCoreMvcRealtimeMiddletier.ClientMessages;
4+
5+
/// <summary>
6+
/// A base representation of a simplified protocol message that can be sent from this middle tier implementation to
7+
/// a client frontend.
8+
/// </summary>
9+
/// <param name="type"></param>
10+
public abstract class ClientSendableMessage(string type) : ClientMessage(type)
11+
{ }
12+
13+
/// <summary>
14+
/// A base representation of a simplified protocol control message that can be sentfrom this middle tier implementation
15+
/// to a client frontend.
16+
/// </summary>
17+
/// <param name="action"></param>
18+
public abstract class ClientSendableControlMessage(string action) : ClientSendableMessage("control")
19+
{
20+
[JsonPropertyName("action")]
21+
public string Action { get; } = action;
22+
}
23+
24+
/// <summary>
25+
/// A message sent from this middle tier implementation to the client frontend upon successful connection
26+
/// establishment. Includes a brief greeting message.
27+
/// </summary>
28+
/// <param name="greeting"></param>
29+
public class ClientSendableConnectedMessage(string greeting) : ClientSendableControlMessage("connected")
30+
{
31+
[JsonPropertyName("greeting")]
32+
public string Greeting { get; set; } = greeting;
33+
}
34+
35+
/// <summary>
36+
/// A message sent from this middle tier implementation to the client frontend when a start of speech is detected in
37+
/// the user input audio.
38+
/// </summary>
39+
public class ClientSendableSpeechStartedMessage : ClientSendableControlMessage
40+
{
41+
public ClientSendableSpeechStartedMessage() : base("speech_started") { }
42+
}
43+
44+
/// <summary>
45+
/// A message sent from this middle tier implementation to the client frontend when a new, incremental piece of
46+
/// generated text content is available.
47+
/// </summary>
48+
/// <param name="delta"></param>
49+
/// <param name="contentId"></param>
50+
public class ClientSendableTextDeltaMessage(string delta, string contentId) : ClientSendableMessage("text_delta")
51+
{
52+
[JsonPropertyName("delta")]
53+
public string Delta { get; set; } = delta;
54+
55+
[JsonPropertyName("id")]
56+
public string ContentId { get; set; } = contentId;
57+
}
58+
59+
/// <summary>
60+
/// A message sent from this middle tier implementation to the client frontend when a transcription of user input
61+
/// audio is available.
62+
/// </summary>
63+
/// <param name="eventId"></param>
64+
/// <param name="transcription"></param>
65+
public class ClientSendableTranscriptionMessage(string eventId, string transcription) : ClientSendableMessage("transcription")
66+
{
67+
[JsonPropertyName("id")]
68+
public string EventId { get; set; } = eventId;
69+
70+
[JsonPropertyName("text")]
71+
public string Transcription { get; set; } = transcription;
72+
}

0 commit comments

Comments
 (0)