Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/tLTGCA4G)
---
title: "Activity 1 - Hello, Azure AI"
type: lab
Expand Down
12 changes: 12 additions & 0 deletions REFLECTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,26 @@

Answer these questions after completing the activity (2-3 sentences each). Connect your answers to specific things you observed while coding and experimenting.


Thanks for all you do to make this class possible.

![Hand Waving](https://github.com/Mayur-Pagote/README_Design_Kit/blob/main/public/Assets/Hand%20Waving.gif)

## 1. Service Surprises

Which of the three Azure AI services (OpenAI, Content Safety, Language) surprised you the most? Connect this to something specific you observed during your experiments -- a response you didn't expect, a behavior that seemed too easy or too hard, or a result that made you rethink how the service works.

_The content saftey suprosed me the most. I thought it would be more sensitive, but it didn't give as many false positives as I thought it was. But it did error out when I fed it antisaftey prompts._

## 2. Lazy Initialization

How would you explain the lazy initialization pattern to a colleague? Why is it used instead of creating clients at the top of the file?

_When you create them at the top you are using memory resources. Loading them when the item is actually called cane save on memory and make the program start quicker_

## 3. Content Safety in the Real World

A resident files this complaint: *"A man was assaulted at this intersection because the street light has been out for months."* This text describes real violence but is a legitimate safety concern. Should the system block it, flag it for human review, or pass it through? What factors would you weigh in making that decision?

_The system should allow it through. I changed the prompts to things that we actual violence and it triggered an error and warning._
_It knew that it wasnt a threat of violence it was explaining something_
109 changes: 43 additions & 66 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,12 @@ def _get_openai_client():
global _openai_client
if _openai_client is None:
# TODO: Uncomment and configure
# from openai import AzureOpenAI
# _openai_client = AzureOpenAI(
# azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
# api_key=os.environ["AZURE_OPENAI_API_KEY"],
# api_version="2024-10-21",
# )
raise NotImplementedError("Configure the Azure OpenAI client")
from openai import AzureOpenAI
_openai_client = AzureOpenAI(
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
api_key=os.environ["AZURE_OPENAI_API_KEY"],
api_version="2024-10-21",
)
return _openai_client


Expand All @@ -62,90 +61,68 @@ def _get_content_safety_client():
# NOTE: The Content Safety SDK handles API versioning internally --
# no api_version parameter is needed (unlike the OpenAI SDK).
# TODO: Uncomment and configure
# from azure.ai.contentsafety import ContentSafetyClient
# from azure.core.credentials import AzureKeyCredential
# _content_safety_client = ContentSafetyClient(
# endpoint=os.environ["AZURE_CONTENT_SAFETY_ENDPOINT"],
# credential=AzureKeyCredential(os.environ["AZURE_CONTENT_SAFETY_KEY"]),
# )
raise NotImplementedError("Configure the Content Safety client")
from azure.ai.contentsafety import ContentSafetyClient
from azure.core.credentials import AzureKeyCredential
_content_safety_client = ContentSafetyClient(
endpoint=os.environ["AZURE_CONTENT_SAFETY_ENDPOINT"],
credential=AzureKeyCredential(os.environ["AZURE_CONTENT_SAFETY_KEY"]),
)
return _content_safety_client


def _get_language_client():
"""Lazily initialize the Azure AI Language client."""
global _language_client
if _language_client is None:
# NOTE: The Language SDK handles API versioning internally --
# no api_version parameter is needed (unlike the OpenAI SDK).
# TODO: Uncomment and configure
# from azure.ai.textanalytics import TextAnalyticsClient
# from azure.core.credentials import AzureKeyCredential
# _language_client = TextAnalyticsClient(
# endpoint=os.environ["AZURE_AI_LANGUAGE_ENDPOINT"],
# credential=AzureKeyCredential(os.environ["AZURE_AI_LANGUAGE_KEY"]),
# )
raise NotImplementedError("Configure the AI Language client")
from azure.ai.textanalytics import TextAnalyticsClient
from azure.core.credentials import AzureKeyCredential
_language_client = TextAnalyticsClient(
endpoint=os.environ["AZURE_AI_LANGUAGE_ENDPOINT"],
credential=AzureKeyCredential(os.environ["AZURE_AI_LANGUAGE_KEY"]),
)
#raise NotImplementedError("Configure the AI Language client")
return _language_client


# ---------------------------------------------------------------------------
# TODO: Step 1 - Classify a 311 request with Azure OpenAI
# ---------------------------------------------------------------------------
def classify_311_request(request_text: str) -> dict:
"""Send a Memphis 311 service request to Azure OpenAI for classification.

Args:
request_text: The citizen's complaint text.

Returns:
dict with keys: category, confidence, reasoning
"""
# TODO: Step 1.1 - Get the OpenAI client
# TODO: Step 1.2 - Call client.chat.completions.create() with:
# model=os.environ.get("AZURE_OPENAI_DEPLOYMENT", "gpt-4o")
# A system message that classifies into: Pothole, Noise Complaint,
# Trash/Litter, Street Light, Water/Sewer, Other
# response_format={"type": "json_object"}, temperature=0
# TODO: Step 1.3 - Parse the JSON response with json.loads()
raise NotImplementedError("Implement classify_311_request in Step 1")
response = _get_openai_client().chat.completions.create(
model=os.environ.get("AZURE_OPENAI_DEPLOYMENT", "gpt-4o"),
messages=[
{"role": "system", "content": "Classify this Memphis 311 request into one of: Pothole, Noise Complaint, Trash/Litter, Street Light, Water/Sewer, Other. Return JSON with keys: category, confidence, reasoning."},
{"role": "user", "content": request_text},
],
response_format={"type": "json_object"},
temperature=0
)
result = json.loads(response.choices[0].message.content)
return result


# ---------------------------------------------------------------------------
# TODO: Step 2 - Check content safety
# ---------------------------------------------------------------------------
def check_content_safety(text: str) -> dict:
"""Check text for harmful content using Azure Content Safety.

Args:
text: Text to analyze.

Returns:
dict with keys: safe (bool), categories (dict of category: severity)
"""
# TODO: Step 2.1 - Get the Content Safety client
# TODO: Step 2.2 - Call client.analyze_text() with AnalyzeTextOptions
# TODO: Step 2.3 - Return safety results
raise NotImplementedError("Implement check_content_safety in Step 2")
from azure.ai.contentsafety.models import AnalyzeTextOptions
result = _get_content_safety_client().analyze_text(AnalyzeTextOptions(text=text))
categories = {
cat.category: cat.severity
for cat in result.categories_analysis
}
safe = all(severity == 0 for severity in categories.values())
return {"safe": True, "categories": categories}


# ---------------------------------------------------------------------------
# TODO: Step 3 - Extract key phrases
# ---------------------------------------------------------------------------
def extract_key_phrases(text: str) -> list[str]:
"""Extract key phrases from text using Azure AI Language.

Args:
text: Text to analyze.

Returns:
List of key phrase strings.
"""
# TODO: Step 3.1 - Get the Language client
# TODO: Step 3.2 - Call client.extract_key_phrases([text])
# TODO: Step 3.3 - Return the list of key phrases
raise NotImplementedError("Implement extract_key_phrases in Step 3")

response = _get_language_client().extract_key_phrases([text])
if response[0].is_error:
raise ValueError(f"Error extracting key phrases: {response[0].error}")
return list(response[0].key_phrases)

def main():
"""Main function -- call all three Azure AI services."""
Expand Down Expand Up @@ -272,4 +249,4 @@ def _print_dispatch_ticket(complaint_text: str, result: dict):


if __name__ == "__main__":
main()
main()
2 changes: 1 addition & 1 deletion reference/sdk-vs-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,4 @@ Notice the differences: the auth header name changes (`api-key` for OpenAI vs `O

## Key Takeaway

Use the SDK unless you have a specific reason not to. The SDK gives you retries, typed responses, and proper error handling -- exactly the things that are easy to forget when writing raw HTTP calls at 2 AM before a deadline.
Use the SDK unless you have a specific reason not to. The SDK gives you retries, typed responses, and proper error handling -- exactly the things that are easy to forget when writing raw HTTP calls at 2 AM before a deadline.
Loading