1+ from io import BytesIO
2+ from agent_framework import AgentRunResponse , ChatAgent , ChatMessage , ai_function
3+ from agent_framework .openai import OpenAIResponsesClient
4+
5+ import asyncio
6+ import os
7+ import httpx
8+ import logging
9+ import base64
10+ from urllib .parse import urlparse
11+ from random import randrange
12+ from typing import TYPE_CHECKING , Annotated , Any
13+ from azure .storage .blob import BlobServiceClient
14+ from PIL import Image
15+ from azure .identity import ManagedIdentityCredential , DefaultAzureCredential
16+
17+ logger = logging .getLogger (__name__ )
18+ logger .setLevel (logging .INFO )
19+
20+ @ai_function
21+ def get_image_content (url : Annotated [str , "url to image stored in Azure Blob Storage" ]) -> str :
22+ """Get the current weather for a given location."""
23+ # Simulate weather data
24+ client_id = os .getenv ("AZURE_CLIENT_ID" , None )
25+ return ImageLoader (client_id ).encode_image_from_url (url )
26+
27+
28+ class ImageLoader :
29+ def __init__ (self , client_id : str = None ):
30+ # Use managed identity for blob storage access
31+ if client_id :
32+ logger .info (f"Using managed identity { client_id } for blob storage access" )
33+ self ._credential = ManagedIdentityCredential (client_id = client_id )
34+ else :
35+ logger .info ("Using default Azure credential for blob storage access" )
36+ self ._credential = DefaultAzureCredential ()
37+ logger .info (f"Credential initialized: { self ._credential } " )
38+
39+
40+ def _blob_service_client (self , storage_account_url : str ) -> str :
41+ return BlobServiceClient (
42+ account_url = storage_account_url ,
43+ credential = self ._credential
44+ )
45+
46+ def encode_image_from_url (self , image_url : str ) -> str :
47+ logger .info (f"Encoding image from URL: { image_url } " )
48+ """Encode image from URL to base64, using managed identity for Azure blob storage URLs."""
49+ try :
50+ # Check if this is an Azure blob storage URL
51+ if self ._is_azure_blob_url (image_url ):
52+ logger .info ("Detected Azure blob storage URL, using authenticated access" )
53+ return self ._encode_image_from_blob_url (image_url )
54+ else :
55+ # Regular HTTP URL - use direct access
56+ logger .info ("Using direct HTTP access for non-blob URL" )
57+ with httpx .Client () as client :
58+ response = client .get (image_url )
59+ response .raise_for_status ()
60+
61+ logger .info (f"Image fetched successfully: { len (response .content )} bytes" )
62+ image = Image .open (BytesIO (response .content ))
63+
64+ # Convert to base64
65+ logger .info ("Encoding image to base64" )
66+ return base64 .b64encode (response .content ).decode ('utf-8' )
67+ except Exception as e :
68+ logger .error (f"Error encoding image from URL { image_url } : { str (e )} " )
69+ raise RuntimeError (f"Failed to process image from URL: { str (e )} " ) from e
70+
71+ def _is_azure_blob_url (self , url : str ) -> bool :
72+ """Check if URL is an Azure blob storage URL."""
73+ try :
74+ parsed = urlparse (url )
75+ logger .info (f"Parsed URL netloc: { parsed .netloc } " )
76+ return 'blob.core.windows.net' in parsed .netloc
77+ except Exception :
78+ return False
79+
80+ def _encode_image_from_blob_url (self , blob_url : str ) -> str :
81+ """Encode image from Azure blob storage URL using managed identity."""
82+ try :
83+ # Parse the blob URL to extract container and blob name
84+ parsed = urlparse (blob_url )
85+ path_parts = parsed .path .lstrip ('/' ).split ('/' )
86+ logger .info (f"Parsed blob URL path parts: { path_parts } " )
87+
88+ if len (path_parts ) < 2 :
89+ raise RuntimeError ("Invalid blob URL format: {blob_url}" )
90+
91+ container_name = path_parts [0 ]
92+ blob_name = '/' .join (path_parts [1 :])
93+
94+ logger .info (f"Accessing blob: container={ container_name } , blob={ blob_name } " )
95+
96+ # Get blob client and download content
97+ blob_client = self ._blob_service_client (parsed .netloc ).get_blob_client (
98+ container = container_name ,
99+ blob = blob_name
100+ )
101+
102+ # Download blob content
103+ with blob_client :
104+ blob_data = blob_client .download_blob ()
105+ content = blob_data .readall ()
106+
107+ logger .info (f"Blob downloaded successfully: { len (content )} bytes" )
108+
109+ # Validate it's an image
110+ image = Image .open (BytesIO (content ))
111+
112+ # Convert to base64
113+ logger .info ("Encoding blob image to base64" )
114+ return base64 .b64encode (content ).decode ('utf-8' )
115+
116+ except Exception as e :
117+ logger .error (f"Error accessing blob { blob_url } : { str (e )} " )
118+ raise RuntimeError (
119+ f"Failed to access blob with managed identity: { str (e )} "
120+ ) from e
121+
0 commit comments