Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c16b23b
feat: add number guessing workflow with custom agent executors
huimiu Aug 12, 2025
5f7b0cc
feat: enhance guessing and judging agent instructions for clarity and…
huimiu Aug 12, 2025
581082a
feat: refactor guessing game workflow to implement student-teacher in…
huimiu Aug 12, 2025
d1597a0
Update python workflow sample
huimiu Sep 9, 2025
86ac02e
Refactor student-teacher workflow implementation
huimiu Sep 28, 2025
00e13fa
Remove dotenv loading from simple_workflow.py
huimiu Sep 28, 2025
7d106e3
Refine agent instructions and observability port
huimiu Oct 10, 2025
fd082b7
feat: migrating hosted agent samples
huimiu Feb 28, 2026
9e4d2ae
chore: update upload-artifact action to v4
huimiu Feb 28, 2026
dcc94b9
feat: enable manual triggering of the package hosted agents workflow
huimiu Feb 28, 2026
42ae22a
fix: correct lastUpdated timestamp for pre-release channel in version…
huimiu Feb 28, 2026
cb815cd
feat: add version reading and tagging for hosted agent packages
huimiu Feb 28, 2026
409da7d
refactor: add workflow for packaging hosted agents with versioning an…
huimiu Feb 28, 2026
c76820e
fix: correct file path in workflow trigger for package_sample.yml
huimiu Feb 28, 2026
d55dbfa
fix: update download URLs and add language support in version manifest
huimiu Mar 1, 2026
a7df7f5
fix: remove unnecessary blank line in version manifest
huimiu Mar 4, 2026
d344390
feat: add release workflow for hosted agents with packaging and uploa…
huimiu Mar 4, 2026
1ac9c58
feat: implement hosted agent workflow with multi-agent collaboration …
huimiu Mar 4, 2026
23067d4
feat: enhance release workflow to support channel-based versioning an…
huimiu Mar 4, 2026
a0388d6
fix: disable release notes generation in release workflow
huimiu Mar 5, 2026
84f9c0e
fix: update stable branch in version manifest and remove pre-release …
huimiu Mar 5, 2026
48b5465
feat: migrating hosted agent samples
huimiu Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions .github/workflows/release_sample.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: Release Hosted Agents

on:
workflow_dispatch:
inputs:
channel:
description: "Release channel"
required: true
type: choice
options:
- stable
- pre-release
default: stable

permissions:
contents: write

jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.tag }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Read version and branch from manifest
id: version
run: |
CHANNEL="${{ github.event.inputs.channel }}"
VERSION=$(jq -r ".channels.\"$CHANNEL\".sampleVersion" samples/hosted-agent/version-manifest.json)
BRANCH=$(jq -r ".channels.\"$CHANNEL\".branch" samples/hosted-agent/version-manifest.json)
echo "tag=$VERSION" >> $GITHUB_OUTPUT
echo "branch=$BRANCH" >> $GITHUB_OUTPUT

- name: Checkout sample branch
uses: actions/checkout@v4
with:
ref: ${{ steps.version.outputs.branch }}
path: sample-source

- name: Copy samples from target branch
run: |
rm -rf samples/hosted-agent/dotnet samples/hosted-agent/python
cp -r sample-source/samples/hosted-agent/dotnet samples/hosted-agent/dotnet
cp -r sample-source/samples/hosted-agent/python samples/hosted-agent/python

- name: Create dotnet agent package
run: |
cd samples/hosted-agent
zip -r dotnet.zip dotnet/
ls -lh dotnet.zip

- name: Create python agent package
run: |
cd samples/hosted-agent
zip -r python.zip python/
ls -lh python.zip

- name: Upload packages as artifacts
uses: actions/upload-artifact@v4
with:
name: hosted-agents-packages
path: |
samples/hosted-agent/dotnet.zip
samples/hosted-agent/python.zip
retention-days: 30

release:
needs: build
runs-on: ubuntu-latest
# ------------------------------------------------------------
# This environment requires manual approval before proceeding.
# Go to: Settings → Environments → release-approval
# Add the specific GitHub users/teams as "Required reviewers".
# They will receive a notification with an approval link when
# this workflow runs.
# ------------------------------------------------------------
environment: release-approval
steps:
- name: Download packages
uses: actions/download-artifact@v4
with:
name: hosted-agents-packages
path: samples/hosted-agent/

- name: Upload packages to Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.build.outputs.version }}
name: Hosted Agents ${{ needs.build.outputs.version }}
prerelease: ${{ github.event.inputs.channel == 'pre-release' }}
files: |
samples/hosted-agent/dotnet.zip
samples/hosted-agent/python.zip
generate_release_notes: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57 changes: 57 additions & 0 deletions samples/hosted-agent/dotnet/agent/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Build outputs
bin/
obj/
out/

# IDE and editor files
.vs/
.vscode/
*.user
*.suo
*.sln.docstates
.foundry/

# Git
.git/
.gitignore

# Documentation and samples (not needed in container)
*.md
*.http

# Ignore files
.dockerignore

# Logs
*.log

# Temporary files
*.tmp
*.temp

# OS files
.DS_Store
Thumbs.db

# Package manager directories
node_modules/
packages/

# Test results
TestResults/
*.trx

# Coverage reports
coverage/
*.coverage
*.coveragexml

# Environment files with secrets
.env
.env.*
*.local
appsettings.*.json
!appsettings.json

.venv/
__pycache__/
20 changes: 20 additions & 0 deletions samples/hosted-agent/dotnet/agent/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Build the application
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
WORKDIR /src

# Copy files from the current directory on the host to the working directory in the container
COPY . .

