Coverage for presidio_analyzer / predefined_recognizers / third_party / azure_ai_language.py: 72%

60 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-03-29 09:03 +0000

1import logging 

2import os 

3from typing import List, Optional 

4 

5try: 

6 from azure.ai.textanalytics import TextAnalyticsClient 

7 from azure.core.credentials import AzureKeyCredential 

8 

9except ImportError: 

10 TextAnalyticsClient = None 

11 AzureKeyCredential = None 

12from presidio_analyzer import AnalysisExplanation, RecognizerResult, RemoteRecognizer 

13from presidio_analyzer.nlp_engine import NlpArtifacts 

14 

15logger = logging.getLogger("presidio-analyzer") 

16 

17 

18class AzureAILanguageRecognizer(RemoteRecognizer): 

19 """Wrapper for PII detection using Azure AI Language.""" 

20 

21 def __init__( 

22 self, 

23 supported_entities: Optional[List[str]] = None, 

24 supported_language: str = "en", 

25 ta_client: Optional["TextAnalyticsClient"] = None, 

26 azure_ai_key: Optional[str] = None, 

27 azure_ai_endpoint: Optional[str] = None, 

28 ): 

29 """ 

30 Wrap the PII detection in Azure AI Language. 

31 

32 :param supported_entities: List of supported entities for this recognizer. 

33 If None, all supported entities will be used. 

34 :param supported_language: Language code to use for the recognizer. 

35 :param ta_client: object of type TextAnalyticsClient. If missing, 

36 the client will be created using the key and endpoint. 

37 :param azure_ai_key: Azure AI for language key 

38 :param azure_ai_endpoint: Azure AI for language endpoint 

39 

40 For more info, see https://learn.microsoft.com/en-us/azure/ai-services/language-service/personally-identifiable-information/overview 

41 """ 

42 

43 super().__init__( 

44 supported_entities=supported_entities, 

45 supported_language=supported_language, 

46 name="Azure AI Language PII", 

47 version="5.2.0", 

48 ) 

49 

50 is_available = bool(TextAnalyticsClient) 

51 if not ta_client and not is_available: 

52 raise ValueError( 

53 "Azure AI Language is not available. " 

54 "Please install the required dependencies:" 

55 "1. azure-ai-textanalytics" 

56 "2. azure-core" 

57 ) 

58 

59 if not supported_entities: 

60 self.supported_entities = self.__get_azure_ai_supported_entities() 

61 

62 if not ta_client: 

63 ta_client = self.__authenticate_client(azure_ai_key, azure_ai_endpoint) 

64 self.ta_client = ta_client 

65 

66 def get_supported_entities(self) -> List[str]: 

67 """ 

68 Return the list of entities this recognizer can identify. 

69 

70 :return: A list of the supported entities by this recognizer 

71 """ 

72 return self.supported_entities 

73 

74 @staticmethod 

75 def __get_azure_ai_supported_entities() -> List[str]: 

76 """Return the list of all supported entities for Azure AI Language.""" 

77 from azure.ai.textanalytics._models import PiiEntityCategory 

78 

79 return [r.value.upper() for r in PiiEntityCategory] 

80 

81 @staticmethod 

82 def __authenticate_client(key: str, endpoint: str) -> TextAnalyticsClient: 

83 """Authenticate the client using the key and endpoint. 

84 

85 :param key: Azure AI Language key 

86 :param endpoint: Azure AI Language endpoint 

87 """ 

88 key = key if key else os.getenv("AZURE_AI_KEY", None) 

89 endpoint = endpoint if endpoint else os.getenv("AZURE_AI_ENDPOINT", None) 

90 if key is None: 

91 raise ValueError( 

92 "Azure AI Language key is required. " 

93 "Please provide a key or set the AZURE_AI_KEY environment variable." 

94 ) 

95 if endpoint is None: 

96 raise ValueError( 

97 "Azure AI Language endpoint is required. " 

98 "Please provide an endpoint " 

99 "or set the AZURE_AI_ENDPOINT environment variable." 

100 ) 

101 

102 ta_credential = AzureKeyCredential(key) 

103 text_analytics_client = TextAnalyticsClient( 

104 endpoint=endpoint, credential=ta_credential 

105 ) 

106 return text_analytics_client 

107 

108 def analyze( 

109 self, text: str, entities: List[str] = None, nlp_artifacts: NlpArtifacts = None 

110 ) -> List[RecognizerResult]: 

111 """ 

112 Analyze text using Azure AI Language. 

113 

114 :param text: Text to analyze 

115 :param entities: List of entities to return 

116 :param nlp_artifacts: Object of type NlpArtifacts, not used in this recognizer. 

117 :return: A list of RecognizerResult, one per each entity found in the text. 

118 """ 

119 if not entities: 

120 entities = self.supported_entities 

121 response = self.ta_client.recognize_pii_entities( 

122 [text], language=self.supported_language 

123 ) 

124 results = [doc for doc in response if not doc.is_error] 

125 recognizer_results = [] 

126 for res in results: 

127 for entity in res.entities: 

128 entity.category = entity.category.upper() 

129 if entity.category.lower() not in [ 

130 ent.lower() for ent in self.supported_entities 

131 ]: 

132 continue 

133 if entity.category.lower() not in [ent.lower() for ent in entities]: 

134 continue 

135 analysis_explanation = AzureAILanguageRecognizer._build_explanation( 

136 original_score=entity.confidence_score, 

137 entity_type=entity.category, 

138 ) 

139 recognizer_results.append( 

140 RecognizerResult( 

141 entity_type=entity.category, 

142 start=entity.offset, 

143 end=entity.offset + entity.length, 

144 score=entity.confidence_score, 

145 analysis_explanation=analysis_explanation, 

146 ) 

147 ) 

148 

149 return recognizer_results 

150 

151 @staticmethod 

152 def _build_explanation( 

153 original_score: float, entity_type: str 

154 ) -> AnalysisExplanation: 

155 explanation = AnalysisExplanation( 

156 recognizer=AzureAILanguageRecognizer.__class__.__name__, 

157 original_score=original_score, 

158 textual_explanation=f"Identified as {entity_type} by Azure AI Language", 

159 ) 

160 return explanation