Skip to content

Commit cfbc4e6

Browse files
eerhardtsebastienrosmitchdennydanmoseleyCopilot
authored
[release/9.4] Add GitHub Models integration (#10170) (#10313)
* Add GitHub Models integration (#10170) * Add GitHub Models integration * Fix parameters and doc * Add package logo * Support GITHUB_TOKEN env * Fix test * Rename to GithubModel * Remove iisexpress in launch * Update playground/GitHubModelsEndToEnd/GitHubModelsEndToEnd.AppHost/GitHubModelsEndToEnd.AppHost.csproj Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com> * Define running state on github models * Add organization * Add tests * Add health checks * Add manifest files * Make health checks opt-in * Feedback * Fix test --------- Co-authored-by: Mitch Denny <midenn@microsoft.com> Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com> * Apply suggestions from code review Co-authored-by: Dan Moseley <danmose@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Sébastien Ros <sebastienros@gmail.com> Co-authored-by: Mitch Denny <midenn@microsoft.com> Co-authored-by: Dan Moseley <danmose@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 6b5ef99 commit cfbc4e6

29 files changed

+1214
-1
lines changed

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ When running tests in automated environments (including Copilot agent), **always
5555

5656
```bash
5757
# Correct - excludes quarantined tests (use this in automation)
58-
dotnet.sh test tests/Project.Tests/Project.Tests.csproj --filter-not-trait "quarantined=true"
58+
dotnet.sh test tests/Project.Tests/Project.Tests.csproj -- --filter-not-trait "quarantined=true"
5959

6060
# For specific test filters, combine with quarantine exclusion
6161
dotnet.sh test tests/Project.Tests/Project.Tests.csproj -- --filter "TestName" --filter-not-trait "quarantined=true"

Aspire.slnx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
<Project Path="src/Aspire.Hosting.AppHost/Aspire.Hosting.AppHost.csproj" />
5050
<Project Path="src/Aspire.Hosting.Docker/Aspire.Hosting.Docker.csproj" />
5151
<Project Path="src/Aspire.Hosting.Garnet/Aspire.Hosting.Garnet.csproj" />
52+
<Project Path="src/Aspire.Hosting.GitHub.Models/Aspire.Hosting.GitHub.Models.csproj" />
5253
<Project Path="src/Aspire.Hosting.Kafka/Aspire.Hosting.Kafka.csproj" />
5354
<Project Path="src/Aspire.Hosting.Keycloak/Aspire.Hosting.Keycloak.csproj" />
5455
<Project Path="src/Aspire.Hosting.Kubernetes/Aspire.Hosting.Kubernetes.csproj" />
@@ -163,6 +164,10 @@
163164
<Project Path="playground/AspireEventHub/EventHubsApi/EventHubsApi.csproj" />
164165
<Project Path="playground/AspireEventHub/EventHubsConsumer/EventHubsConsumer.csproj" />
165166
</Folder>
167+
<Folder Name="/playground/GitHubModelsEndToEnd/">
168+
<Project Path="playground/GitHubModelsEndToEnd/GitHubModelsEndToEnd.AppHost/GitHubModelsEndToEnd.AppHost.csproj" />
169+
<Project Path="playground/GitHubModelsEndToEnd/GitHubModelsEndToEnd.WebStory/GitHubModelsEndToEnd.WebStory.csproj" />
170+
</Folder>
166171
<Folder Name="/playground/ExternalServices/">
167172
<Project Path="playground/ExternalServices/ExternalServices.AppHost/ExternalServices.AppHost.csproj" />
168173
<Project Path="playground/ExternalServices/WebFrontEnd/WebFrontEnd.csproj" />
@@ -375,6 +380,7 @@
375380
<Project Path="tests/Aspire.Hosting.Containers.Tests/Aspire.Hosting.Containers.Tests.csproj" />
376381
<Project Path="tests/Aspire.Hosting.Docker.Tests/Aspire.Hosting.Docker.Tests.csproj" />
377382
<Project Path="tests/Aspire.Hosting.Garnet.Tests/Aspire.Hosting.Garnet.Tests.csproj" />
383+
<Project Path="tests/Aspire.Hosting.GitHub.Models.Tests/Aspire.Hosting.GitHub.Models.Tests.csproj" />
378384
<Project Path="tests/Aspire.Hosting.Kafka.Tests/Aspire.Hosting.Kafka.Tests.csproj" />
379385
<Project Path="tests/Aspire.Hosting.Keycloak.Tests/Aspire.Hosting.Keycloak.Tests.csproj" />
380386
<Project Path="tests/Aspire.Hosting.Kubernetes.Tests/Aspire.Hosting.Kubernetes.Tests.csproj" />
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<IsAspireHost>true</IsAspireHost>
9+
<UserSecretsId>b8e8b8c7-4a45-4b2e-8c7d-9e8f7a6b5c4d</UserSecretsId>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<Compile Include="..\..\KnownResourceNames.cs" Link="KnownResourceNames.cs" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<AspireProjectOrPackageReference Include="Aspire.Hosting.Azure.AppContainers" />
18+
<AspireProjectOrPackageReference Include="Aspire.Hosting.AppHost" />
19+
<AspireProjectOrPackageReference Include="Aspire.Hosting.GitHub.Models" />
20+
<ProjectReference Include="..\GitHubModelsEndToEnd.WebStory\GitHubModelsEndToEnd.WebStory.csproj" />
21+
</ItemGroup>
22+
23+
</Project>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
var builder = DistributedApplication.CreateBuilder(args);
5+
builder.AddAzureContainerAppEnvironment("env");
6+
7+
var chat = builder.AddGitHubModel("chat", "openai/gpt-4o-mini");
8+
9+
// To set the GitHub Models API key define the value for the following parameter in User Secrets.
10+
// Alternatively, you can set the environment variable GITHUB_TOKEN and comment the line below.
11+
chat.WithApiKey(builder.AddParameter("github-api-key", secret: true));
12+
13+
builder.AddProject<Projects.GitHubModelsEndToEnd_WebStory>("webstory")
14+
.WithExternalHttpEndpoints()
15+
.WithReference(chat).WaitFor(chat);
16+
17+
#if !SKIP_DASHBOARD_REFERENCE
18+
// This project is only added in playground projects to support development/debugging
19+
// of the dashboard. It is not required in end developer code. Comment out this code
20+
// or build with `/p:SkipDashboardReference=true`, to test end developer
21+
// dashboard launch experience, Refer to Directory.Build.props for the path to
22+
// the dashboard binary (defaults to the Aspire.Dashboard bin output in the
23+
// artifacts dir).
24+
builder.AddProject<Projects.Aspire_Dashboard>(KnownResourceNames.AspireDashboard);
25+
#endif
26+
27+
builder.Build().Run();
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"https": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": true,
8+
"applicationUrl": "https://localhost:15215;http://localhost:15216",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development",
11+
"DOTNET_ENVIRONMENT": "Development",
12+
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16195",
13+
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037"
14+
}
15+
},
16+
"http": {
17+
"commandName": "Project",
18+
"dotnetRunMessages": true,
19+
"launchBrowser": true,
20+
"applicationUrl": "http://localhost:15216",
21+
"environmentVariables": {
22+
"ASPNETCORE_ENVIRONMENT": "Development",
23+
"DOTNET_ENVIRONMENT": "Development",
24+
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16195",
25+
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038",
26+
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
27+
}
28+
},
29+
"generate-manifest": {
30+
"commandName": "Project",
31+
"launchBrowser": true,
32+
"dotnetRunMessages": true,
33+
"commandLineArgs": "--publisher manifest --output-path aspire-manifest.json",
34+
"environmentVariables": {
35+
"ASPNETCORE_ENVIRONMENT": "Development",
36+
"DOTNET_ENVIRONMENT": "Development"
37+
}
38+
}
39+
}
40+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"$schema": "https://json.schemastore.org/aspire-8.0.json",
3+
"resources": {
4+
"env": {
5+
"type": "azure.bicep.v0",
6+
"path": "env.module.bicep",
7+
"params": {
8+
"userPrincipalId": ""
9+
}
10+
},
11+
"chat": {
12+
"type": "value.v0",
13+
"connectionString": "Endpoint=https://models.github.ai/inference;Key={github-api-key.value};Model=openai/gpt-4o-mini;DeploymentId=openai/gpt-4o-mini"
14+
},
15+
"github-api-key": {
16+
"type": "parameter.v0",
17+
"value": "{github-api-key.inputs.value}",
18+
"inputs": {
19+
"value": {
20+
"type": "string",
21+
"secret": true
22+
}
23+
}
24+
},
25+
"webstory": {
26+
"type": "project.v1",
27+
"path": "../GitHubModelsEndToEnd.WebStory/GitHubModelsEndToEnd.WebStory.csproj",
28+
"deployment": {
29+
"type": "azure.bicep.v0",
30+
"path": "webstory.module.bicep",
31+
"params": {
32+
"env_outputs_azure_container_apps_environment_default_domain": "{env.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN}",
33+
"env_outputs_azure_container_apps_environment_id": "{env.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_ID}",
34+
"env_outputs_azure_container_registry_endpoint": "{env.outputs.AZURE_CONTAINER_REGISTRY_ENDPOINT}",
35+
"env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}",
36+
"webstory_containerimage": "{webstory.containerImage}",
37+
"webstory_containerport": "{webstory.containerPort}",
38+
"github_api_key_value": "{github-api-key.value}"
39+
}
40+
},
41+
"env": {
42+
"OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true",
43+
"OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true",
44+
"OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY": "in_memory",
45+
"ASPNETCORE_FORWARDEDHEADERS_ENABLED": "true",
46+
"HTTP_PORTS": "{webstory.bindings.http.targetPort}",
47+
"ConnectionStrings__chat": "{chat.connectionString}"
48+
},
49+
"bindings": {
50+
"http": {
51+
"scheme": "http",
52+
"protocol": "tcp",
53+
"transport": "http",
54+
"external": true
55+
},
56+
"https": {
57+
"scheme": "https",
58+
"protocol": "tcp",
59+
"transport": "http",
60+
"external": true
61+
}
62+
}
63+
}
64+
}
65+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
@description('The location for the resource(s) to be deployed.')
2+
param location string = resourceGroup().location
3+
4+
param userPrincipalId string
5+
6+
param tags object = { }
7+
8+
resource env_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = {
9+
name: take('env_mi-${uniqueString(resourceGroup().id)}', 128)
10+
location: location
11+
tags: tags
12+
}
13+
14+
resource env_acr 'Microsoft.ContainerRegistry/registries@2025-04-01' = {
15+
name: take('envacr${uniqueString(resourceGroup().id)}', 50)
16+
location: location
17+
sku: {
18+
name: 'Basic'
19+
}
20+
tags: tags
21+
}
22+
23+
resource env_acr_env_mi_AcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
24+
name: guid(env_acr.id, env_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d'))
25+
properties: {
26+
principalId: env_mi.properties.principalId
27+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
28+
principalType: 'ServicePrincipal'
29+
}
30+
scope: env_acr
31+
}
32+
33+
resource env_law 'Microsoft.OperationalInsights/workspaces@2025-02-01' = {
34+
name: take('envlaw-${uniqueString(resourceGroup().id)}', 63)
35+
location: location
36+
properties: {
37+
sku: {
38+
name: 'PerGB2018'
39+
}
40+
}
41+
tags: tags
42+
}
43+
44+
resource env 'Microsoft.App/managedEnvironments@2025-01-01' = {
45+
name: take('env${uniqueString(resourceGroup().id)}', 24)
46+
location: location
47+
properties: {
48+
appLogsConfiguration: {
49+
destination: 'log-analytics'
50+
logAnalyticsConfiguration: {
51+
customerId: env_law.properties.customerId
52+
sharedKey: env_law.listKeys().primarySharedKey
53+
}
54+
}
55+
workloadProfiles: [
56+
{
57+
name: 'consumption'
58+
workloadProfileType: 'Consumption'
59+
}
60+
]
61+
}
62+
tags: tags
63+
}
64+
65+
resource aspireDashboard 'Microsoft.App/managedEnvironments/dotNetComponents@2024-10-02-preview' = {
66+
name: 'aspire-dashboard'
67+
properties: {
68+
componentType: 'AspireDashboard'
69+
}
70+
parent: env
71+
}
72+
73+
output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = env_law.name
74+
75+
output AZURE_LOG_ANALYTICS_WORKSPACE_ID string = env_law.id
76+
77+
output AZURE_CONTAINER_REGISTRY_NAME string = env_acr.name
78+
79+
output AZURE_CONTAINER_REGISTRY_ENDPOINT string = env_acr.properties.loginServer
80+
81+
output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id
82+
83+
output AZURE_CONTAINER_APPS_ENVIRONMENT_NAME string = env.name
84+
85+
output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = env.id
86+
87+
output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = env.properties.defaultDomain
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
@description('The location for the resource(s) to be deployed.')
2+
param location string = resourceGroup().location
3+
4+
param env_outputs_azure_container_apps_environment_default_domain string
5+
6+
param env_outputs_azure_container_apps_environment_id string
7+
8+
param env_outputs_azure_container_registry_endpoint string
9+
10+
param env_outputs_azure_container_registry_managed_identity_id string
11+
12+
param webstory_containerimage string
13+
14+
param webstory_containerport string
15+
16+
@secure()
17+
param github_api_key_value string
18+
19+
resource webstory 'Microsoft.App/containerApps@2025-02-02-preview' = {
20+
name: 'webstory'
21+
location: location
22+
properties: {
23+
configuration: {
24+
secrets: [
25+
{
26+
name: 'connectionstrings--chat'
27+
value: 'Endpoint=https://models.github.ai/inference;Key=${github_api_key_value};Model=openai/gpt-4o-mini;DeploymentId=openai/gpt-4o-mini'
28+
}
29+
]
30+
activeRevisionsMode: 'Single'
31+
ingress: {
32+
external: true
33+
targetPort: int(webstory_containerport)
34+
transport: 'http'
35+
}
36+
registries: [
37+
{
38+
server: env_outputs_azure_container_registry_endpoint
39+
identity: env_outputs_azure_container_registry_managed_identity_id
40+
}
41+
]
42+
runtime: {
43+
dotnet: {
44+
autoConfigureDataProtection: true
45+
}
46+
}
47+
}
48+
environmentId: env_outputs_azure_container_apps_environment_id
49+
template: {
50+
containers: [
51+
{
52+
image: webstory_containerimage
53+
name: 'webstory'
54+
env: [
55+
{
56+
name: 'OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES'
57+
value: 'true'
58+
}
59+
{
60+
name: 'OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES'
61+
value: 'true'
62+
}
63+
{
64+
name: 'OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY'
65+
value: 'in_memory'
66+
}
67+
{
68+
name: 'ASPNETCORE_FORWARDEDHEADERS_ENABLED'
69+
value: 'true'
70+
}
71+
{
72+
name: 'HTTP_PORTS'
73+
value: webstory_containerport
74+
}
75+
{
76+
name: 'ConnectionStrings__chat'
77+
secretRef: 'connectionstrings--chat'
78+
}
79+
]
80+
}
81+
]
82+
scale: {
83+
minReplicas: 1
84+
}
85+
}
86+
}
87+
identity: {
88+
type: 'UserAssigned'
89+
userAssignedIdentities: {
90+
'${env_outputs_azure_container_registry_managed_identity_id}': { }
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)