Skip to content

Commit 5dd4d87

Browse files
qfaiclaude
andauthored
feat: add foundry-proxy-agent VS C# template (#15320)
* feat: add foundry-proxy-agent VS C# template Add foundry-proxy-agent template Include Bicep infra for local and Azure deploy Add README with architecture and quick start Register template in fx-core Add Azure AI User permission requirement * test: add e2e test for foundry-proxy-agent VS C# template * feat: add foundry CLI question infrastructure for foundry-proxy-agent template - Add foundryEndpointQuestion() and foundryAgentIdQuestion() in create.ts - Add foundryNode() with conditional agentId child in commonNodes.ts - Register foundryNode in constructNode.ts - Wire foundryNode into VS scaffold tree in createRootNode.ts - Add foundry-endpoint and foundry-agent-id CLI options in CreateProjectOptions.ts - Add localized strings for foundry questions in package.nls.json - Register FoundryProxyAgent in templates metadata (templateNames.ts, vs.ts) - Regenerate allTemplates.json and defaultGeneratorTemplates.json - Update e2e test to pass foundry parameters and fix fs.pathExists usage * fix: add resource group for foundry-proxy-agent local provision in e2e test - Create local resource group before local provision - Pass AZURE_RESOURCE_GROUP_NAME to local provision (needed for Bot Service + OAuth) - Add cleanup for local resource group in after hook * fix: move SSO app from Bicep to YAML aadApp actions - Use aadApp/create + aadApp/update for SSO app - Add aad.manifest.json.tpl for SSO app config - Bicep only references existing app, creates FCI + SP - Avoids Bicep PUT needing serviceManagementReference - Remove unused bicep modules * fix: add SSO_APP_ACCESS_AS_USER_PERMISSION_ID generation for aadApp/update - Add script step to generate deterministic GUID based on app name - Add SSO_APP_ACCESS_AS_USER_PERMISSION_ID to .env.local.tpl - Apply to both m365agents.local.yml.tpl and m365agents.yml.tpl - Fixes MissingEnvironmentVariablesError in CI provision * fix: use AAD_APP_ACCESS_AS_USER_PERMISSION_ID for auto-generation - Change from SSO_APP_ACCESS_AS_USER_PERMISSION_ID to AAD_APP_ACCESS_AS_USER_PERMISSION_ID - aadApp/update driver automatically generates UUID for AAD_APP_* placeholders - Remove invalid guid() helper attempts (not registered in Handlebars) - Leave env var empty to trigger auto-generation on first provision * fix: use appId instead of uniqueName to reference existing SSO app - Change from uniqueName to appId for existing resource lookup - Use existingSsoApp.id for federatedCredential name - Fixes 'Cannot find requested resource' error in Bicep deployment * fix: remove existing resource reference and use parameters directly - Remove existingSsoApp resource declaration (cannot use appId in existing) - Use ssoAppObjectId parameter directly for federatedCredential name - Use ssoAppObjectId for aadAppObjectId output - Fixes BCP173 error: appId cannot be used in existing resource declaration * fix: use uniqueName instead of id to reference existing AAD app in Bicep The Microsoft.Graph/applications existing resource in Bicep requires uniqueName property (display name) and does not support id property (object ID) according to Bicep validation error BCP173. This change updates app-registration.bicep to: - Add existingSsoApp resource using uniqueName: ssoAppName - Use existingSsoApp.id for federated credential name and output - Remove direct usage of ssoAppObjectId parameter This fixes the "Cannot find requested resource" error in CI pipeline for foundry-proxy-agent template provision. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: set uniqueName in aadApp/create for Bicep Microsoft Graph extension compatibility This change resolves the "Cannot find requested resource" error when Bicep tries to reference existing AAD applications created by aadApp/create action. Root Cause: - Bicep Microsoft Graph extension requires 'uniqueName' property to reference existing applications - aadApp/create was not setting uniqueName when creating apps via Microsoft Graph API - This caused Bicep deployment to fail when trying to create child resources (federated credentials) Solution: 1. Added setUniqueName() method in AadAppClient to PATCH uniqueName property after app creation 2. Modified aadApp/create driver to call setUniqueName() immediately after creating the app 3. Updated app-registration.bicep to use 'existing' resource with uniqueName reference 4. Removed unnecessary ssoAppObjectId and ssoAppId parameters from Bicep modules Benefits: - Enables proper Bicep referencing of AAD apps created via YAML actions - Allows creating federated credentials and service principals for existing apps - Maintains separation of concerns (app creation via YAML, credentials/SP via Bicep) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: revert to use bicep to create sso aap * test: add coverage for foundryNode, constructNode, and templateReplaceMap * fix: add sso app id placeholder * fix: the dev tunnel endpoint might change, so need to update bot service everytime debug * ci: add same-tenant e2e pipeline for foundry proxy agent and exclude from main e2e * ci: temp copy same-tenant workflow to sdk-e2e-test for manual trigger test * ci: revert sdk-e2e-test.yml to dev branch version * test: use ARM bot service validation instead of MOS API for foundry proxy agent e2e test * fix: make enableAppInsights default to be false * fix: change region to australiacentral * fix: region * fix: add well known env naimg convention * fix: update e2e-test-same-tenant.yml and fix agentproxy test * fix: update proxy agent manifest to 1.25 * fix: remove unused field in .env --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 2ebaf8c commit 5dd4d87

