Skip to content

Commit bebff21

Browse files
resolve merge conflicts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2 parents 621729a + 5a3fb46 commit bebff21

39 files changed

Lines changed: 4687 additions & 3968 deletions

File tree

.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 }}

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/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

docs/images/AddDetails.png

-292 KB
Loading

docs/images/AddPlatform.png

-110 KB
Loading

docs/images/Web.png

-135 KB
Loading

extensions/teams/infra/azure.bicep

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ param webAppName string = resourceBaseName
3030
param location string = resourceGroup().location
3131

3232
// Compute resources for your Web App
33-
resource serverfarm 'Microsoft.Web/serverfarms@2024-04-01' = {
33+
resource serverfarm 'Microsoft.Web/serverfarms@2021-02-01' = {
3434
kind: 'app'
3535
location: location
3636
name: serverfarmsName
@@ -40,10 +40,13 @@ resource serverfarm 'Microsoft.Web/serverfarms@2024-04-01' = {
4040
}
4141

4242
// Web App that hosts your bot
43-
resource webApp 'Microsoft.Web/sites@2024-04-01' = {
43+
resource webApp 'Microsoft.Web/sites@2021-02-01' = {
4444
kind: 'app'
4545
location: location
4646
name: webAppName
47+
identity: {
48+
type: 'SystemAssigned'
49+
}
4750
properties: {
4851
serverFarmId: serverfarm.id
4952
httpsOnly: true

0 commit comments

Comments
 (0)