microsoft-agents-a365-tooling-extensions-azureaifoundry 0.2.1.dev2__py3-none-any.whl → 0.2.1.dev5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,18 +11,21 @@ servers to agents.
11
11
 
12
12
  # Standard library imports
13
13
  import logging
14
- from typing import Optional, List, Tuple
14
+ from typing import List, Optional, Sequence, Tuple
15
15
 
16
16
  # Third-party imports - Azure AI
17
+ from azure.ai.agents import AgentsClient
18
+ from azure.ai.agents.models import McpTool, ThreadMessage, ToolResources
17
19
  from azure.ai.projects import AIProjectClient
18
20
  from azure.identity import DefaultAzureCredential
19
- from azure.ai.agents.models import McpTool, ToolResources
20
21
  from microsoft_agents.hosting.core import Authorization, TurnContext
22
+
23
+ from microsoft_agents_a365.runtime import OperationError, OperationResult
21
24
  from microsoft_agents_a365.runtime.utility import Utility
25
+ from microsoft_agents_a365.tooling.models import ChatHistoryMessage, ToolOptions
22
26
  from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import (
23
27
  McpToolServerConfigurationService,
24
28
  )
25
- from microsoft_agents_a365.tooling.models import ToolOptions
26
29
  from microsoft_agents_a365.tooling.utils.constants import Constants
27
30
  from microsoft_agents_a365.tooling.utils.utility import get_mcp_platform_authentication_scope
28
31
 
@@ -217,3 +220,253 @@ class McpToolRegistrationService:
217
220
  )
218
221
 
219
222
  return (tool_definitions, combined_tool_resources)
