🕓 7 MIN As 2024 wraps up, …
In this article, we’ll explore Uno Platform implementations using a ChatGPT-enabled chat application. We’ll look at using C# markup, which offers a XAML-like structure to efficiently manage the user interface and functionality. Additionally, we will discuss the Uno.Extensions.Configuration package, which provides robust configuration management, ensuring the secure handling of settings, such as API keys.
Furthermore, we’ll demonstrate the straightforward process of integrating OpenAI services into our .NET projects using NuGet packages and dependency injection. We’ll also introduce MVUX—a method for effective state management that supports data binding and promotes immutability.
By the end of this article, you’ll better understand how to use C# markup, MVUX, and Uno Extensions and apply these techniques in your own Uno Platform projects. YouÂ
C# Markup allows developers to use a consistent language for both design and functionality. Structured similarly to XAML, C# Markup simplifies the integration of elements, controls, and bindings, making it more accessible to handle UI tasks. Developers can leverage constructors and extension methods to add controls, set properties, and manage resources effortlessly. Additionally, breaking down code into helper methods improves both the readability and maintainability of the code.
For instance, consider our implementation of a `Prompt` method, designed to render the input textbox and send button section:
public MainPage()
{
this.Background(ThemeResource.Get("ApplicationPageBackgroundThemeBrush"))
.DataContext((page, vm) => page
.Content(
new Grid()
//Properties left out for brevity
.Children(
Header(vm),
Messages(vm),
Prompt(vm)
)
)
);
}
private Grid Prompt(BindableMainModel vm)
=> new Grid()
.Grid(row: 2)
.ColumnDefinitions("*, Auto")
.VerticalAlignment(VerticalAlignment.Bottom)
.HorizontalAlignment(HorizontalAlignment.Stretch)
.Children(
new TextBox()
.PlaceholderText("Message ChatGPT")
.CommandExtensions(x => x.Command(() => vm.AskMessage))
.Text(x => x.Bind(() => vm.Prompt)
.TwoWay()
.UpdateSourceTrigger(UpdateSourceTrigger.PropertyChanged)),
new Button()
.Grid(column: 1)
.Style(Theme.Button.Styles.Icon)
.Command(() => vm.AskMessage)
.Content(
new PathIcon()
.Data("M2.01 21L23 12L2.01 3L2 10L17 12L2 14L2.01 21Z")
)
);
To learn more about using C# Markup, please take a look at our C# Markup docs
Uno Platform provides strong configuration management capabilities, simplifying how developers handle and retrieve application settings. Through the Uno.Extensions.Configuration package, you can effortlessly read and write settings across multiple sources. This is particularly useful for accessing essential parameters like API keys.
For instance, in our ChatGPT example, we use this feature by adding an “ApiKey” attribute in the “AppConfig” section of the appsettings.json file.
{
"AppConfig": {
"Environment": "Production",
"ApiKey": ""
},
"ApiClient": {
"UseNativeHandler": true
}
}
Then we change the existing AppConfig
 record to have an ApiKey
 property that reflects the json structure:
public record AppConfig
{
public string? Environment { get; init; }
+ public string? ApiKey { get; init; }
}
Afterward, we can access the AppConfig
 class across our project. This is possible because we can obtain an IOptions<AppConfig> appConfig
 parameter through Dependency Injection.
By adding configurations to the appsettings.json file and accessing them through the AppConfig
 class, developers can simplify the integration of external services like OpenAI. This method improves security and flexibility by keeping sensitive information separate from the main code.
For more information, please see our Configuration docs.
Integrating OpenAI services into Uno Platform applications opens up a world of possibilities for creating intelligent and interactive experiences. With the Betalgo.OpenAI NuGet package, developers can access OpenAI services such as ChatGPT and DALL-E with ease.
The integration begins with setting up the OpenAiOptions
 class from OpenAI, this class is responsible for receiving our API Key. So we define a ChatAiOptions
 that derives from OpenAiOptions
 and in its constructor we obtain a IOptions<AppConfig>
 as parameter, where we can access our AppConfig.ApiKey
.
Once that is done, we can start working on our ChatService
 implementation. This class receive a IChatCompletionService
 as parameter, that was registered in the Dependency Injection container. We are particularly interrested on two methods of the implementation of thar interface, CreateCompletion
 (takes a request as parameter and returns an object with the AI response) and CreateCompletionAsStream
 (takes a request as parameter and asyncronously returns the AI response as it gets generated).
Users have the option to input a message to provide context for ChatGPT, guiding its conversation or behavior. For instance, they can provide background information or specific topics of interest. For our sample we use this in “You are Uno ChatGPT Sample, a helpful assistant helping users to learn more about how to develop using Uno Platform.”, ChatGPT can adopt a particular persona or focus its responses accordingly. For example, users could input lines like “You are Borat, a clueless journalist from Kazakhstan” or “You are Buzz Lightyear, a space ranger on a mission to infinity and beyond” allowing ChatGPT to respond in character or tailor its answers to match the chosen persona.
ChatGPT as Borat and Buzz Lightyear:
To ensure that this setup functions correctly, it’s essential to remember to register the necessary classes in the Dependency Injection container.
.ConfigureServices(
(context, services) =>
{
services
.AddSingleton()
.AddSingleton()
.AddSingleton();
})
With the ChatService acting as a bridge between the model and OpenAI services, developers can incorporate AI-driven functionalities into their applications.
For more information, please see our Dependency Injection docs.
MVUX, a pattern for state management, offers an alternative to traditional MVVM architectures. Built on the Model-View-Update paradigm, MVUX simplifies state management and the pain of dealing with MVVM while encouraging immutability. By defining models, views, and update actions, developers can create robust and responsive applications.
MVUX extends the concept of immutability through the use of records, ensuring that states remain consistent throughout the application lifecycle. This approach enhances reliability and predictability, crucial for building complex applications.
Within our app we have a MainModel
partial
record
where we defined the properties that will be bound to our view.
public IState Prompt => State.Value(this, () => string.Empty);
public IState UseStream => State.Value(this, () => CanStream);
public IListState Messages => ListState.Empty(this);
Also we have the command methods that will be triggered when the Send button is pressed and will connect with the OpenAI services, sending our request and getting a response. We defined two methods; the first one is Ask, which uses the ChatService.CompleteChatAsync(...)
method which returns the complete AI response and the AskAsStream
that uses the ChatService.CompleteChatStreamingAsync(...)
method that asyncronously returns the AI response as it gets generated. Take a look at how the AskAsStream
 was implemented:
private async ValueTask AskAsStream(string prompt, CancellationToken ct)
{
if (prompt is null or { Length: 0 })
{
return;
}
//Add the User prompt message to the conversation
await Messages.AddAsync(new Message(prompt), ct);
await Prompt.Set(string.Empty, ct);
//Add a Plaecholder loading message while awaiting the AI response
var message = Message.CreateLoading();
await Messages.AddAsync(message, ct);
//Create the request with the conversation history
var request = await CreateRequest();
//Send the request to AI Services
await foreach (var response in _chatService.AskAsStream(request).WithCancellation(ct))
{
//Finds the message with same id and updates the instance with new part of response
await Messages.UpdateAsync(message.With(response), ct);
}
}
Tags:
🕓 2 MIN Need a last-minute holiday …
Uno Platform
360 rue Saint-Jacques, suite G101,
Montréal, Québec, Canada
H2Y 1P5
USA/CANADA toll free: +1-877-237-0471
International: +1-514-312-6958
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.
Uno Platform 5.2 LIVE Webinar – Today at 3 PM EST – Watch