RUN dotnet restore
RUN dotnet build -c Release --no-restore
RUN dotnet publish -c Release --no-build -o /app

# Run the application
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
WORKDIR /app

# Copy everything needed to run the app from the "build" stage.
COPY --from=build /app .

EXPOSE 8088
ENTRYPOINT ["dotnet", "{{SafeProjectName}}.dll"]
136 changes: 136 additions & 0 deletions samples/hosted-agent/dotnet/agent/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Seattle Hotel Agent - A simple agent with a tool to find hotels in Seattle.
// Uses Microsoft Agent Framework with Azure AI Foundry.
// Ready for deployment to Foundry Hosted Agent service.

using System.ComponentModel;
using System.Globalization;
using System.Text;
using System.ClientModel.Primitives;
using Azure.AI.AgentServer.AgentFramework.Extensions;
using Azure.AI.OpenAI;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

// Get configuration from environment variables
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT")
?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("MODEL_DEPLOYMENT_NAME") ?? "gpt-4.1-mini";
Console.WriteLine($"Project Endpoint: {endpoint}");
Console.WriteLine($"Model Deployment: {deploymentName}");
// Simulated hotel data for Seattle
var seattleHotels = new[]
{
new Hotel("Contoso Suites", 189, 4.5, "Downtown"),
new Hotel("Fabrikam Residences", 159, 4.2, "Pike Place Market"),
new Hotel("Alpine Ski House", 249, 4.7, "Seattle Center"),
new Hotel("Margie's Travel Lodge", 219, 4.4, "Waterfront"),
new Hotel("Northwind Inn", 139, 4.0, "Capitol Hill"),
new Hotel("Relecloud Hotel", 99, 3.8, "University District"),
};

[Description("Get available hotels in Seattle for the specified dates. This simulates a call to a hotel availability API.")]
string GetAvailableHotels(
[Description("Check-in date in YYYY-MM-DD format")] string checkInDate,
[Description("Check-out date in YYYY-MM-DD format")] string checkOutDate,
[Description("Maximum price per night in USD (optional, defaults to 500)")] int maxPrice = 500)
{
try
{
// Parse dates
if (!DateTime.TryParseExact(checkInDate, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var checkIn))
{
return $"Error parsing check-in date. Please use YYYY-MM-DD format.";
}

if (!DateTime.TryParseExact(checkOutDate, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var checkOut))
{
return $"Error parsing check-out date. Please use YYYY-MM-DD format.";
}

// Validate dates
if (checkOut <= checkIn)
{
return "Error: Check-out date must be after check-in date.";
}

var nights = (checkOut - checkIn).Days;

// Filter hotels by price
var availableHotels = seattleHotels.Where(h => h.PricePerNight <= maxPrice).ToList();

if (availableHotels.Count == 0)
{
return $"No hotels found in Seattle within your budget of ${maxPrice}/night.";
}

// Build response
var result = new StringBuilder();
result.AppendLine($"Available hotels in Seattle from {checkInDate} to {checkOutDate} ({nights} nights):");
result.AppendLine();

foreach (var hotel in availableHotels)
{
var totalCost = hotel.PricePerNight * nights;
result.AppendLine($"**{hotel.Name}**");
result.AppendLine($" Location: {hotel.Location}");
result.AppendLine($" Rating: {hotel.Rating}/5");
result.AppendLine($" ${hotel.PricePerNight}/night (Total: ${totalCost})");
result.AppendLine();
}

return result.ToString();
}
catch (Exception ex)
{
return $"Error processing request. Details: {ex.Message}";
}
}

// Create chat client using AIProjectClient to get the OpenAI connection from the project
var credential = new DefaultAzureCredential();
AIProjectClient projectClient = new AIProjectClient(new Uri(endpoint), credential);

// Get the OpenAI connection from the project
ClientConnection connection = projectClient.GetConnection(typeof(AzureOpenAIClient).FullName!);

if (!connection.TryGetLocatorAsUri(out Uri? openAiEndpoint) || openAiEndpoint is null)
{
throw new InvalidOperationException("Failed to get OpenAI endpoint from project connection.");
}
openAiEndpoint = new Uri($"https://{openAiEndpoint.Host}");
Console.WriteLine($"OpenAI Endpoint: {openAiEndpoint}");

var chatClient = new AzureOpenAIClient(openAiEndpoint, credential)
.GetChatClient(deploymentName)
.AsIChatClient()
.AsBuilder()
.UseOpenTelemetry(sourceName: "Agents", configure: cfg => cfg.EnableSensitiveData = false)
.Build();

var agent = new ChatClientAgent(chatClient,
name: "SeattleHotelAgent",
instructions: """
You are a helpful travel assistant specializing in finding hotels in Seattle, Washington.

When a user asks about hotels in Seattle:
1. Ask for their check-in and check-out dates if not provided
2. Ask about their budget preferences if not mentioned
3. Use the GetAvailableHotels tool to find available options
4. Present the results in a friendly, informative way
5. Offer to help with additional questions about the hotels or Seattle

Be conversational and helpful. If users ask about things outside of Seattle hotels,
politely let them know you specialize in Seattle hotel recommendations.
""",
tools: [AIFunctionFactory.Create(GetAvailableHotels)])
.AsBuilder()
.UseOpenTelemetry(sourceName: "Agents", configure: cfg => cfg.EnableSensitiveData = false)
.Build();

Console.WriteLine("Seattle Hotel Agent Server running on http://localhost:8088");
await agent.RunAIAgentAsync(telemetrySourceName: "Agents");

// Hotel record for simulated data
record Hotel(string Name, int PricePerNight, double Rating, string Location);
Loading