223
+
224
+ # ============================================================================
225
+ # Public Methods - Chat History API
226
+ # ============================================================================
227
+
228
+ async def send_chat_history_messages(
229
+ self,
230
+ turn_context: TurnContext,
231
+ messages: Sequence[ThreadMessage],
232
+ tool_options: Optional[ToolOptions] = None,
233
+ ) -> OperationResult:
234
+ """
235
+ Send Azure AI Foundry chat history messages to the MCP platform.
236
+
237
+ This method accepts a sequence of Azure AI Foundry ThreadMessage objects,
238
+ converts them to ChatHistoryMessage format, and sends them to the MCP
239
+ platform for real-time threat protection.
240
+
241
+ Args:
242
+ turn_context: TurnContext from the Agents SDK containing conversation info.
243
+ messages: Sequence of Azure AI Foundry ThreadMessage objects to send.
244
+ tool_options: Optional configuration for the request.
245
+
246
+ Returns:
247
+ OperationResult indicating success or failure.
248
+
249
+ Raises:
250
+ ValueError: If turn_context or messages is None.
251
+
252
+ Example:
253
+ >>> service = McpToolRegistrationService()
254
+ >>> messages = await agents_client.messages.list(thread_id=thread_id)
255
+ >>> result = await service.send_chat_history_messages(
256
+ ... turn_context, list(messages)
257
+ ... )
258
+ >>> if result.succeeded:
259
+ ... print("Chat history sent successfully")
260
+ """
261
+ # Input validation
262
+ if turn_context is None:
263
+ raise ValueError("turn_context cannot be None")
264
+ if messages is None:
265
+ raise ValueError("messages cannot be None")
266
+
267
+ self._logger.info(f"Sending {len(messages)} Azure AI Foundry messages as chat history")
268
+
269
+ # Set default options with orchestrator name
270
+ if tool_options is None:
271
+ tool_options = ToolOptions(orchestrator_name=self._orchestrator_name)
272
+ elif tool_options.orchestrator_name is None:
273
+ tool_options.orchestrator_name = self._orchestrator_name
274
+
275
+ try:
276
+ # Convert ThreadMessage objects to ChatHistoryMessage format
277
+ chat_history_messages = self._convert_thread_messages_to_chat_history(messages)
278
+
279
+ self._logger.debug(
280
+ f"Converted {len(chat_history_messages)} messages to ChatHistoryMessage format"
281
+ )
282
+
283
+ # Delegate to core service
284
+ result = await self._mcp_server_configuration_service.send_chat_history(
285
+ turn_context=turn_context,
286
+ chat_history_messages=chat_history_messages,
287
+ options=tool_options,
288
+ )
289
+
290
+ if result.succeeded:
291
+ self._logger.info(
292
+ f"Chat history sent successfully with {len(chat_history_messages)} messages"
293
+ )
294
+ else:
295
+ self._logger.error(f"Failed to send chat history: {result}")
296
+
297
+ return result
298
+
299
+ except ValueError:
300
+ # Re-raise validation errors from the core service
301
+ raise
302
+ except Exception as ex:
303
+ self._logger.error(f"Failed to send chat history messages: {ex}")
304
+ return OperationResult.failed(OperationError(ex))
305
+
306
+ async def send_chat_history(
307
+ self,
308
+ agents_client: AgentsClient,
309
+ thread_id: str,
310
+ turn_context: TurnContext,
311
+ tool_options: Optional[ToolOptions] = None,
312
+ ) -> OperationResult:
313
+ """
314
+ Retrieve and send chat history from Azure AI Foundry to the MCP platform.
315
+
316
+ This method retrieves messages from the Azure AI Foundry Agents API using
317
+ the provided client and thread ID, converts them to ChatHistoryMessage
318
+ format, and sends them to the MCP platform.
319
+
320
+ Args:
321
+ agents_client: The Azure AI Foundry AgentsClient instance.
322
+ thread_id: The thread ID containing the messages to send.
323
+ turn_context: TurnContext from the Agents SDK containing conversation info.
324
+ tool_options: Optional configuration for the request.
325
+
326
+ Returns:
327
+ OperationResult indicating success or failure.
328
+
329
+ Raises:
330
+ ValueError: If agents_client, thread_id, or turn_context is None/empty.
331
+
332
+ Example:
333
+ >>> from azure.ai.agents import AgentsClient
334
+ >>> from azure.identity import DefaultAzureCredential
335
+ >>>
336
+ >>> client = AgentsClient(endpoint, credential=DefaultAzureCredential())
337
+ >>> service = McpToolRegistrationService()
338
+ >>> result = await service.send_chat_history(
339
+ ... client, thread_id, turn_context
340
+ ... )
341
+ """
342
+ # Input validation
343
+ if agents_client is None:
344
+ raise ValueError("agents_client cannot be None")
345
+ if thread_id is None or not thread_id.strip():
346
+ raise ValueError("thread_id cannot be empty")
347
+ if turn_context is None:
348
+ raise ValueError("turn_context cannot be None")
349
+
350
+ try:
351
+ # Retrieve messages from the thread
352
+ messages: List[ThreadMessage] = []
353
+ async for message in agents_client.messages.list(thread_id=thread_id):
354
+ messages.append(message)
355
+
356
+ self._logger.info(f"Retrieved {len(messages)} messages from thread {thread_id}")
357
+
358
+ # Delegate to send_chat_history_messages
359
+ return await self.send_chat_history_messages(
360
+ turn_context=turn_context,
361
+ messages=messages,
362
+ tool_options=tool_options,
363
+ )
364
+
365
+ except ValueError:
366
+ # Re-raise validation errors
367
+ raise
368
+ except Exception as ex:
369
+ self._logger.error(f"Failed to send chat history from thread {thread_id}: {ex}")
370
+ return OperationResult.failed(OperationError(ex))
371
+
372
+ # ============================================================================
373
+ # Private Methods - Message Conversion Helpers
374
+ # ============================================================================
375
+
376
+ def _convert_thread_messages_to_chat_history(
377
+ self,
378
+ messages: Sequence[ThreadMessage],
379
+ ) -> List[ChatHistoryMessage]:
380
+ """
381
+ Convert Azure AI Foundry ThreadMessage objects to ChatHistoryMessage format.
382
+
383
+ This internal helper method transforms Azure AI Foundry's native ThreadMessage
384
+ objects into the ChatHistoryMessage format expected by the MCP platform's
385
+ real-time threat protection endpoint.
386
+
387
+ Args:
388
+ messages: Sequence of ThreadMessage objects to convert.
389
+
390
+ Returns:
391
+ List of ChatHistoryMessage objects ready for the MCP platform.
392
+
393
+ Note:
394
+ - Messages with None id, None role, or empty content are filtered out
395
+ - Role is extracted via the .value property of the MessageRole enum
396
+ - Timestamp is taken from message.created_at
397
+ """
398
+ history_messages: List[ChatHistoryMessage] = []
399
+
400
+ for message in messages:
401
+ # Skip None messages
402
+ if message is None:
403
+ self._logger.warning("Skipping null message")
404
+ continue
405
+
406
+ # Skip messages with None id
407
+ if message.id is None:
408
+ self._logger.warning("Skipping message with null ID")
409
+ continue
410
+
411
+ # Skip messages with None role
412
+ if message.role is None:
413
+ self._logger.warning(f"Skipping message with null role (ID: {message.id})")
414
+ continue
415
+
416
+ # Extract content from message
417
+ content = self._extract_content_from_message(message)
418
+
419
+ # Skip messages with empty content
420
+ if not content or not content.strip():
421
+ self._logger.warning(f"Skipping message {message.id} with empty content")
422
+ continue
423
+
424
+ # Convert role enum to lowercase string
425
+ role_value = message.role.value if hasattr(message.role, "value") else str(message.role)
426
+ role = role_value.lower()
427
+
428
+ # Create ChatHistoryMessage
429
+ history_message = ChatHistoryMessage(
430
+ id=message.id,
431
+ role=role,
432
+ content=content,
433
+ timestamp=message.created_at,
434
+ )
435
+ history_messages.append(history_message)
436
+
437
+ self._logger.debug(
438
+ f"Converted message {message.id} with role '{role}' to ChatHistoryMessage"
439
+ )
440
+
441
+ if len(history_messages) == 0 and len(messages) > 0:
442
+ self._logger.warning("All messages were filtered out during conversion")
443
+
444
+ return history_messages
445
+
446
+ def _extract_content_from_message(self, message: ThreadMessage) -> str:
447
+ """
448
+ Extract text content from a ThreadMessage's content items.
449
+
450
+ This method iterates through the message's content list and extracts
451
+ text from MessageTextContent items, concatenating them with spaces.
452
+
453
+ Args:
454
+ message: Azure AI Foundry ThreadMessage object.
455
+
456
+ Returns:
457
+ Concatenated text content as string, or empty string if no text found.
458
+ """
459
+ if message.content is None or len(message.content) == 0:
460
+ return ""
461
+
462
+ text_parts: List[str] = []
463
+
464
+ for content_item in message.content:
465
+ # Check for MessageTextContent by duck typing (has text attribute with value)
466
+ # This handles both real SDK types and mock objects in tests
467
+ if hasattr(content_item, "text") and content_item.text is not None:
468
+ text_value = getattr(content_item.text, "value", None)
469
+ if text_value is not None and text_value:
470
+ text_parts.append(text_value)
471
+
472
+ return " ".join(text_parts)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microsoft-agents-a365-tooling-extensions-azureaifoundry
3
- Version: 0.2.1.dev2
3
+ Version: 0.2.1.dev5
4
4
  Summary: Azure AI Foundry integration for Agent 365 Tooling SDK
