Skip to content

Commit bf8813f

Browse files
Merge branch 'dev' into psl-us-41323
2 parents 45d8995 + 84fb40a commit bf8813f

47 files changed

Lines changed: 5347 additions & 5457 deletions

File tree

Some content is hidden

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

.devcontainer/devcontainer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
},
66
"features": {
77
"ghcr.io/devcontainers/features/azure-cli:1": {
8-
"extensions": "ml"
8+
"extensions": "ml",
9+
"installBicep": true,
10+
"version": "latest",
11+
"bicepVersion": "latest"
912
},
1013
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
1114
"ghcr.io/devcontainers/features/node:1": {},

.github/workflows/import-sample-data-postgresql.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,6 @@ jobs:
143143
with:
144144
python-version: '3.11'
145145

146-
- name: Upgrade Azure CLI and install extensions
147-
run: |
148-
az upgrade --yes --all
149-
az extension add --name rdbms-connect --upgrade --yes || true
150-
151146
- name: Discover PostgreSQL Server from Resource Group
152147
env:
153148
INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}

.github/workflows/validate-bicep-params.yml

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,16 @@ jobs:
3434
- name: Validate infra/ parameters
3535
id: validate_infra
3636
continue-on-error: true
37+
env:
38+
ACCELERATOR_NAME: ${{ env.accelerator_name }}
3739
run: |
3840
set +e
39-
python scripts/validate_bicep_params.py --dir infra --strict --no-color --json-output infra_results.json 2>&1 | tee infra_output.txt
41+
RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
42+
python scripts/validate_bicep_params.py --dir infra --strict --no-color \
43+
--json-output infra_results.json \
44+
--html-output email_body.html \
45+
--accelerator-name "${ACCELERATOR_NAME}" \
46+
--run-url "${RUN_URL}" 2>&1 | tee infra_output.txt
4047
EXIT_CODE=${PIPESTATUS[0]}
4148
set -e
4249
echo "## Infra Param Validation" >> "$GITHUB_STEP_SUMMARY"
@@ -61,24 +68,21 @@ jobs:
6168
name: bicep-validation-results
6269
path: |
6370
infra_results.json
71+
email_body.html
6472
retention-days: 30
6573

