-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1. url encoding for sql password 2. text-to-speech: stream instead of creating a temp file 3. check for undefined category id 4. fix bicep configuration for bing search 5. fix function for the indexer
- Loading branch information
Showing
10 changed files
with
285 additions
and
181 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
172 changes: 96 additions & 76 deletions
172
backend/func/acs_skillset_for_indexer/GetImageEmbeddings/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,99 @@ | ||
import os | ||
import json | ||
import logging | ||
import requests | ||
|
||
import azure.functions as func | ||
|
||
|
||
def main(req: func.HttpRequest) -> func.HttpResponse: | ||
logging.info('Python HTTP trigger function processed a request.') | ||
|
||
# Extract values from request payload | ||
req_body = req.get_body().decode('utf-8') | ||
logging.info(f"Request body: {req_body}") | ||
|
||
if req_body: | ||
import os | ||
import json | ||
import logging | ||
import requests | ||
import azure.functions as func | ||
|
||
# Sample of input and output data | ||
# https://learn.microsoft.com/en-us/azure/search/cognitive-search-custom-skill-web-api#sample-input-json-structure | ||
# https://learn.microsoft.com/en-us/azure/search/cognitive-search-custom-skill-web-api#sample-output-json-structure | ||
|
||
|
||
def main(req: func.HttpRequest) -> func.HttpResponse: | ||
logging.info("Python HTTP trigger function processed a request.") | ||
|
||
try: | ||
req_body = req.get_body().decode("utf-8") | ||
logging.info(f"Request body: {req_body}") | ||
|
||
request = json.loads(req_body) | ||
values = request['values'] | ||
values = request.get("values", []) | ||
|
||
# Process values and generate the response payload | ||
response_values = [] | ||
if not values: | ||
logging.info("No values provided in the request.") | ||
return func.HttpResponse( | ||
json.dumps({"values": []}), mimetype="application/json", status_code=200 | ||
) | ||
|
||
response_results = [] | ||
for value in values: | ||
imageUrl = value['data']['imgPath'] | ||
recordId = value['recordId'] | ||
logging.info(f"Input imageUrl: {imageUrl}") | ||
logging.info(f"Input recordId: {recordId}") | ||
|
||
# Get image embeddings | ||
vector = get_image_embeddings(imageUrl) | ||
|
||
# Add the processed value to the response payload | ||
response_values.append({ | ||
"recordId": recordId, | ||
"data": { | ||
"vector": vector | ||
}, | ||
"errors": None, | ||
"warnings": None | ||
}) | ||
|
||
# Create the response object | ||
response_body = { | ||
"values": response_values | ||
} | ||
logging.info(f"Response body: {response_body}") | ||
|
||
# Return the response | ||
return func.HttpResponse(json.dumps(response_body), mimetype="application/json") | ||
else: | ||
logging.info("req_body is empty") | ||
|
||
|
||
def get_image_embeddings(imageUrl): | ||
cogSvcsEndpoint = os.environ["COGNITIVE_SERVICES_ENDPOINT"] | ||
cogSvcsApiKey = os.environ["COGNITIVE_SERVICES_API_KEY"] | ||
|
||
url = f"{cogSvcsEndpoint}/computervision/retrieval:vectorizeImage" | ||
|
||
params = { | ||
"api-version": "2023-02-01-preview" | ||
} | ||
|
||
headers = { | ||
"Content-Type": "application/json", | ||
"Ocp-Apim-Subscription-Key": cogSvcsApiKey | ||
} | ||
|
||
data = { | ||
"url": imageUrl | ||
} | ||
|
||
response = requests.post(url, params=params, headers=headers, json=data) | ||
|
||
if response.status_code != 200: | ||
logging.error(f"Error: {response.status_code}, {response.text}") | ||
response.raise_for_status() | ||
|
||
embeddings = response.json()["vector"] | ||
return embeddings | ||
record_id = value.get("recordId", "Unknown") | ||
logging.info(f"Processing recordId: {record_id}") | ||
|
||
img_path = value.get("data", {}).get("imgPath") | ||
if not img_path: | ||
logging.error("imgPath is missing.") | ||
response = create_error_response(record_id, "Missing key: imgPath") | ||
response_results.append(response) | ||
continue | ||
|
||
vector = get_image_embeddings(img_path) | ||
if vector: | ||
response = create_success_response(record_id, vector) | ||
else: | ||
response = create_error_response( | ||
record_id, "Failed to retrieve image embeddings." | ||
) | ||
response_results.append(response) | ||
|
||
logging.info(f"Response body: {response_results}") | ||
return func.HttpResponse( | ||
json.dumps({"values": response_results}), | ||
mimetype="application/json", | ||
status_code=200, | ||
) | ||
except Exception as e: | ||
logging.error(f"Unexpected error: {e}") | ||
return func.HttpResponse(f"Internal Server Error: {e}", status_code=500) | ||
|
||
|
||
def get_image_embeddings(img_path): | ||
cog_svcs_endpoint = os.getenv("COGNITIVE_SERVICES_ENDPOINT") | ||
cog_svcs_api_key = os.getenv("COGNITIVE_SERVICES_API_KEY") | ||
cog_svcs_api_version = os.getenv("COGNITIVE_SERVICES_API_VERSION", "2024-02-01") | ||
|
||
url = f"{cog_svcs_endpoint}/computervision/retrieval:vectorizeImage" | ||
params = {"api-version": cog_svcs_api_version} | ||
headers = { | ||
"Content-Type": "application/json", | ||
"Ocp-Apim-Subscription-Key": cog_svcs_api_key, | ||
} | ||
data = {"url": img_path} | ||
|
||
try: | ||
response = requests.post(url, params=params, headers=headers, json=data) | ||
if response.status_code != 200: | ||
logging.error(f"Error: {response.status_code}, {response.text}") | ||
return None | ||
return response.json().get("vector", []) | ||
except Exception as e: | ||
logging.error(f"Error getting image embeddings: {e}") | ||
return None | ||
|
||
|
||
def create_success_response(record_id, vector): | ||
return { | ||
"recordId": record_id, | ||
"data": {"vector": vector}, | ||
"errors": [], | ||
"warnings": None, | ||
} | ||
|
||
|
||
def create_error_response(record_id, message): | ||
return { | ||
"recordId": record_id, | ||
"data": None, | ||
"errors": [{"message": message}], | ||
"warnings": None, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,83 @@ | ||
import os | ||
import uuid | ||
from azure.cognitiveservices.speech import CancellationReason, SpeechSynthesisCancellationDetails, ResultReason, SpeechConfig, SpeechSynthesizer, AudioDataStream, SpeechSynthesisOutputFormat | ||
from azure.cognitiveservices.speech.audio import AudioOutputConfig | ||
from azure.cognitiveservices.speech import ( | ||
CancellationReason, | ||
SpeechSynthesisCancellationDetails, | ||
ResultReason, | ||
SpeechConfig, | ||
SpeechSynthesizer, | ||
AudioDataStream, | ||
SpeechSynthesisOutputFormat, | ||
) | ||
from azure.cognitiveservices.speech.audio import AudioOutputConfig, PullAudioOutputStream | ||
|
||
async def synthesize_speech(text: str, speech_subscription_key: str, speech_region: str): | ||
|
||
async def synthesize_speech( | ||
text: str, speech_subscription_key: str, speech_region: str | ||
): | ||
try: | ||
# Validate input parameters | ||
if not text: | ||
raise ValueError("Text for speech synthesis cannot be empty.") | ||
if not speech_subscription_key or not speech_region: | ||
raise ValueError("Speech subscription key and region must be provided.") | ||
|
||
# Configure speech synthesis | ||
speech_config = SpeechConfig( | ||
subscription=speech_subscription_key, region=speech_region) | ||
subscription=speech_subscription_key, region=speech_region | ||
) | ||
speech_config.speech_synthesis_language = "en-US" | ||
speech_config.speech_synthesis_voice_name = "en-US-JennyMultilingualNeural" | ||
# https://learn.microsoft.com/en-us/answers/questions/1184428/azure-text-to-speech-error-code-0x38-(spxerr-audio | ||
# the remote app service the default audio config needs to be set to an audio file | ||
# instead of default as in local machine it cannot default to a speaker in this case. | ||
file_name = str(uuid.uuid4()) + ".mp3" | ||
file_config = AudioOutputConfig(filename=file_name) | ||
|
||
|
||
# Set up pull stream and audio output | ||
pull_stream = PullAudioOutputStream() | ||
audio_config = AudioOutputConfig(stream=pull_stream) | ||
|
||
# Specify the output format | ||
speech_config.set_speech_synthesis_output_format( | ||
SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3) | ||
synthesizer = SpeechSynthesizer(speech_config=speech_config, audio_config=file_config) | ||
SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3 | ||
) | ||
|
||
# Initialize the synthesizer | ||
synthesizer = SpeechSynthesizer( | ||
speech_config=speech_config, audio_config=audio_config | ||
) | ||
|
||
# Start speech synthesis asynchronously | ||
result = synthesizer.speak_text_async(text).get() | ||
|
||
# Handle synthesis result | ||
if result.reason == ResultReason.SynthesizingAudioCompleted: | ||
print("Speech synthesized to speaker for text [{}]".format(text)) | ||
stream = AudioDataStream(result) | ||
print(f"Speech synthesized successfully for text: {text}") | ||
audio_data_stream = AudioDataStream(result) | ||
audio_buffer = bytes(16000) | ||
audio_data = bytearray() | ||
|
||
while True: | ||
num_bytes_read = stream.read_data(audio_buffer) | ||
num_bytes_read = audio_data_stream.read_data(audio_buffer) | ||
if num_bytes_read == 0: | ||
break | ||
audio_data.extend(audio_buffer[:num_bytes_read]) | ||
|
||
return bytes(audio_data) | ||
elif result.reason == ResultReason.Canceled: | ||
cancellation_details = SpeechSynthesisCancellationDetails.from_result( | ||
result) | ||
print("Speech synthesis canceled: {}".format( | ||
cancellation_details.reason)) | ||
if cancellation_details.reason == CancellationReason.Error: | ||
if cancellation_details.error_details: | ||
print("Error details: {}".format( | ||
cancellation_details.error_details)) | ||
print("Did you update the subscription info?") | ||
return {"message": "Speech synthesis canceled", "error": cancellation_details.reason} | ||
result | ||
) | ||
print(f"Speech synthesis canceled: {cancellation_details.reason}") | ||
if ( | ||
cancellation_details.reason == CancellationReason.Error | ||
and cancellation_details.error_details | ||
): | ||
print(f"Error details: {cancellation_details.error_details}") | ||
print("Ensure that the subscription info is correct.") | ||
return { | ||
"message": "Speech synthesis canceled", | ||
"error": cancellation_details.reason, | ||
} | ||
|
||
except Exception as e: | ||
print("Error: {}".format(e)) | ||
return {"message": "Error", "error": e} | ||
print(f"Error during speech synthesis: {e}") | ||
return {"message": "Error", "error": str(e)} | ||
|
||
finally: | ||
# After processing (or if an error occurs), delete the file | ||
os.remove(file_name) | ||
# Ensure resources are cleaned up properly | ||
if "synthesizer" in locals(): | ||
del synthesizer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,3 +15,4 @@ passlib==1.7.4 | |
bcrypt==4.0.1 | ||
SQLAlchemy==2.0.27 | ||
azure-identity~=1.17.1 | ||
azure-functions~=1.21.3 |
Oops, something went wrong.