Skip to content

Commit 45ea4a2

Browse files
authored
feat (website): [refresh] use the new libs for Foundry (new) Agents backend (#64)
* update from net8 to net10 * update packages to their latest * migrate from Foundry (classic) to Foundry (new) projects API * update zip * add instructions to publish an agent with a dedicated endpoint * update foundry agents URL from classic to Microsoft Foundry (new) highlights: - foundry (new) uses new REST api `/agents` endpoint while classic targets `/assistants` * minor improvment to retrieve the agent id * update app service runtime from 8.0 to 10.0
1 parent 431f3b5 commit 45ea4a2

8 files changed

Lines changed: 51 additions & 48 deletions

File tree

.devcontainer/devcontainer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "AI Agent service chat basic reference implementation",
3-
"image": "mcr.microsoft.com/devcontainers/dotnet:dev-8.0-jammy",
3+
"image": "mcr.microsoft.com/devcontainers/dotnet:dev-10.0",
44
"runArgs": ["--network=host"],
55
"remoteUser": "vscode",
66
"features": {
@@ -13,7 +13,7 @@
1313
"ms-azuretools.vscode-bicep"
1414
],
1515
"settings": {}
16-
}
16+
}
1717
},
1818
"forwardPorts": []
19-
}
19+
}

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ To test this scenario, you'll be deploying a prompt-based AI agent. The agent us
154154

155155
1. Click **Save** buton at the top-right of the Agents' Playground panel.
156156

157+
1. Click **Publish** button at the top-right of the Agents' Playground panel and then click **Publish Agent** button.
158+
159+
1. From the confirmation dialog click **Publish** button.
160+
161+
This will promote an agent from a development asset into a managed Azure resource with a dedicated endpoint.
162+
157163
### 3. Test the agent from the Foundry portal in the playground
158164

159165
Here you'll test your orchestration agent by invoking it directly from the Foundry portal agent playground chat experience.
@@ -175,15 +181,15 @@ Workloads build chat functionality into an application. Those interfaces usually
175181
```bash
176182
FOUNDRY_NAME="aif${BASE_NAME}"
177183
FOUNDRY_PROJECT_NAME="projchat"
178-
FOUNDRY_AGENT_CREATE_URL="https://${FOUNDRY_NAME}.services.ai.azure.com/api/projects/${FOUNDRY_PROJECT_NAME}/assistants?api-version=2025-05-15-preview"
184+
FOUNDRY_AGENTS_URL="https://${FOUNDRY_NAME}.services.ai.azure.com/api/projects/${FOUNDRY_PROJECT_NAME}/agents?api-version=2025-11-15-preview"
179185

180-
echo $FOUNDRY_AGENT_CREATE_URL
186+
echo $FOUNDRY_AGENTS_URL
181187
```
182188

183189
1. Get Agent ID value.
184190