6674
- name: Send schedule notification on failure
6775
if: github.event_name == 'schedule' && steps.result.outputs.status == 'failure'
6876
env:
6977
LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
70-
GITHUB_REPOSITORY: ${{ github.repository }}
71-
GITHUB_RUN_ID: ${{ github.run_id }}
7278
ACCELERATOR_NAME: ${{ env.accelerator_name }}
7379
run: |
74-
RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
75-
INFRA_OUTPUT=$(sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g' infra_output.txt)
80+
EMAIL_BODY=$(cat email_body.html)
7681
7782
jq -n \
7883
--arg name "${ACCELERATOR_NAME}" \
79-
--arg infra "$INFRA_OUTPUT" \
80-
--arg url "$RUN_URL" \
81-
'{subject: ("Bicep Parameter Validation Report - " + $name + " - Issues Detected"), body: ("<p>Dear Team,</p><p>The scheduled <strong>Bicep Parameter Validation</strong> for <strong>" + $name + "</strong> has detected parameter mapping errors.</p><p><strong>infra/ Results:</strong></p><pre>" + $infra + "</pre><p><strong>Run URL:</strong> <a href=\"" + $url + "\">" + $url + "</a></p><p>Please fix the parameter mapping issues at your earliest convenience.</p><p>Best regards,<br>Your Automation Team</p>")}' \
84+
--arg body "$EMAIL_BODY" \
85+
'{subject: ("Bicep Parameter Validation Report - " + $name + " - Issues Detected"), body: $body}' \
8286
| curl -X POST "${LOGICAPP_URL}" \
8387
-H "Content-Type: application/json" \
8488
-d @- || echo "Failed to send notification"
@@ -87,18 +91,14 @@ jobs:
8791
if: github.event_name == 'schedule' && steps.result.outputs.status == 'success'
8892
env:
8993
LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
90-
GITHUB_REPOSITORY: ${{ github.repository }}
91-
GITHUB_RUN_ID: ${{ github.run_id }}
9294
ACCELERATOR_NAME: ${{ env.accelerator_name }}
9395
run: |
94-
RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
95-
INFRA_OUTPUT=$(sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g' infra_output.txt)
96+
EMAIL_BODY=$(cat email_body.html)
9697
9798
jq -n \
9899
--arg name "${ACCELERATOR_NAME}" \
99-
--arg infra "$INFRA_OUTPUT" \
100-
--arg url "$RUN_URL" \
101-
'{subject: ("Bicep Parameter Validation Report - " + $name + " - Passed"), body: ("<p>Dear Team,</p><p>The scheduled <strong>Bicep Parameter Validation</strong> for <strong>" + $name + "</strong> has completed successfully. All parameter mappings are valid.</p><p><strong>infra/ Results:</strong></p><pre>" + $infra + "</pre><p><strong>Run URL:</strong> <a href=\"" + $url + "\">" + $url + "</a></p><p>Best regards,<br>Your Automation Team</p>")}' \
100+
--arg body "$EMAIL_BODY" \
101+
'{subject: ("Bicep Parameter Validation Report - " + $name + " - Passed"), body: $body}' \
102102
| curl -X POST "${LOGICAPP_URL}" \
103103
-H "Content-Type: application/json" \
104104
-d @- || echo "Failed to send notification"

code/create_app.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -742,15 +742,32 @@ def speech_config():
742742
"""Get the speech config for Azure Speech."""
743743
try:
744744
logger.info("Method speech_config started")
745-
speech_key = env_helper.AZURE_SPEECH_KEY or get_speech_key(env_helper)
746745

747-
response = requests.post(
748-
f"{env_helper.AZURE_SPEECH_REGION_ENDPOINT}sts/v1.0/issueToken",
749-
headers={
750-
"Ocp-Apim-Subscription-Key": speech_key,
751-
},
752-
timeout=5,
753-
)
746+
if env_helper.AZURE_AUTH_TYPE == "rbac":
747+
credential = get_azure_credential(
748+
env_helper.MANAGED_IDENTITY_CLIENT_ID
749+
)
750+
token = credential.get_token(
751+
"https://cognitiveservices.azure.com/.default"
752+
)
753+
response = requests.post(
754+
f"{env_helper.AZURE_SPEECH_REGION_ENDPOINT}sts/v1.0/issueToken",
755+
headers={
756+
"Authorization": f"Bearer {token.token}",
757+
},
758+
timeout=5,
759+
)
760+
else:
761+
speech_key = env_helper.AZURE_SPEECH_KEY or get_speech_key(
762+
env_helper
763+
)
764+
response = requests.post(
765+
f"{env_helper.AZURE_SPEECH_REGION_ENDPOINT}sts/v1.0/issueToken",
766+
headers={
767+
"Ocp-Apim-Subscription-Key": speech_key,
768+
},
769+
timeout=5,
770+
)
754771

755772
if response.status_code == 200:
756773
return {

code/frontend/src/components/Answer/Answer.test.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -736,10 +736,9 @@ describe("Answer.tsx", () => {
736736
});
737737
test('test the api thow error', async () => {
738738
(global.fetch as jest.Mock).mockResolvedValueOnce({ ok: false })
739-
const consoleSpy = jest.spyOn(console, 'log');
740-
let renderref
739+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined);
741740
await act(async () => {
742-
renderref = render(
741+
render(
743742
<Answer
744743
answer={{
745744
answer: componentPropsWithCitations.answer.answer,
@@ -752,8 +751,15 @@ describe("Answer.tsx", () => {
752751
/>
753752
);
754753
});
754+
const playBtn = screen.getByRole('button', {
755+
name: /speak/i
756+
});
757+
758+
await act(async () => {
759+
fireEvent.click(playBtn);
760+
});
761+
755762
expect(consoleSpy).toHaveBeenCalled();
756-
// Restore the original console.log
757763
consoleSpy.mockRestore();
758764
});
759765
// test('test speech', async () => {

code/frontend/src/components/Answer/Answer.tsx

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,16 @@ export const Answer = ({
6969
toggleIsRefAccordionOpen();
7070
};
7171

72-
const initializeSynthesizer = () => {
72+
const initializeSynthesizer = (
73+
token: string = synthesizerData.token,
74+
region: string = synthesizerData.region
75+
) => {
76+
if (!token || !region) {
77+
return null;
78+
}
7379
const speechConfig = sdk.SpeechConfig.fromAuthorizationToken(
74-
synthesizerData.token,
75-
synthesizerData.region
80+
token,
81+
region
7682
);
7783
const newAudioDestination = new SpeechSDK.SpeakerAudioDestination();
7884
const audioConfig =
@@ -87,41 +93,32 @@ export const Answer = ({
8793
clearTimeout(playbackTimeout);
8894
}
8995
setRemainingDuration(0);
96+
return newSynthesizer;
9097
};
9198

9299
useEffect(() => {
93-
if (synthesizerData.token != "") {
94-
initializeSynthesizer();
95-
96-
return () => {
97-
if (synthesizer) {
98-
synthesizer.close();
99-
}
100-
if (audioDestination) {
101-
audioDestination.close();
102-
}
103-
if (playbackTimeout) {
104-
clearTimeout(playbackTimeout);
105-
}
106-
};
107-
}
108-
}, [index, synthesizerData]);
109-
110-
useEffect(() => {
111-
const fetchSythesizerData = async () => {
112-
const response = await fetch("/api/speech");
113-
try {
114-
if (!response.ok) {
115-
throw new Error("Network response was not ok");
116-
}
117-
const data = await response.json();
118-
setSynthesizerData({ token: data.token, region: data.region });
119-
} catch (e) {
120-
console.log(e);
100+
return () => {
101+
if (synthesizer) {
102+
synthesizer.close();
103+
}
104+
if (audioDestination) {
105+
audioDestination.close();
106+
}
107+
if (playbackTimeout) {
108+
clearTimeout(playbackTimeout);
121109
}
122110
};
123-
fetchSythesizerData();
124-
}, []);
111+
}, [synthesizer, audioDestination, playbackTimeout]);
112+
113+
const fetchSythesizerData = async (): Promise<{ token: string; region: string }> => {
114+
const response = await fetch("/api/speech");
115+
if (!response.ok) {
116+
throw new Error("Network response was not ok");
117+
}
118+
const data = await response.json();
119+
setSynthesizerData({ token: data.token, region: data.region });
120+
return { token: data.token, region: data.region };
121+
};
125122

126123
useEffect(() => {
127124
if (!isActive && synthesizer && isSpeaking) {
@@ -193,10 +190,12 @@ export const Answer = ({
193190
return "";
194191
};
195192

196-
const startSpeech = () => {
197-
if (synthesizer) {
193+
const startSpeech = (
194+
activeSynthesizer: SpeechSDK.SpeechSynthesizer | null = synthesizer
195+
) => {
196+
if (activeSynthesizer) {
198197
const text = getAnswerText();
199-
synthesizer?.speakTextAsync(
198+
activeSynthesizer.speakTextAsync(
200199
text,
201200
(result) => {
202201
if (
@@ -261,8 +260,30 @@ export const Answer = ({
261260
}
262261
}
263262
} else {
264-
onSpeak(index, "speak", resetSpeech);
265-
startSpeech();
263+
const startSpeechPlayback = async () => {
264+
try {
265+
if (!synthesizer) {
266+
let token = synthesizerData.token;
267+
let region = synthesizerData.region;
268+
if (!synthesizerData.token || !synthesizerData.region) {
269+
const speechData = await fetchSythesizerData();
270+
token = speechData.token;
271+
region = speechData.region;
272+
}
273+
const newSynthesizer = initializeSynthesizer(token, region);
274+
onSpeak(index, "speak", resetSpeech);
275+
startSpeech(newSynthesizer);
276+
return;
277+
}
278+
279+
onSpeak(index, "speak", resetSpeech);
280+
startSpeech();
281+
} catch (error) {
282+
console.error("Error initializing speech synthesis:", error);
283+
}
284+
};
285+
286+
void startSpeechPlayback();
266287
}
267288
};
268289

code/tests/test_create_app.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ def env_helper_mock():
9292
AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG
9393
)
9494
env_helper.SHOULD_STREAM = True
95+
env_helper.AZURE_AUTH_TYPE = "keys"
9596
env_helper.is_auth_type_keys.return_value = True
9697
env_helper.CONVERSATION_FLOW = ConversationFlow.CUSTOM.value
9798

@@ -128,25 +129,24 @@ def test_returns_speech_token_using_keys(
128129
timeout=5,
129130
)
130131

131-
@patch("create_app.CognitiveServicesManagementClient")
132+
@patch("create_app.get_azure_credential")
132133
@patch("create_app.requests")
133134
def test_returns_speech_token_using_rbac(
134135
self,
135136
requests: MagicMock,
136-
CognitiveServicesManagementClientMock: MagicMock,
137+
get_azure_credential_mock: MagicMock,
137138
env_helper_mock: MagicMock,
138139
client: FlaskClient,
139140
):
140141
"""Test that the speech token is returned correctly when using RBAC."""
141142
# given
143+
env_helper_mock.AZURE_AUTH_TYPE = "rbac"
142144
env_helper_mock.AZURE_SPEECH_KEY = None
145+
env_helper_mock.MANAGED_IDENTITY_CLIENT_ID = "mock-client-id"
143146

144-
mock_cognitive_services_client_mock = (
145-
CognitiveServicesManagementClientMock.return_value
146-
)
147-
mock_cognitive_services_client_mock.accounts.list_keys.return_value = MagicMock(
148-
key1="mock-key1", key2="mock-key2"
149-
)
147+
mock_credential = MagicMock()
148+
mock_credential.get_token.return_value = MagicMock(token="mock-aad-token")
149+
get_azure_credential_mock.return_value = mock_credential
150150

151151
mock_response: MagicMock = requests.post.return_value
152152
mock_response.text = "speech-token"
@@ -163,10 +163,14 @@ def test_returns_speech_token_using_rbac(
163163
"languages": AZURE_SPEECH_RECOGNIZER_LANGUAGES,
164164
}
165165

166+
get_azure_credential_mock.assert_called_once_with("mock-client-id")
167+
mock_credential.get_token.assert_called_once_with(
168+
"https://cognitiveservices.azure.com/.default"
169+
)
166170
requests.post.assert_called_once_with(
167171
f"{AZURE_SPEECH_REGION_ENDPOINT}sts/v1.0/issueToken",
168172
headers={
169-
"Ocp-Apim-Subscription-Key": "mock-key1",
173+
"Authorization": "Bearer mock-aad-token",
170174
},
171175
timeout=5,
172176
)

docs/LOCAL_DEPLOYMENT.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ Select one of the following options to set up your Chat with your Data local dep
129129
- [Python 3.11](https://www.python.org/downloads/release/python-3119/)
130130
- [Node.js LTS](https://nodejs.org/en)
131131
- [Azure Developer CLI](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd) <small>(v1.18.0+)</small>
132+
- [Bicep CLI](https://learn.microsoft.com/azure/azure-resource-manager/bicep/install) <small>(v0.33.0+)</small>
132133
- [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local)
133134
- [Git](https://git-scm.com/downloads)
134135
- [PowerShell 7.0+](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell)

docs/NON_DEVCONTAINER_SETUP.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ If you are unable to run this accelerator using a DevContainer or in GitHub Code
1414
- [Python 3.11](https://www.python.org/downloads/release/python-3119/)
1515
- [Node.js LTS](https://nodejs.org/en)
1616
- [Azure Developer CLI](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd) <small>(v1.18.0+)</small>
17+
- [Bicep CLI](https://learn.microsoft.com/azure/azure-resource-manager/bicep/install) <small>(v0.33.0+)</small>
1718
- [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local)
1819

1920
## Setup

docs/create_new_app_registration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
![Redirect URL](images/AddRedirectURL.png)
2222

23-
6. Click on `+ Add a platform`.
23+
6. Click on `+ Add redirect URI`.
2424

2525
![+ Add platform](images/AddPlatform.png)
2626

0 commit comments

Comments
 (0)