5
5
  Author-email: Microsoft <support@microsoft.com>
6
6
  License: MIT
@@ -1,7 +1,7 @@
1
1
  microsoft_agents_a365/tooling/extensions/azureaifoundry/__init__.py,sha256=kK4Q6Sa0vMEwPvI_nnIUJj9rCVfLxsW8Ecghy3tWYlI,487
2
2
  microsoft_agents_a365/tooling/extensions/azureaifoundry/services/__init__.py,sha256=i6HP6TSmCi9XQkm0xZ304xwy7B0GE4RDB4u5d8K9V_k,392
3
- microsoft_agents_a365/tooling/extensions/azureaifoundry/services/mcp_tool_registration_service.py,sha256=vmSvFR0tH9fbZLKLc2MPMSPNjl5vSg5y76NgNY-6VIw,8891
4
- microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev2.dist-info/METADATA,sha256=FmCMQi5R0cbc02eJGNi-m4vrf0irxUvb5QNHL_dsBL0,3418
5
- microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
6
- microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev2.dist-info/top_level.txt,sha256=G3c2_4sy5_EM_BWO67SbK2tKj4G8XFn-QXRbh8g9Lgk,22
7
- microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev2.dist-info/RECORD,,
3
+ microsoft_agents_a365/tooling/extensions/azureaifoundry/services/mcp_tool_registration_service.py,sha256=lvnlGH7DX-QSFXzhdhioYZ0JRGTWH-8uE3WbpitZwE8,18949
4
+ microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev5.dist-info/METADATA,sha256=YiCEjz5fzQWyniauI0NwBJ4JRNTocRGCTRK-m609hJo,3418
5
+ microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
6
+ microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev5.dist-info/top_level.txt,sha256=G3c2_4sy5_EM_BWO67SbK2tKj4G8XFn-QXRbh8g9Lgk,22
7
+ microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev5.dist-info/RECORD,,