File tree

62 files changed

+6199
-6
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+6199
-6
lines changed
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
name: E2E Test - Same Tenant
2+
3+
# This workflow runs e2e tests that require the Azure account and M365 account to be in the same tenant.
4+
# Credentials are provided via TEST_E2E_ACCOUNT_NAME (var) and TEST_E2E_ACCOUNT_PASSWORD (secret).
5+
#
6+
# To add a new same-tenant test, extend the grep -E pattern in the setup job's case-listing steps:
7+
# grep -E "FoundryProxyAgent|YourNewFeature"
8+
9+
on:
10+
workflow_dispatch:
11+
inputs:
12+
cases:
13+
description: 'Specific cases to execute. Example: ["./vs/FoundryProxyAgent.dotnet.tests.ts"]. Leave empty to run all same-tenant defaults.'
14+
required: false
15+
type: string
16+
17+
cli-version:
18+
description: "cli version. Leave empty to use local or alpha build."
19+
required: false
20+
type: string
21+
default: ""
22+
23+
target-testplan-id:
24+
description: "target testplan id. If not set, will not archive ado testplan"
25+
required: false
26+
type: string
27+
28+
schedule:
29+
- cron: "0 23 * * *"
30+
31+
pull_request:
32+
branches: [dev, release/**]
33+
34+
permissions:
35+
actions: read
36+
37+
jobs:
38+
setup:
39+
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'OfficeDev/microsoft-365-agents-toolkit') }}
40+
runs-on: ubuntu-latest
41+
environment: engineering
42+
permissions:
43+
id-token: write
44+
contents: read
45+
outputs:
46+
cases: ${{ steps.schedule-cases.outputs.cases || steps.pr-cases.outputs.cases || steps.dispatch-cases.outputs.cases }}
47+
env:
48+
AZURE_ACCOUNT_NAME: ${{ vars.TEST_E2E_ACCOUNT_NAME }}
49+
AZURE_ACCOUNT_PASSWORD: ${{ secrets.TEST_E2E_ACCOUNT_PASSWORD }}
50+
AZURE_SUBSCRIPTION_ID: ${{ vars.TEST_E2E_SUBSCRIPTION_ID }}
51+
AZURE_TENANT_ID: ${{ vars.TEST_TENANT_CLEAN_TENANT_ID }}
52+
M365_ACCOUNT_NAME: ${{ vars.TEST_E2E_ACCOUNT_NAME }}
53+
M365_ACCOUNT_PASSWORD: ${{ secrets.TEST_E2E_ACCOUNT_PASSWORD }}
54+
M365_TENANT_ID: ${{ vars.TEST_TENANT_CLEAN_TENANT_ID }}
55+
AUTO_TEST_PLAN_ID: ${{ github.event.inputs.target-testplan-id }}
56+
CI_ENABLED: "true"
57+
58+
steps:
59+
- name: Checkout
60+
uses: actions/checkout@v3
61+
62+
- uses: azure/login@v1
63+
with:
64+
client-id: ${{ secrets.DEVOPS_CLIENT_ID }}
65+
tenant-id: ${{ secrets.DEVOPS_TENANT_ID }}
66+
subscription-id: ${{ secrets.DEVOPS_SUB_ID }}
67+
68+
- name: setup project
69+
uses: ./.github/actions/setup-project
70+
with:
71+
range: "e2e"
72+
73+
# To add more same-tenant cases, extend the grep -E pattern, e.g.:
74+
# grep -E "FoundryProxyAgent|YourNewFeature"
75+
- name: List cases for schedule
76+
id: schedule-cases
77+
if: ${{ github.event_name == 'schedule' }}
78+
working-directory: packages/tests/src/e2e
79+
run: |
80+
cases=`find . -wholename "*.tests.ts" | grep -E "FoundryProxyAgent" | jq -Rsc '[split("\n") | .[]| select(.!="")]'`
81+
echo "cases=$cases" >> $GITHUB_OUTPUT
82+
83+
- name: List cases for pull request
84+
id: pr-cases
85+
if: ${{ github.event_name == 'pull_request' }}
86+
working-directory: packages/tests/src/e2e
87+
run: |
88+
cases=`find . -wholename "*.tests.ts" | grep -E "FoundryProxyAgent" | jq -Rsc '[split("\n") | .[]| select(.!="")]'`
89+
echo "cases=$cases" >> $GITHUB_OUTPUT
90+
91+
- name: List cases for dispatch
92+
id: dispatch-cases
93+
if: ${{ github.event_name == 'workflow_dispatch' }}
94+
working-directory: packages/tests/src/e2e
95+
run: |
96+
inputCases='${{ github.event.inputs.cases }}'
97+
if [ -z "$inputCases" ]; then
98+
allCases=`find . -wholename "*.tests.ts" | grep -E "FoundryProxyAgent" | jq -Rsc '[split("\n") | .[]| select(.!="")]'`
99+
echo "cases=$allCases" >> $GITHUB_OUTPUT
100+
else
101+
echo "cases=$inputCases" >> $GITHUB_OUTPUT
102+
fi
103+
104+
- name: E2E Test clean
105+
working-directory: packages/tests
106+
run: |
107+
npm run test:e2e:clean
108+
109+
execute-case:
110+
if: ${{ needs.setup.outputs.cases }}
111+
env:
112+
AZURE_ACCOUNT_NAME: ${{ vars.TEST_E2E_ACCOUNT_NAME }}
113+
AZURE_ACCOUNT_PASSWORD: ${{ secrets.TEST_E2E_ACCOUNT_PASSWORD }}
114+
AZURE_SUBSCRIPTION_ID: ${{ vars.TEST_E2E_SUBSCRIPTION_ID }}
115+
AZURE_TENANT_ID: ${{ vars.TEST_TENANT_CLEAN_TENANT_ID }}
116+
M365_ACCOUNT_NAME: ${{ vars.TEST_E2E_ACCOUNT_NAME }}
117+
M365_ACCOUNT_PASSWORD: ${{ secrets.TEST_E2E_ACCOUNT_PASSWORD }}
118+
M365_TENANT_ID: ${{ vars.TEST_TENANT_CLEAN_TENANT_ID }}
119+
AUTO_TEST_PLAN_ID: ${{ github.event.inputs.target-testplan-id }}
120+
CI_ENABLED: "true"
121+
TEAMSFX_DEBUG_TEMPLATE: "true"
122+
NODE_ENV: "development"
123+
TEAMSFX_AAD_DEPLOY_ONLY: "true"
124+
SIDELOADING_SERVICE_ENDPOINT: ${{ secrets.SIDELOADING_SERVICE_ENDPOINT }}
125+
SIDELOADING_SERVICE_SCOPE: ${{ secrets.SIDELOADING_SERVICE_SCOPE }}
126+
needs: setup
127+
runs-on: ubuntu-latest
128+
environment: engineering
129+
permissions:
130+
id-token: write
131+
contents: read
132+
strategy:
133+
fail-fast: false
134+
matrix:
135+
cases: ${{ fromJson(needs.setup.outputs.cases) }}
136+
name: ${{ matrix.cases }}
137+
outputs:
138+
cases: ${{ matrix.cases }}
139+
140+
steps:
141+
- name: Checkout
142+
uses: actions/checkout@v3
143+
144+
- uses: azure/login@v1
145+
with:
146+
client-id: ${{ secrets.DEVOPS_CLIENT_ID }}
147+
tenant-id: ${{ secrets.DEVOPS_TENANT_ID }}
148+
subscription-id: ${{ secrets.DEVOPS_SUB_ID }}
149+
150+
- name: Setup node
151+
uses: actions/setup-node@v3
152+
with:
153+
node-version: 22
154+
155+
- name: Setup .NET
156+
if: contains(matrix.cases, '.dotnet.')
157+
uses: actions/setup-dotnet@v3
158+
with:
159+
dotnet-version: |
160+
8.0.x
161+
162+
- uses: pnpm/action-setup@v4
163+
164+
- name: Setup project
165+
run: |
166+
npm run setup:e2e
167+
168+
- name: install cli with specific version
169+
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.cli-version != '' }}
170+
run: |
171+
npm install -g @microsoft/m365agentstoolkit-cli@${{ github.event.inputs.cli-version }}
172+
173+
- name: install cli for schedule
174+
if: ${{ github.event_name == 'schedule' || github.event_name == 'pull_request' || (github.event.ref == 'refs/heads/dev' && github.event_name == 'workflow_dispatch' && github.event.inputs.cli-version == '') }}
175+
run: |
176+
npm install -g @microsoft/m365agentstoolkit-cli@alpha
177+
178+
- name: link cli for workflow_dispatch
179+
if: ${{ github.event_name == 'workflow_dispatch' && github.event.ref != 'refs/heads/dev' && github.event.inputs.cli-version == '' }}
180+
run: |
181+
pnpm link --global
182+
183+
- name: print system info
184+
run: |
185+
lscpu
186+
187+
- name: run test
188+
working-directory: packages/tests/src/e2e
189+
run: |
190+
file=`find . -wholename "${{ matrix.cases }}"`
191+
if [ -z "$file" ]; then
192+
echo "can't find target case in $file"
193+
exit 1
194+
else
195+
npx mocha --reporter mochawesome --timeout 1200000 $file
196+
fi
197+
198+
- name: get report name
199+
id: get-report-name
200+
if: ${{ always() }}
201+
run: |
202+
name="${{ matrix.cases }}"
203+
name="${name//'.tests.ts'/}"
204+
name="${name//.\//}"
205+
name="${name//\//_}"
206+
echo "name=$name" >> $GITHUB_OUTPUT
207+
208+
- name: Upload test report
209+
uses: actions/upload-artifact@v4
210+
if: ${{ always() }}
211+
with:
212+
name: test-result-${{ steps.get-report-name.outputs.name }}
213+
path: |
214+
./packages/tests/src/e2e/mochawesome-report/mochawesome.json
215+
216+
tear-down:
217+
needs: execute-case
218+
if: ${{ always() && toJSON(needs.execute-case.outputs.cases) != 'null' }}
219+
runs-on: ubuntu-latest
220+
env:
221+
AZURE_ACCOUNT_NAME: ${{ vars.TEST_E2E_ACCOUNT_NAME }}
222+
AZURE_ACCOUNT_PASSWORD: ${{ secrets.TEST_E2E_ACCOUNT_PASSWORD }}
223+
AZURE_SUBSCRIPTION_ID: ${{ vars.TEST_E2E_SUBSCRIPTION_ID }}
224+
AZURE_TENANT_ID: ${{ vars.TEST_TENANT_CLEAN_TENANT_ID }}
225+
M365_ACCOUNT_NAME: ${{ vars.TEST_E2E_ACCOUNT_NAME }}
226+
M365_ACCOUNT_PASSWORD: ${{ secrets.TEST_E2E_ACCOUNT_PASSWORD }}
227+
M365_TENANT_ID: ${{ vars.TEST_TENANT_CLEAN_TENANT_ID }}
228+
CI_ENABLED: "true"
229+
steps:
230+
- name: Checkout
231+
uses: actions/checkout@v3
232+
233+
- name: setup project
234+
uses: ./.github/actions/setup-project
235+
236+
- name: E2E Test clean
237+
working-directory: packages/tests
238+
run: |
239+
npm run test:e2e:clean

.github/workflows/e2e-test.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,15 @@ jobs:
8686
if: ${{ github.event_name == 'schedule' }}
8787
working-directory: packages/tests/src/e2e
8888
run: |
89-
cases=`find . -wholename "*.tests.ts" | jq -Rsc '[split("\n") | .[]| select(.!="")]'`
89+
cases=`find . -wholename "*.tests.ts" | grep -v "FoundryProxyAgent" | jq -Rsc '[split("\n") | .[]| select(.!="")]'`
9090
echo "cases=$cases" >> $GITHUB_OUTPUT
9191
9292
- name: List cases for pull request
9393
id: pr-cases
9494
if: ${{ github.event_name == 'pull_request' }}
9595
working-directory: packages/tests/src/e2e
9696
run: |
97-
cases=`find . -wholename "*.tests.ts" | grep -v "/samples/" | jq -Rsc '[split("\n") | .[]| select(.!="")]'`
97+
cases=`find . -wholename "*.tests.ts" | grep -v "/samples/" | grep -v "FoundryProxyAgent" | jq -Rsc '[split("\n") | .[]| select(.!="")]'`
9898
echo "cases=$cases" >> $GITHUB_OUTPUT
9999
100100
- name: List cases for dispatch
@@ -104,7 +104,7 @@ jobs:
104104
run: |
105105
inputCases='${{ github.event.inputs.cases }}'
106106
if [ -z "$inputCases" ]; then
107-
allCases=`find . -wholename "*.tests.ts" | jq -Rsc '[split("\n") | .[]| select(.!="")]'`
107+
allCases=`find . -wholename "*.tests.ts" | grep -v "FoundryProxyAgent" | jq -Rsc '[split("\n") | .[]| select(.!="")]'`
108108
echo "cases=$allCases" >> $GITHUB_OUTPUT
109109
else
110110
echo "cases=$inputCases" >> $GITHUB_OUTPUT

packages/fx-core/resource/package.nls.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@
374374
"core.createProjectQuestion.capability.customCopilotWeatherOption.detail": "A weather forecast agent, which is built with Microsoft 365 Agents SDK and Semantic Kernel",
375375
"core.createProjectQuestion.capability.customCopilotTravelOption.label": "Travel Agent",
376376
"core.createProjectQuestion.capability.customCopilotTravelOption.detail": "A travel planning agent, which is built with Microsoft 365 Agents SDK, Microsoft 365 Retrieval API and Agent Framework",
377+
"core.createProjectQuestion.capability.foundryProxyAgentOption.label": "Microsoft Foundry Proxy Agent",
378+
"core.createProjectQuestion.capability.foundryProxyAgentOption.detail": "An agent that proxies requests to Microsoft Azure AI Foundry agents, enabling Teams and M365 Copilot integration",
377379
"core.createProjectQuestion.capability.customEngineAgent.description": "Works in Teams and Microsoft 365 Copilot",
378380
"core.createProjectQuestion.capability.knowledgeWebSearch.label": "Web Search",
379381
"core.createProjectQuestion.capability.knowledgeWebSearch.detail": "Search the web for grounding information.",
@@ -409,6 +411,10 @@
409411
"core.createProjectQuestion.llmService.azureOpenAIAssistantID.placeholder": "Enter Azure OpenAI Assistant ID now or set it later in the project",
410412
"core.createProjectQuestion.llmService.azureOpenAIEmbeddingDeploymentName.title": "Azure OpenAI Embedding Deployment Name",
411413
"core.createProjectQuestion.llmService.azureOpenAIEmbeddingDeploymentName.placeholder": "Enter Azure OpenAI embedding deployment name now or set it later in the project",
414+
"core.createProjectQuestion.foundry.endpoint.title": "Azure AI Foundry Project Endpoint",
415+
"core.createProjectQuestion.foundry.endpoint.placeholder": "Input Azure AI Foundry project endpoint now or set it later in the project",
416+
"core.createProjectQuestion.foundry.agentId.title": "Azure AI Foundry Agent ID",
417+
"core.createProjectQuestion.foundry.agentId.placeholder": "Input Azure AI Foundry agent ID now or set it later in the project",
412418
"core.createProjectQuestion.apiPlugin.importPlugin.label": "Import from an Existing Action",
413419
"core.createProjectQuestion.apiPlugin.importPlugin.detail": "Import from an existing action file",
414420
"core.createProjectQuestion.apiSpec.title": "OpenAPI Description Document",

packages/fx-core/src/component/generator/templates/templateNames.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export enum TemplateNames {
2222
BasicCustomEngineAgent = "basic-custom-engine-agent",
2323
WeatherAgent = "weather-agent",
2424
TravelAgent = "travel-agent",
25+
FoundryProxyAgent = "foundry-proxy-agent",
2526

2627
// agent for Teams
2728
CustomCopilotBasic = "custom-copilot-basic",

packages/fx-core/src/component/generator/templates/templateReplaceMap.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export function getTemplateReplaceMap(inputs: Inputs): { [key: string]: string }
2727
inputs[QuestionNames.AzureOpenAIEmbeddingDeploymentName];
2828
const gcName: string | undefined = inputs[QuestionNames.GCName];
2929
const gcConnectionId: string | undefined = inputs[QuestionNames.GCConnectionId];
30+
const foundryEndpoint: string | undefined = inputs[QuestionNames.FoundryEndpoint];
31+
const foundryAgentId: string | undefined = inputs[QuestionNames.FoundryAgentId];
3032

3133
if (inputs.projectId !== undefined && (openAIKey || azureOpenAIKey)) {
3234
const cryptoProvider = new LocalCrypto(inputs.projectId);
@@ -66,6 +68,8 @@ export function getTemplateReplaceMap(inputs: Inputs): { [key: string]: string }
6668
azureAISearchEndpoint: azureAISearchEndpoint ?? "",
6769
gcName: gcName ?? "",
6870
gcConnectionId: gcConnectionId ?? "",
71+
FoundryEndpoint: foundryEndpoint ?? "",
72+
FoundryAgentId: foundryAgentId ?? "",
6973
openAIEmbeddingModel: openAIEmbeddingModel ?? "",
7074
NewProjectTypeName: process.env.TEAMSFX_NEW_PROJECT_TYPE_NAME ?? "M365Agent",
7175
NewProjectTypeExt: process.env.TEAMSFX_NEW_PROJECT_TYPE_EXTENSION ?? "atkproj",

packages/fx-core/src/question/create.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,24 @@ export function azureOpenAIEmbeddingDeploymentNameQuestion(): TextInputQuestion
10411041
};
10421042
}
10431043

1044+
export function foundryEndpointQuestion(): TextInputQuestion {
1045+
return {
1046+
type: "text",
1047+
name: QuestionNames.FoundryEndpoint,
1048+
title: getLocalizedString("core.createProjectQuestion.foundry.endpoint.title"),
1049+
placeholder: getLocalizedString("core.createProjectQuestion.foundry.endpoint.placeholder"),
1050+
};
1051+
}
1052+
1053+
export function foundryAgentIdQuestion(): TextInputQuestion {
1054+
return {
1055+
type: "text",
1056+
name: QuestionNames.FoundryAgentId,
1057+
title: getLocalizedString("core.createProjectQuestion.foundry.agentId.title"),
1058+
placeholder: getLocalizedString("core.createProjectQuestion.foundry.agentId.placeholder"),
1059+
};
1060+
}
1061+
10441062
export function apiPluginStartQuestion(doesProjectExists?: boolean): SingleSelectQuestion {
10451063
return {
10461064
type: "singleSelect",

packages/fx-core/src/question/options/CreateProjectOptions.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,16 @@ export const CreateProjectOptions: CLICommandOption[] = [
149149
type: "string",
150150
description: "OpenAI Key",
151151
},
152+
{
153+
name: "foundry-endpoint",
154+
type: "string",
155+
description: "Azure AI Foundry Project Endpoint",
156+
},
157+
{
158+
name: "foundry-agent-id",
159+
type: "string",
160+
description: "Azure AI Foundry Agent ID",
161+
},
152162
{
153163
name: "folder",
154164
type: "string",

packages/fx-core/src/question/questionNames.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ export enum QuestionNames {
6363
AzureAISearchApiKey = "azure-ai-search-api-key",
6464
AzureAISearchEndpoint = "azure-ai-search-endpoint",
6565

66+
FoundryEndpoint = "foundry-endpoint",
67+
FoundryAgentId = "foundry-agent-id",
68+
6669
Features = "features",
6770
Env = "env",
6871
SourceEnvName = "sourceEnvName",

packages/fx-core/src/question/scaffold/commonNodes.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
apiOperationQuestion,
1515
apiSpecLocationQuestion,
1616
apiSpecTypeSelectQuestion,
17+
foundryAgentIdQuestion,
18+
foundryEndpointQuestion,
1719
searchOpenAPISpecQueryQuestion,
1820
selectOpenApiSpecQuestion,
1921
} from "../create";
@@ -178,3 +180,21 @@ export function inputOrSearchAPISpecNode(): IQTreeNode {
178180
],
179181
};
180182
}
183+
184+
export function foundryNode(
185+
condition?: StringValidation | StringArrayValidation | ConditionFunc
186+
): IQTreeNode {
187+
return {
188+
condition: condition,
189+
data: foundryEndpointQuestion(),
190+
children: [
191+
{
192+
condition: (inputs: Inputs) => {
193+
return inputs[QuestionNames.FoundryEndpoint]?.length > 0;
194+
},
195+
data: foundryAgentIdQuestion(),
196+
},
197+
// Future: add foundryAgentNameQuestion() here as an alternative to agentId
198+
],
199+
};
200+
}

0 commit comments

Comments
 (0)