Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added CloudMachine README Walkthrough #47025

Merged
merged 13 commits into from
Nov 8, 2024
278 changes: 278 additions & 0 deletions sdk/cloudmachine/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
# Azure CloudMachine

Write Azure apps in 5 minutes

## Getting started

### Prerequisites

> You must have an [Azure subscription](https://azure.microsoft.com/free/dotnet/).
> You must have .NET 8 (or higher) installed
> You must have Azure CLI installed
> You must have Azure Developer CLI installed
> You must have npm installed
> You must be logged into Azure CLI and Azure Developer CLI

### Walkthrough

#### Create Server Project

In a command line window type
```dotnetcli
mkdir cmdemo
cd cmdemo
mkdir server
cd server
dotnet new web
```

Add `Azure.CloudMachine.All` package
```dotnetcli
dotnet add package Azure.CloudMachine.All --prerelease
```
#### Use Azure Dev CLI to provision CloudMachine

Open Program.cs file and add the following two lines of code to the top of the file
```csharp
using Azure.Provisioning.CloudMachine;

if (CloudMachineInfrastructure.Configure(args)) return;
```
The `CloudMachineInfrastructure.Configure` call allows running the app with `-bicep` switch, which generate bicep files required to provision resources in Azure. Let's generate these bicep files now.
```dotnetcli
dotnet run -bicep
```
As you can see, a folder called `infra` was created and it should have several bicep files in it. Let's now initialize the project for azd.
```dotnetcli
azd init
```
select template, choose 'yes' when asked 'Continue initializing an app here?', choose the 'minimal' template, and use 'cmserver' as the environment name

Once the initialization completes, let's provision the resources. Select `eastus` as the region
```dotnetcli
azd provision
```
When provisioning finishes, you should see something like the following in the console output
```dotnetcli
(✓) Done: Resource group: cm125957681369428 (627ms)
```
And if you go to your Azure portal, or execute the following az cli command to see the resource group created. The resource group will have server resources such us Storage, ServiceBus, and EventGrid.
```dotnetcli
az resource list --resource-group <resource_group_from_command_line> --output table
```

#### Use CDK to add resources to the CloudMachine

Since we are writing an AI application, we need to provision Azure OpenAI resources. To do this, add the follwoing class to the end of the Program.cs file:
```csharp
class AssistantService {
internal static void Configure(CloudMachineInfrastructure cm) {
cm.AddFeature(new OpenAIFeature() { Chat = new AIModel("gpt-4o-mini", "2024-07-18") });
}
}
```
And change the infrastructure configuration call att he begining of the file to:
```csharp
if (CloudMachineInfrastructure.Configure(args, AssistantService.Configure)) return;
```
And now regenerate new bicep with the new Azure OpenAI resources, and re-provision
```dotnetcli
dotnet run -bicep
azd provision
```

#### Call CloudMachine APIs

You are now ready to call Azure OpenAI service from the app. To do this, add `CloudMachineClient` field and a `Chat` method to `AssistantService`:
```csharp
class AssistantService {
CloudMachineClient cm = new CloudMachineClient();

public async Task<string> Chat(string message) {
var client = cm.GetOpenAIChatClient();
ChatCompletion completion = await client.CompleteChatAsync(message);
return completion.Content[0].Text;
}
}
```
Lastly, create an instance of the service and call the `Chat` method when the app users browses the app:
```csharp
var service = new AssistantService();
app.MapGet("/", async () => {
return await service.Chat("List all noble gases");
});
```
The full program should look like following:
```csharp
using Azure.CloudMachine;
using Azure.CloudMachine.OpenAI;
using Azure.Provisioning.CloudMachine;
using Azure.Provisioning.CloudMachine.OpenAI;
using OpenAI.Chat;

if (CloudMachineInfrastructure.Configure(args, AssistantService.Configure)) return;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var service = new AssistantService();
app.MapGet("/", async () => {
return await service.Chat("List all noble gases");
});

app.Run();

class AssistantService {
CloudMachineClient cm = new CloudMachineClient();

public async Task<string> Chat(string message) {
var client = cm.GetOpenAIChatClient();
ChatCompletion completion = await client.CompleteChatAsync(message);
return completion.Content[0].Text;
}

internal static void Configure(CloudMachineInfrastructure cm) {
cm.AddFeature(new OpenAIFeature() {
Chat = new AIModel("gpt-4o-mini", "2024-07-18")
});
}
}
```
You can now start the application
```dotnetcli
dotnet run
```
and browse to the URI printed in the console.

#### Use TDK to expose Web APIs and generate TypeSpec
First, let's define an API we want to expose. We will do it using an interface. Add the following interface to the end of Program.cs:
```csharp
interface IAssistantService {
Task<string> Chat(string message);
}
```
Make sure that the `AssistantService` implements the interface:
```csharp
class AssistantService : IAssistantService
```
Expose the service methods as web APIs by adding the following line after the existing 'var service = new AssistantService();' line:
```csharp
app.Map(service);
```
Lastly, add the ability to generate TypeSpec for the new API by adding new statement to the `Configure` method
```csharp
cm.AddEndpoints<IAssistantService>();
```
Your program shoud now look like following:
```csharp
using Azure.CloudMachine;
using Azure.CloudMachine.OpenAI;
using Azure.Provisioning.CloudMachine;
using Azure.Provisioning.CloudMachine.OpenAI;
using OpenAI.Chat;
using System.ClientModel.TypeSpec;

if (CloudMachineInfrastructure.Configure(args, AssistantService.Configure)) return;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var service = new AssistantService();
app.Map(service);
app.MapGet("/", async () => {
return await service.Chat("List all noble gasses");
});

app.Run();

class AssistantService : IAssistantService {
CloudMachineClient cm = new CloudMachineClient();

public async Task<string> Chat(string message) {
var client = cm.GetOpenAIChatClient();
ChatCompletion completion = await client.CompleteChatAsync(message);
return completion.Content[0].Text;
}

internal static void Configure(CloudMachineInfrastructure cm) {
cm.AddFeature(new OpenAIFeature() {
Chat = new AIModel("gpt-4o-mini", "2024-07-18")
});
cm.AddEndpoints<IAssistantService>();
}
}

interface IAssistantService {
Task<string> Chat(string message);
}
```
You can now start the application and browse to the /chat endpoint [TDB]

But what's more interesting, you can run the app with the -tsp switch to generate TypeSpec for the endpoint:
```dotnetcli
dotnet run -tsp
```
This will create a `tsp` directory, AssistantService.tsp file, with the following contents:
```tsp
import "@typespec/http";
import "@typespec/rest";
import "@azure-tools/typespec-client-generator-core";

@service({
title: "AssistantService",
})

namespace AssistantService;

using TypeSpec.Http;
using TypeSpec.Rest;
using Azure.ClientGenerator.Core;

@client interface AssistantServiceClient {
@get @route("chat") Chat(@body message: string) : {
@statusCode statusCode: 200;
@body response : string;
};
}
```
#### Generate client libraries from TypeSpec
Let's now generate and build a C# client library from the `AssistantService.tsp` file:
```dotnetcli
cd ..
npm install @typespec/http-client-csharp
tsp compile .\server\tsp\AssistantService.tsp --emit "@typespec/http-client-csharp"
dotnet build tsp-output\@typespec\http-client-csharp\src\AssistantService.csproj
```

#### Create command line client app for the service
```dotnetcli
mkdir cmdclient
cd cmdclient
dotnet new console
dotnet add reference ..\tsp-output\@typespec\http-client-csharp\src\AssistantService.csproj
```
And change the Program.cs file to the following, replacing the client URI with the URI in your server's launchsettings.json file ('cmdemo\server\Properties' folder)

```csharp
using AssistantService;

var client = new AssistantServiceClient(new Uri("http://localhost:5121/"));

while(true){
string message = Console.ReadLine();
var completion = client.Chat(message);
Console.WriteLine(completion);
}
```
Now start the server
```dotnetcli
start cmd
cd..
cd server
dotnet run
```
Go back to the client project command window and start the client:
```dotnetcli
dotnet run
```
You can use the simple command line app to ask Azure OpenAI some questions.