185191
```bash
186-
AGENT_ID=$(az rest -u $FOUNDRY_AGENT_CREATE_URL -m "get" --resource "https://ai.azure.com" --query 'data[0].id' -o tsv)
192+
AGENT_ID=$(az rest -u $FOUNDRY_AGENTS_URL -m "get" --resource "https://ai.azure.com" --query last_id -o tsv)
187193

188194
echo $AGENT_ID
189195
````

infra-as-code/bicep/web-app.bicep

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ resource webApp 'Microsoft.Web/sites@2024-04-01' = {
126126
http20Enabled: false
127127
publicNetworkAccess: 'Enabled'
128128
alwaysOn: true
129-
linuxFxVersion: 'DOTNETCORE|8.0'
129+
linuxFxVersion: 'DOTNETCORE|10.0'
130130
netFrameworkVersion: null
131131
windowsFxVersion: null
132132
}

website/chatui.zip

4.98 MB
Binary file not shown.
Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
using Microsoft.AspNetCore.Mvc;
22
using Microsoft.Extensions.Options;
3-
using Azure;
4-
using Azure.AI.Agents.Persistent;
3+
using Azure.AI.Projects;
4+
using Azure.AI.Projects.OpenAI;
5+
using OpenAI.Responses;
56
using chatui.Configuration;
67

78
namespace chatui.Controllers;
@@ -10,56 +11,51 @@ namespace chatui.Controllers;
1011
[Route("[controller]/[action]")]
1112

1213
public class ChatController(
13-
PersistentAgentsClient client,
14+
AIProjectClient projectClient,
1415
IOptionsMonitor<ChatApiOptions> options,
1516
ILogger<ChatController> logger) : ControllerBase
1617
{
17-
private readonly PersistentAgentsClient _client = client;
18+
private readonly AIProjectClient _projectClient = projectClient;
1819
private readonly IOptionsMonitor<ChatApiOptions> _options = options;
1920
private readonly ILogger<ChatController> _logger = logger;
2021

21-
// TODO: [security] Do not trust client to provide threadId. Instead map current user to their active threadid in your application's own state store.
22-
// Without this security control in place, a user can inject messages into another user's thread.
23-
[HttpPost("{threadId}")]
24-
public async Task<IActionResult> Completions([FromRoute] string threadId, [FromBody] string prompt)
22+
// TODO: [security] Do not trust client to provide conversationId. Instead map current user to their active converstaionid in your application's own state store.
23+
// Without this security control in place, a user can inject messages into another user's conversation.
24+
[HttpPost("{conversationId}")]
25+
public async Task<IActionResult> Completions([FromRoute] string conversationId, [FromBody] string message)
2526
{
26-
if (string.IsNullOrWhiteSpace(prompt))
27-
throw new ArgumentException("Prompt cannot be null, empty, or whitespace.", nameof(prompt));
27+
if (string.IsNullOrWhiteSpace(message))
28+
throw new ArgumentException("Message cannot be null, empty, or whitespace.", nameof(message));
29+
_logger.LogDebug("Prompt received {Prompt}", message);
2830

29-
_logger.LogDebug("Prompt received {Prompt}", prompt);
30-
var _config = _options.CurrentValue;
31-
32-
PersistentThreadMessage message = await _client.Messages.CreateMessageAsync(
33-
threadId,
34-
MessageRole.User,
35-
prompt);
31+
#pragma warning disable OPENAI001
32+
MessageResponseItem userMessageResponseItem = ResponseItem.CreateUserMessageItem(
33+
[ResponseContentPart.CreateInputTextPart(message)]);
3634

37-
ThreadRun run = await _client.Runs.CreateRunAsync(threadId, _config.AIAgentId);
35+
var _config = _options.CurrentValue;
36+
AgentRecord agentRecord = await _projectClient.Agents.GetAgentAsync(_config.AIAgentId);
37+
var agent = agentRecord.Versions.Latest;
3838

39-
while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress || run.Status == RunStatus.RequiresAction)
40-
{
41-
await Task.Delay(TimeSpan.FromMilliseconds(500));
42-
run = (await _client.Runs.GetRunAsync(threadId, run.Id)).Value;
43-
}
39+
ProjectResponsesClient responsesClient
40+
= _projectClient.OpenAI.GetProjectResponsesClientForAgent(agent, conversationId);
4441

45-
Pageable<PersistentThreadMessage> messages = _client.Messages.GetMessages(
46-
threadId: threadId, order: ListSortOrder.Ascending);
42+
var agentResponseItem = await responsesClient.CreateResponseAsync([userMessageResponseItem]);
4743

48-
var fullText =
49-
messages
50-
.Where(m => m.Role == MessageRole.Agent)
51-
.SelectMany(m => m.ContentItems.OfType<MessageTextContent>())
52-
.Last().Text;
44+
var fullText = agentResponseItem.Value.GetOutputText();
5345

5446
return Ok(new { data = fullText });
5547
}
5648

5749
[HttpPost]
58-
public async Task<IActionResult> Threads()
50+
public async Task<IActionResult> Conversations()
5951
{
60-
// TODO [performance efficiency] Delay creating a thread until the first user message arrives.
61-
PersistentAgentThread thread = await _client.Threads.CreateThreadAsync();
52+
// TODO [performance efficiency] Delay creating a conversation until the first user message arrives.
53+
ProjectConversationCreationOptions conversationOptions = new();
54+
55+
ProjectConversation conversation
56+
= await _projectClient.OpenAI.Conversations.CreateProjectConversationAsync(
57+
conversationOptions);
6258

63-
return Ok(new { id = thread.Id });
59+
return Ok(new { id = conversation.Id });
6460
}
6561
}

website/chatui/Program.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using Microsoft.Extensions.Options;
2-
using Azure.AI.Agents.Persistent;
2+
using Azure.AI.Projects;
33
using Azure.Identity;
44
using chatui.Configuration;
55

@@ -13,9 +13,9 @@
1313
builder.Services.AddSingleton((provider) =>
1414
{
1515
var config = provider.GetRequiredService<IOptions<ChatApiOptions>>().Value;
16-
PersistentAgentsClient client = new(config.AIProjectEndpoint, new DefaultAzureCredential());
16+
AIProjectClient projectClient = new(new Uri(config.AIProjectEndpoint), new DefaultAzureCredential());
1717

18-
return client;
18+
return projectClient;
1919
});
2020

2121
builder.Services.AddControllersWithViews();

website/chatui/Views/Home/Index.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@
220220
}
221221
222222
async function createThread() {
223-
const response = await fetch("/chat/threads", {
223+
const response = await fetch("/chat/conversations", {
224224
method: "POST",
225225
headers: { "Content-Type": "application/json" }
226226
});

website/chatui/chatui.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22
<PropertyGroup>
3-
<TargetFramework>net8.0</TargetFramework>
3+
<TargetFramework>net10.0</TargetFramework>
44
<Nullable>enable</Nullable>
55
<ImplicitUsings>enable</ImplicitUsings>
6+
<EnablePreviewFeatures>true</EnablePreviewFeatures>
67
</PropertyGroup>
78
<ItemGroup>
89
<Folder Include="wwwroot\" />
910
</ItemGroup>
1011
<ItemGroup>
11-
<PackageReference Include="Azure.AI.Agents.Persistent" Version="1.1.0" />
12-
<PackageReference Include="Azure.Identity" Version="1.17.0" />
12+
<PackageReference Include="Azure.AI.Projects" Version="1.2.0-beta.5" />
13+
<PackageReference Include="Azure.Identity" Version="1.17.1" />
1314
</ItemGroup>
1415
</Project>

0 commit comments

Comments
 (0)