microsoft-agents-a365-tooling 0.2.0.dev5__tar.gz → 0.2.1.dev0__tar.gz
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.
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/PKG-INFO +4 -1
- microsoft_agents_a365_tooling-0.2.1.dev0/microsoft_agents_a365/tooling/extensions/__init__.py +28 -0
- microsoft_agents_a365_tooling-0.2.1.dev0/microsoft_agents_a365/tooling/models/__init__.py +15 -0
- microsoft_agents_a365_tooling-0.2.1.dev0/microsoft_agents_a365/tooling/models/chat_history_message.py +49 -0
- microsoft_agents_a365_tooling-0.2.1.dev0/microsoft_agents_a365/tooling/models/chat_message_request.py +64 -0
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365/tooling/models/mcp_server_config.py +7 -1
- microsoft_agents_a365_tooling-0.2.1.dev0/microsoft_agents_a365/tooling/models/tool_options.py +17 -0
- microsoft_agents_a365_tooling-0.2.1.dev0/microsoft_agents_a365/tooling/py.typed +0 -0
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365/tooling/services/__init__.py +2 -1
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365/tooling/services/mcp_tool_server_configuration_service.py +232 -19
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365/tooling/utils/__init__.py +0 -2
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365/tooling/utils/constants.py +5 -1
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365/tooling/utils/utility.py +20 -37
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365_tooling.egg-info/PKG-INFO +4 -1
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365_tooling.egg-info/SOURCES.txt +8 -0
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365_tooling.egg-info/requires.txt +1 -0
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365_tooling.egg-info/top_level.txt +1 -0
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/pyproject.toml +6 -0
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/setup.py +1 -1
- microsoft_agents_a365_tooling-0.2.0.dev5/microsoft_agents_a365/tooling/models/__init__.py +0 -11
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/README.md +0 -0
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365/tooling/__init__.py +0 -0
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/microsoft_agents_a365_tooling.egg-info/dependency_links.txt +0 -0
- {microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/setup.cfg +0 -0
{microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microsoft-agents-a365-tooling
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1.dev0
|
|
4
4
|
Summary: Agent 365 Tooling SDK, providing functionality to work with Agent 365 Tools.
|
|
5
5
|
Author-email: Microsoft <support@microsoft.com>
|
|
6
6
|
License: MIT
|
|
@@ -18,14 +18,17 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
18
18
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
19
|
Requires-Python: >=3.11
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
21
22
|
Requires-Dist: pydantic>=2.0.0
|
|
22
23
|
Requires-Dist: typing-extensions>=4.0.0
|
|
24
|
+
Requires-Dist: microsoft-agents-hosting-core<0.6.0,>=0.4.0
|
|
23
25
|
Provides-Extra: dev
|
|
24
26
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
25
27
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
26
28
|
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
27
29
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
28
30
|
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
29
32
|
|
|
30
33
|
# microsoft-agents-a365-tooling
|
|
31
34
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""Microsoft Agent 365 Tooling Extensions namespace package.
|
|
5
|
+
|
|
6
|
+
This file enables the `microsoft_agents_a365.tooling.extensions` namespace
|
|
7
|
+
to span multiple installed packages (e.g., extensions-openai, extensions-agentframework).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
from pkgutil import extend_path
|
|
12
|
+
|
|
13
|
+
# Standard pkgutil-style namespace extension
|
|
14
|
+
__path__ = extend_path(__path__, __name__)
|
|
15
|
+
|
|
16
|
+
# For editable installs with custom finders, manually discover extension paths
|
|
17
|
+
for finder in sys.meta_path:
|
|
18
|
+
if hasattr(finder, "find_spec"):
|
|
19
|
+
try:
|
|
20
|
+
spec = finder.find_spec(__name__, None)
|
|
21
|
+
if spec is not None and spec.submodule_search_locations:
|
|
22
|
+
for path in spec.submodule_search_locations:
|
|
23
|
+
if path not in __path__ and not path.endswith(".__path_hook__"):
|
|
24
|
+
__path__.append(path)
|
|
25
|
+
except (ImportError, TypeError):
|
|
26
|
+
# Some meta path finders may not support this namespace and can raise
|
|
27
|
+
# ImportError or TypeError; ignore these and continue discovering paths.
|
|
28
|
+
pass
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Common models for MCP tooling.
|
|
6
|
+
|
|
7
|
+
This module defines data models used across the MCP tooling framework.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .chat_history_message import ChatHistoryMessage
|
|
11
|
+
from .chat_message_request import ChatMessageRequest
|
|
12
|
+
from .mcp_server_config import MCPServerConfig
|
|
13
|
+
from .tool_options import ToolOptions
|
|
14
|
+
|
|
15
|
+
__all__ = ["MCPServerConfig", "ToolOptions", "ChatHistoryMessage", "ChatMessageRequest"]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""Chat history message model."""
|
|
5
|
+
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Literal, Optional
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ChatHistoryMessage(BaseModel):
|
|
13
|
+
"""
|
|
14
|
+
Represents a single message in the chat history.
|
|
15
|
+
|
|
16
|
+
This model is used to capture individual messages exchanged between
|
|
17
|
+
users and the AI assistant for threat protection analysis and
|
|
18
|
+
compliance monitoring.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
id: Optional unique identifier for the message.
|
|
22
|
+
role: The role of the message sender (user, assistant, or system).
|
|
23
|
+
content: The text content of the message.
|
|
24
|
+
timestamp: Optional timestamp when the message was created.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
>>> message = ChatHistoryMessage(role="user", content="Hello, how can you help?")
|
|
28
|
+
>>> print(message.role)
|
|
29
|
+
'user'
|
|
30
|
+
>>> print(message.content)
|
|
31
|
+
'Hello, how can you help?'
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
35
|
+
|
|
36
|
+
id: Optional[str] = Field(default=None, description="Unique message identifier")
|
|
37
|
+
role: Literal["user", "assistant", "system"] = Field(
|
|
38
|
+
..., description="The role of the message sender"
|
|
39
|
+
)
|
|
40
|
+
content: str = Field(..., description="The message content")
|
|
41
|
+
timestamp: Optional[datetime] = Field(default=None, description="When the message was created")
|
|
42
|
+
|
|
43
|
+
@field_validator("content")
|
|
44
|
+
@classmethod
|
|
45
|
+
def content_not_empty(cls, v: str) -> str:
|
|
46
|
+
"""Validate that content is not empty or whitespace-only."""
|
|
47
|
+
if not v or not v.strip():
|
|
48
|
+
raise ValueError("content cannot be empty or whitespace")
|
|
49
|
+
return v
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""Chat message request model."""
|
|
5
|
+
|
|
6
|
+
from typing import List
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
9
|
+
|
|
10
|
+
from .chat_history_message import ChatHistoryMessage
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ChatMessageRequest(BaseModel):
|
|
14
|
+
"""
|
|
15
|
+
Request payload for sending chat history to MCP platform.
|
|
16
|
+
|
|
17
|
+
This model represents the complete request body sent to the MCP platform's
|
|
18
|
+
chat history endpoint for threat protection analysis. It includes the
|
|
19
|
+
current conversation context and historical messages.
|
|
20
|
+
|
|
21
|
+
The model uses field aliases to serialize to camelCase JSON format
|
|
22
|
+
as required by the MCP platform API.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
conversation_id: Unique identifier for the conversation.
|
|
26
|
+
message_id: Unique identifier for the current message.
|
|
27
|
+
user_message: The current user message being processed.
|
|
28
|
+
chat_history: List of previous messages in the conversation.
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> from microsoft_agents_a365.tooling.models import ChatHistoryMessage
|
|
32
|
+
>>> request = ChatMessageRequest(
|
|
33
|
+
... conversation_id="conv-123",
|
|
34
|
+
... message_id="msg-456",
|
|
35
|
+
... user_message="What is the weather today?",
|
|
36
|
+
... chat_history=[
|
|
37
|
+
... ChatHistoryMessage(role="user", content="Hello"),
|
|
38
|
+
... ChatHistoryMessage(role="assistant", content="Hi there!"),
|
|
39
|
+
... ]
|
|
40
|
+
... )
|
|
41
|
+
>>> # Serialize to camelCase JSON
|
|
42
|
+
>>> json_dict = request.model_dump(by_alias=True)
|
|
43
|
+
>>> print(json_dict["conversationId"])
|
|
44
|
+
'conv-123'
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
48
|
+
|
|
49
|
+
conversation_id: str = Field(
|
|
50
|
+
..., alias="conversationId", description="Unique conversation identifier"
|
|
51
|
+
)
|
|
52
|
+
message_id: str = Field(..., alias="messageId", description="Current message identifier")
|
|
53
|
+
user_message: str = Field(..., alias="userMessage", description="The current user message")
|
|
54
|
+
chat_history: List[ChatHistoryMessage] = Field(
|
|
55
|
+
..., alias="chatHistory", description="Previous messages in the conversation"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
@field_validator("conversation_id", "message_id", "user_message")
|
|
59
|
+
@classmethod
|
|
60
|
+
def not_empty(cls, v: str) -> str:
|
|
61
|
+
"""Validate that string fields are not empty or whitespace-only."""
|
|
62
|
+
if not v or not v.strip():
|
|
63
|
+
raise ValueError("Field cannot be empty or whitespace")
|
|
64
|
+
return v
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
# Copyright (c) Microsoft
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
2
3
|
|
|
3
4
|
"""
|
|
4
5
|
MCP Server Configuration model.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
from dataclasses import dataclass
|
|
9
|
+
from typing import Optional
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
@dataclass
|
|
@@ -19,6 +21,10 @@ class MCPServerConfig:
|
|
|
19
21
|
#: Gets or sets the unique name of the MCP server.
|
|
20
22
|
mcp_server_unique_name: str
|
|
21
23
|
|
|
24
|
+
#: Gets or sets the custom URL for the MCP server. If provided, this URL will be used
|
|
25
|
+
#: instead of constructing the URL from the base URL and unique name.
|
|
26
|
+
url: Optional[str] = None
|
|
27
|
+
|
|
22
28
|
def __post_init__(self):
|
|
23
29
|
"""Validate the configuration after initialization."""
|
|
24
30
|
if not self.mcp_server_name:
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Tooling Options model.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ToolOptions:
|
|
14
|
+
"""Configuration options for tooling operations."""
|
|
15
|
+
|
|
16
|
+
#: Gets or sets the name of the orchestrator.
|
|
17
|
+
orchestrator_name: Optional[str]
|
|
File without changes
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) Microsoft
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
2
3
|
|
|
3
4
|
"""
|
|
4
5
|
MCP Tool Server Configuration Service.
|
|
@@ -17,20 +18,42 @@ The service supports both development and production scenarios:
|
|
|
17
18
|
# ==============================================================================
|
|
18
19
|
|
|
19
20
|
# Standard library imports
|
|
21
|
+
import asyncio
|
|
20
22
|
import json
|
|
21
23
|
import logging
|
|
22
24
|
import os
|
|
23
25
|
import sys
|
|
24
26
|
from pathlib import Path
|
|
25
27
|
from typing import Any, Dict, List, Optional
|
|
28
|
+
from urllib.parse import urlparse
|
|
26
29
|
|
|
27
30
|
# Third-party imports
|
|
28
31
|
import aiohttp
|
|
32
|
+
from microsoft_agents.hosting.core import TurnContext
|
|
29
33
|
|
|
30
34
|
# Local imports
|
|
31
|
-
from ..models import MCPServerConfig
|
|
35
|
+
from ..models import ChatHistoryMessage, ChatMessageRequest, MCPServerConfig, ToolOptions
|
|
32
36
|
from ..utils import Constants
|
|
33
|
-
from ..utils.utility import
|
|
37
|
+
from ..utils.utility import (
|
|
38
|
+
get_tooling_gateway_for_digital_worker,
|
|
39
|
+
build_mcp_server_url,
|
|
40
|
+
get_chat_history_endpoint,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Runtime Imports
|
|
44
|
+
from microsoft_agents_a365.runtime import OperationError, OperationResult
|
|
45
|
+
from microsoft_agents_a365.runtime.utility import Utility as RuntimeUtility
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ==============================================================================
|
|
49
|
+
# CONSTANTS
|
|
50
|
+
# ==============================================================================
|
|
51
|
+
|
|
52
|
+
# HTTP timeout in seconds for request operations
|
|
53
|
+
DEFAULT_REQUEST_TIMEOUT_SECONDS = 30
|
|
54
|
+
|
|
55
|
+
# HTTP status code for successful response
|
|
56
|
+
HTTP_STATUS_OK = 200
|
|
34
57
|
|
|
35
58
|
|
|
36
59
|
# ==============================================================================
|
|
@@ -66,7 +89,7 @@ class McpToolServerConfigurationService:
|
|
|
66
89
|
# --------------------------------------------------------------------------
|
|
67
90
|
|
|
68
91
|
async def list_tool_servers(
|
|
69
|
-
self, agentic_app_id: str, auth_token: str
|
|
92
|
+
self, agentic_app_id: str, auth_token: str, options: Optional[ToolOptions] = None
|
|
70
93
|
) -> List[MCPServerConfig]:
|
|
71
94
|
"""
|
|
72
95
|
Gets the list of MCP Servers that are configured for the agent.
|
|
@@ -74,6 +97,7 @@ class McpToolServerConfigurationService:
|
|
|
74
97
|
Args:
|
|
75
98
|
agentic_app_id: Agentic App ID for the agent.
|
|
76
99
|
auth_token: Authentication token to access the MCP servers.
|
|
100
|
+
options: Optional ToolOptions instance containing optional parameters.
|
|
77
101
|
|
|
78
102
|
Returns:
|
|
79
103
|
List[MCPServerConfig]: Returns the list of MCP Servers that are configured.
|
|
@@ -85,13 +109,17 @@ class McpToolServerConfigurationService:
|
|
|
85
109
|
# Validate input parameters
|
|
86
110
|
self._validate_input_parameters(agentic_app_id, auth_token)
|
|
87
111
|
|
|
112
|
+
# Use default options if none provided
|
|
113
|
+
if options is None:
|
|
114
|
+
options = ToolOptions(orchestrator_name=None)
|
|
115
|
+
|
|
88
116
|
self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}")
|
|
89
117
|
|
|
90
118
|
# Determine configuration source based on environment
|
|
91
119
|
if self._is_development_scenario():
|
|
92
120
|
return self._load_servers_from_manifest()
|
|
93
121
|
else:
|
|
94
|
-
return await self._load_servers_from_gateway(agentic_app_id, auth_token)
|
|
122
|
+
return await self._load_servers_from_gateway(agentic_app_id, auth_token, options)
|
|
95
123
|
|
|
96
124
|
# --------------------------------------------------------------------------
|
|
97
125
|
# ENVIRONMENT DETECTION
|
|
@@ -275,7 +303,7 @@ class McpToolServerConfigurationService:
|
|
|
275
303
|
# --------------------------------------------------------------------------
|
|
276
304
|
|
|
277
305
|
async def _load_servers_from_gateway(
|
|
278
|
-
self, agentic_app_id: str, auth_token: str
|
|
306
|
+
self, agentic_app_id: str, auth_token: str, options: ToolOptions
|
|
279
307
|
) -> List[MCPServerConfig]:
|
|
280
308
|
"""
|
|
281
309
|
Reads MCP server configurations from tooling gateway endpoint for production scenario.
|
|
@@ -283,6 +311,7 @@ class McpToolServerConfigurationService:
|
|
|
283
311
|
Args:
|
|
284
312
|
agentic_app_id: Agentic App ID for the agent.
|
|
285
313
|
auth_token: Authentication token to access the tooling gateway.
|
|
314
|
+
options: ToolOptions instance containing optional parameters.
|
|
286
315
|
|
|
287
316
|
Returns:
|
|
288
317
|
List[MCPServerConfig]: List of MCP server configurations from tooling gateway.
|
|
@@ -294,7 +323,7 @@ class McpToolServerConfigurationService:
|
|
|
294
323
|
|
|
295
324
|
try:
|
|
296
325
|
config_endpoint = get_tooling_gateway_for_digital_worker(agentic_app_id)
|
|
297
|
-
headers = self._prepare_gateway_headers(auth_token)
|
|
326
|
+
headers = self._prepare_gateway_headers(auth_token, options)
|
|
298
327
|
|
|
299
328
|
self._logger.info(f"Calling tooling gateway endpoint: {config_endpoint}")
|
|
300
329
|
|
|
@@ -323,18 +352,22 @@ class McpToolServerConfigurationService:
|
|
|
323
352
|
|
|
324
353
|
return mcp_servers
|
|
325
354
|
|
|
326
|
-
def _prepare_gateway_headers(self, auth_token: str) -> Dict[str, str]:
|
|
355
|
+
def _prepare_gateway_headers(self, auth_token: str, options: ToolOptions) -> Dict[str, str]:
|
|
327
356
|
"""
|
|
328
357
|
Prepares headers for tooling gateway requests.
|
|
329
358
|
|
|
330
359
|
Args:
|
|
331
360
|
auth_token: Authentication token.
|
|
361
|
+
options: ToolOptions instance containing optional parameters.
|
|
332
362
|
|
|
333
363
|
Returns:
|
|
334
364
|
Dictionary of HTTP headers.
|
|
335
365
|
"""
|
|
336
366
|
return {
|
|
337
|
-
|
|
367
|
+
Constants.Headers.AUTHORIZATION: f"{Constants.Headers.BEARER_PREFIX} {auth_token}",
|
|
368
|
+
Constants.Headers.USER_AGENT: RuntimeUtility.get_user_agent_header(
|
|
369
|
+
options.orchestrator_name
|
|
370
|
+
),
|
|
338
371
|
}
|
|
339
372
|
|
|
340
373
|
async def _parse_gateway_response(
|
|
@@ -379,16 +412,26 @@ class McpToolServerConfigurationService:
|
|
|
379
412
|
MCPServerConfig object or None if parsing fails.
|
|
380
413
|
"""
|
|
381
414
|
try:
|
|
382
|
-
|
|
383
|
-
|
|
415
|
+
mcp_server_name = self._extract_server_name(server_element)
|
|
416
|
+
mcp_server_unique_name = self._extract_server_unique_name(server_element)
|
|
384
417
|
|
|
385
|
-
if not self._validate_server_strings(
|
|
418
|
+
if not self._validate_server_strings(mcp_server_name, mcp_server_unique_name):
|
|
386
419
|
return None
|
|
387
420
|
|
|
388
|
-
#
|
|
389
|
-
|
|
421
|
+
# Check if a URL is provided
|
|
422
|
+
endpoint = self._extract_server_url(server_element)
|
|
390
423
|
|
|
391
|
-
|
|
424
|
+
# Use mcp_server_name if available, otherwise fall back to mcp_server_unique_name for URL construction
|
|
425
|
+
server_name = mcp_server_name or mcp_server_unique_name
|
|
426
|
+
|
|
427
|
+
# Determine the final URL: use custom URL if provided, otherwise construct it
|
|
428
|
+
final_url = endpoint if endpoint else build_mcp_server_url(server_name)
|
|
429
|
+
|
|
430
|
+
return MCPServerConfig(
|
|
431
|
+
mcp_server_name=mcp_server_name,
|
|
432
|
+
mcp_server_unique_name=mcp_server_unique_name,
|
|
433
|
+
url=final_url,
|
|
434
|
+
)
|
|
392
435
|
|
|
393
436
|
except Exception:
|
|
394
437
|
return None
|
|
@@ -406,13 +449,26 @@ class McpToolServerConfigurationService:
|
|
|
406
449
|
MCPServerConfig object or None if parsing fails.
|
|
407
450
|
"""
|
|
408
451
|
try:
|
|
409
|
-
|
|
410
|
-
|
|
452
|
+
mcp_server_name = self._extract_server_name(server_element)
|
|
453
|
+
mcp_server_unique_name = self._extract_server_unique_name(server_element)
|
|
411
454
|
|
|
412
|
-
if not self._validate_server_strings(
|
|
455
|
+
if not self._validate_server_strings(mcp_server_name, mcp_server_unique_name):
|
|
413
456
|
return None
|
|
414
457
|
|
|
415
|
-
|
|
458
|
+
# Check if a URL is provided by the gateway
|
|
459
|
+
endpoint = self._extract_server_url(server_element)
|
|
460
|
+
|
|
461
|
+
# Use mcp_server_name if available, otherwise fall back to mcp_server_unique_name for URL construction
|
|
462
|
+
server_name = mcp_server_name or mcp_server_unique_name
|
|
463
|
+
|
|
464
|
+
# Determine the final URL: use custom URL if provided, otherwise construct it
|
|
465
|
+
final_url = endpoint if endpoint else build_mcp_server_url(server_name)
|
|
466
|
+
|
|
467
|
+
return MCPServerConfig(
|
|
468
|
+
mcp_server_name=mcp_server_name,
|
|
469
|
+
mcp_server_unique_name=mcp_server_unique_name,
|
|
470
|
+
url=final_url,
|
|
471
|
+
)
|
|
416
472
|
|
|
417
473
|
except Exception:
|
|
418
474
|
return None
|
|
@@ -467,6 +523,21 @@ class McpToolServerConfigurationService:
|
|
|
467
523
|
return server_element["mcpServerUniqueName"]
|
|
468
524
|
return None
|
|
469
525
|
|
|
526
|
+
def _extract_server_url(self, server_element: Dict[str, Any]) -> Optional[str]:
|
|
527
|
+
"""
|
|
528
|
+
Extracts custom server URL from configuration element.
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
server_element: Configuration dictionary.
|
|
532
|
+
|
|
533
|
+
Returns:
|
|
534
|
+
Server URL string or None.
|
|
535
|
+
"""
|
|
536
|
+
# Check for 'url' field in both manifest and gateway responses
|
|
537
|
+
if "url" in server_element and isinstance(server_element["url"], str):
|
|
538
|
+
return server_element["url"]
|
|
539
|
+
return None
|
|
540
|
+
|
|
470
541
|
def _validate_server_strings(self, name: Optional[str], unique_name: Optional[str]) -> bool:
|
|
471
542
|
"""
|
|
472
543
|
Validates that server name and unique name are valid strings.
|
|
@@ -479,3 +550,145 @@ class McpToolServerConfigurationService:
|
|
|
479
550
|
True if both strings are valid, False otherwise.
|
|
480
551
|
"""
|
|
481
552
|
return name is not None and name.strip() and unique_name is not None and unique_name.strip()
|
|
553
|
+
|
|
554
|
+
# --------------------------------------------------------------------------
|
|
555
|
+
# SEND CHAT HISTORY
|
|
556
|
+
# --------------------------------------------------------------------------
|
|
557
|
+
|
|
558
|
+
async def send_chat_history(
|
|
559
|
+
self,
|
|
560
|
+
turn_context: TurnContext,
|
|
561
|
+
chat_history_messages: List[ChatHistoryMessage],
|
|
562
|
+
options: Optional[ToolOptions] = None,
|
|
563
|
+
) -> OperationResult:
|
|
564
|
+
"""
|
|
565
|
+
Sends chat history to the MCP platform for real-time threat protection.
|
|
566
|
+
|
|
567
|
+
Args:
|
|
568
|
+
turn_context: TurnContext from the Agents SDK containing conversation information.
|
|
569
|
+
Must have a valid activity with conversation.id, activity.id, and
|
|
570
|
+
activity.text.
|
|
571
|
+
chat_history_messages: List of ChatHistoryMessage objects representing the chat
|
|
572
|
+
history. Must be non-empty.
|
|
573
|
+
options: Optional ToolOptions instance containing optional parameters.
|
|
574
|
+
|
|
575
|
+
Returns:
|
|
576
|
+
OperationResult: An OperationResult indicating success or failure.
|
|
577
|
+
On success, returns OperationResult.success().
|
|
578
|
+
On failure, returns OperationResult.failed() with error details.
|
|
579
|
+
|
|
580
|
+
Raises:
|
|
581
|
+
ValueError: If turn_context is None, chat_history_messages is None or empty,
|
|
582
|
+
turn_context.activity is None, or any of the required fields
|
|
583
|
+
(conversation.id, activity.id, activity.text) are missing or empty.
|
|
584
|
+
|
|
585
|
+
Example:
|
|
586
|
+
>>> from datetime import datetime, timezone
|
|
587
|
+
>>> from microsoft_agents_a365.tooling.models import ChatHistoryMessage
|
|
588
|
+
>>>
|
|
589
|
+
>>> history = [
|
|
590
|
+
... ChatHistoryMessage("msg-1", "user", "Hello", datetime.now(timezone.utc)),
|
|
591
|
+
... ChatHistoryMessage("msg-2", "assistant", "Hi!", datetime.now(timezone.utc))
|
|
592
|
+
... ]
|
|
593
|
+
>>>
|
|
594
|
+
>>> service = McpToolServerConfigurationService()
|
|
595
|
+
>>> result = await service.send_chat_history(turn_context, history)
|
|
596
|
+
>>> if result.succeeded:
|
|
597
|
+
... print("Chat history sent successfully")
|
|
598
|
+
"""
|
|
599
|
+
# Validate input parameters
|
|
600
|
+
if turn_context is None:
|
|
601
|
+
raise ValueError("turn_context cannot be None")
|
|
602
|
+
if chat_history_messages is None or len(chat_history_messages) == 0:
|
|
603
|
+
raise ValueError("chat_history_messages cannot be None or empty")
|
|
604
|
+
|
|
605
|
+
# Extract required information from turn context
|
|
606
|
+
if not turn_context.activity:
|
|
607
|
+
raise ValueError("turn_context.activity cannot be None")
|
|
608
|
+
|
|
609
|
+
conversation_id: Optional[str] = (
|
|
610
|
+
turn_context.activity.conversation.id if turn_context.activity.conversation else None
|
|
611
|
+
)
|
|
612
|
+
message_id: Optional[str] = turn_context.activity.id
|
|
613
|
+
user_message: Optional[str] = turn_context.activity.text
|
|
614
|
+
|
|
615
|
+
if conversation_id is None or (
|
|
616
|
+
isinstance(conversation_id, str) and not conversation_id.strip()
|
|
617
|
+
):
|
|
618
|
+
raise ValueError(
|
|
619
|
+
"conversation_id cannot be empty or None (from turn_context.activity.conversation.id)"
|
|
620
|
+
)
|
|
621
|
+
if message_id is None or (isinstance(message_id, str) and not message_id.strip()):
|
|
622
|
+
raise ValueError("message_id cannot be empty or None (from turn_context.activity.id)")
|
|
623
|
+
if user_message is None or (isinstance(user_message, str) and not user_message.strip()):
|
|
624
|
+
raise ValueError(
|
|
625
|
+
"user_message cannot be empty or None (from turn_context.activity.text)"
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
# Use default options if none provided
|
|
629
|
+
if options is None:
|
|
630
|
+
options = ToolOptions(orchestrator_name=None)
|
|
631
|
+
|
|
632
|
+
# Get the endpoint URL
|
|
633
|
+
endpoint = get_chat_history_endpoint()
|
|
634
|
+
|
|
635
|
+
# Log only the URL path to avoid accidentally exposing sensitive data in query strings
|
|
636
|
+
parsed_url = urlparse(endpoint)
|
|
637
|
+
self._logger.debug(f"Sending chat history to endpoint path: {parsed_url.path}")
|
|
638
|
+
|
|
639
|
+
# Create the request payload
|
|
640
|
+
request = ChatMessageRequest(
|
|
641
|
+
conversation_id=conversation_id,
|
|
642
|
+
message_id=message_id,
|
|
643
|
+
user_message=user_message,
|
|
644
|
+
chat_history=chat_history_messages,
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
try:
|
|
648
|
+
# Prepare headers (no authentication required)
|
|
649
|
+
headers = {
|
|
650
|
+
Constants.Headers.USER_AGENT: RuntimeUtility.get_user_agent_header(
|
|
651
|
+
options.orchestrator_name
|
|
652
|
+
),
|
|
653
|
+
"Content-Type": "application/json",
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
# Convert request to JSON (using Pydantic's model_dump with aliases for camelCase)
|
|
657
|
+
json_data = json.dumps(request.model_dump(by_alias=True, mode="json"))
|
|
658
|
+
|
|
659
|
+
# Send POST request with timeout to prevent indefinite hangs
|
|
660
|
+
timeout = aiohttp.ClientTimeout(total=DEFAULT_REQUEST_TIMEOUT_SECONDS)
|
|
661
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
662
|
+
async with session.post(endpoint, headers=headers, data=json_data) as response:
|
|
663
|
+
if response.status == HTTP_STATUS_OK:
|
|
664
|
+
self._logger.info("Successfully sent chat history to MCP platform")
|
|
665
|
+
return OperationResult.success()
|
|
666
|
+
else:
|
|
667
|
+
error_text = await response.text()
|
|
668
|
+
self._logger.error(
|
|
669
|
+
f"HTTP error sending chat history: HTTP {response.status}. "
|
|
670
|
+
f"Response: {error_text[:500]}"
|
|
671
|
+
)
|
|
672
|
+
# Use ClientResponseError for consistent error handling
|
|
673
|
+
http_error = aiohttp.ClientResponseError(
|
|
674
|
+
request_info=response.request_info,
|
|
675
|
+
history=response.history,
|
|
676
|
+
status=response.status,
|
|
677
|
+
message=error_text,
|
|
678
|
+
headers=response.headers,
|
|
679
|
+
)
|
|
680
|
+
return OperationResult.failed(OperationError(http_error))
|
|
681
|
+
|
|
682
|
+
except asyncio.TimeoutError as timeout_ex:
|
|
683
|
+
# Catch TimeoutError before ClientError since aiohttp.ServerTimeoutError
|
|
684
|
+
# inherits from both asyncio.TimeoutError and aiohttp.ClientError
|
|
685
|
+
self._logger.error(
|
|
686
|
+
f"Request timeout sending chat history to '{endpoint}': {str(timeout_ex)}"
|
|
687
|
+
)
|
|
688
|
+
return OperationResult.failed(OperationError(timeout_ex))
|
|
689
|
+
except aiohttp.ClientError as http_ex:
|
|
690
|
+
self._logger.error(f"HTTP error sending chat history to '{endpoint}': {str(http_ex)}")
|
|
691
|
+
return OperationResult.failed(OperationError(http_ex))
|
|
692
|
+
except Exception as ex:
|
|
693
|
+
self._logger.error(f"Failed to send chat history to '{endpoint}': {str(ex)}")
|
|
694
|
+
return OperationResult.failed(OperationError(ex))
|
|
@@ -10,7 +10,6 @@ from .utility import (
|
|
|
10
10
|
get_tooling_gateway_for_digital_worker,
|
|
11
11
|
get_mcp_base_url,
|
|
12
12
|
build_mcp_server_url,
|
|
13
|
-
get_tools_mode,
|
|
14
13
|
get_mcp_platform_authentication_scope,
|
|
15
14
|
)
|
|
16
15
|
|
|
@@ -19,6 +18,5 @@ __all__ = [
|
|
|
19
18
|
"get_tooling_gateway_for_digital_worker",
|
|
20
19
|
"get_mcp_base_url",
|
|
21
20
|
"build_mcp_server_url",
|
|
22
|
-
"get_tools_mode",
|
|
23
21
|
"get_mcp_platform_authentication_scope",
|
|
24
22
|
]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) Microsoft
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
2
3
|
|
|
3
4
|
"""
|
|
4
5
|
Provides constant values used throughout the Tooling components.
|
|
@@ -20,3 +21,6 @@ class Constants:
|
|
|
20
21
|
|
|
21
22
|
#: The prefix used for Bearer authentication tokens in HTTP headers.
|
|
22
23
|
BEARER_PREFIX = "Bearer"
|
|
24
|
+
|
|
25
|
+
#: The header name for User-Agent information.
|
|
26
|
+
USER_AGENT = "User-Agent"
|
|
@@ -1,23 +1,19 @@
|
|
|
1
|
-
# Copyright (c) Microsoft
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
2
3
|
|
|
3
4
|
"""
|
|
4
5
|
Provides utility functions for the Tooling components.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
import os
|
|
8
|
-
from enum import Enum
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class ToolsMode(Enum):
|
|
12
|
-
"""Enumeration for different tools modes."""
|
|
13
|
-
|
|
14
|
-
MOCK_MCP_SERVER = "MockMCPServer"
|
|
15
|
-
MCP_PLATFORM = "MCPPlatform"
|
|
16
9
|
|
|
17
10
|
|
|
18
11
|
# Constants for base URLs
|
|
19
12
|
MCP_PLATFORM_PROD_BASE_URL = "https://agent365.svc.cloud.microsoft"
|
|
20
13
|
|
|
14
|
+
# API endpoint paths
|
|
15
|
+
CHAT_HISTORY_ENDPOINT_PATH = "/agents/real-time-threat-protection/chat-message"
|
|
16
|
+
|
|
21
17
|
PPAPI_TOKEN_SCOPE = "https://api.powerplatform.com"
|
|
22
18
|
PROD_MCP_PLATFORM_AUTHENTICATION_SCOPE = "ea9ffc3e-8a23-4a7d-836d-234d7c7565c1/.default"
|
|
23
19
|
|
|
@@ -43,13 +39,6 @@ def get_mcp_base_url() -> str:
|
|
|
43
39
|
Returns:
|
|
44
40
|
str: The base URL for MCP servers.
|
|
45
41
|
"""
|
|
46
|
-
environment = _get_current_environment().lower()
|
|
47
|
-
|
|
48
|
-
if environment == "development":
|
|
49
|
-
tools_mode = get_tools_mode()
|
|
50
|
-
if tools_mode == ToolsMode.MOCK_MCP_SERVER:
|
|
51
|
-
return os.getenv("MOCK_MCP_SERVER_URL", "http://localhost:5309/mcp-mock/agents/servers")
|
|
52
|
-
|
|
53
42
|
return f"{_get_mcp_platform_base_url()}/agents/servers"
|
|
54
43
|
|
|
55
44
|
|
|
@@ -85,39 +74,33 @@ def _get_mcp_platform_base_url() -> str:
|
|
|
85
74
|
Returns:
|
|
86
75
|
str: The base URL for MCP platform.
|
|
87
76
|
"""
|
|
88
|
-
|
|
89
|
-
|
|
77
|
+
endpoint = os.getenv("MCP_PLATFORM_ENDPOINT")
|
|
78
|
+
if endpoint is not None:
|
|
79
|
+
return endpoint
|
|
90
80
|
|
|
91
81
|
return MCP_PLATFORM_PROD_BASE_URL
|
|
92
82
|
|
|
93
83
|
|
|
94
|
-
def
|
|
84
|
+
def get_mcp_platform_authentication_scope() -> list[str]:
|
|
95
85
|
"""
|
|
96
|
-
Gets the
|
|
86
|
+
Gets the MCP platform authentication scope.
|
|
97
87
|
|
|
98
88
|
Returns:
|
|
99
|
-
|
|
89
|
+
list[str]: A list containing the appropriate MCP platform authentication scope.
|
|
100
90
|
"""
|
|
101
|
-
|
|
91
|
+
env_scope = os.getenv("MCP_PLATFORM_AUTHENTICATION_SCOPE", "")
|
|
102
92
|
|
|
103
|
-
if
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
|
|
93
|
+
if env_scope:
|
|
94
|
+
return [env_scope]
|
|
95
|
+
|
|
96
|
+
return [PROD_MCP_PLATFORM_AUTHENTICATION_SCOPE]
|
|
107
97
|
|
|
108
98
|
|
|
109
|
-
def
|
|
99
|
+
def get_chat_history_endpoint() -> str:
|
|
110
100
|
"""
|
|
111
|
-
Gets the
|
|
101
|
+
Gets the chat history endpoint URL for sending chat history to the MCP platform.
|
|
112
102
|
|
|
113
103
|
Returns:
|
|
114
|
-
|
|
104
|
+
str: The chat history endpoint URL.
|
|
115
105
|
"""
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
envScope = os.getenv("MCP_PLATFORM_AUTHENTICATION_SCOPE", "")
|
|
119
|
-
|
|
120
|
-
if envScope:
|
|
121
|
-
return [envScope]
|
|
122
|
-
|
|
123
|
-
return [PROD_MCP_PLATFORM_AUTHENTICATION_SCOPE]
|
|
106
|
+
return f"{_get_mcp_platform_base_url()}{CHAT_HISTORY_ENDPOINT_PATH}"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microsoft-agents-a365-tooling
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1.dev0
|
|
4
4
|
Summary: Agent 365 Tooling SDK, providing functionality to work with Agent 365 Tools.
|
|
5
5
|
Author-email: Microsoft <support@microsoft.com>
|
|
6
6
|
License: MIT
|
|
@@ -18,14 +18,17 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
18
18
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
19
|
Requires-Python: >=3.11
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
21
22
|
Requires-Dist: pydantic>=2.0.0
|
|
22
23
|
Requires-Dist: typing-extensions>=4.0.0
|
|
24
|
+
Requires-Dist: microsoft-agents-hosting-core<0.6.0,>=0.4.0
|
|
23
25
|
Provides-Extra: dev
|
|
24
26
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
25
27
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
26
28
|
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
27
29
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
28
30
|
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
29
32
|
|
|
30
33
|
# microsoft-agents-a365-tooling
|
|
31
34
|
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
README.md
|
|
2
2
|
pyproject.toml
|
|
3
3
|
setup.py
|
|
4
|
+
../../LICENSE
|
|
5
|
+
docs/../../LICENSE
|
|
6
|
+
microsoft_agents_a365/../../LICENSE
|
|
4
7
|
microsoft_agents_a365/tooling/__init__.py
|
|
8
|
+
microsoft_agents_a365/tooling/py.typed
|
|
9
|
+
microsoft_agents_a365/tooling/extensions/__init__.py
|
|
5
10
|
microsoft_agents_a365/tooling/models/__init__.py
|
|
11
|
+
microsoft_agents_a365/tooling/models/chat_history_message.py
|
|
12
|
+
microsoft_agents_a365/tooling/models/chat_message_request.py
|
|
6
13
|
microsoft_agents_a365/tooling/models/mcp_server_config.py
|
|
14
|
+
microsoft_agents_a365/tooling/models/tool_options.py
|
|
7
15
|
microsoft_agents_a365/tooling/services/__init__.py
|
|
8
16
|
microsoft_agents_a365/tooling/services/mcp_tool_server_configuration_service.py
|
|
9
17
|
microsoft_agents_a365/tooling/utils/__init__.py
|
{microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/pyproject.toml
RENAMED
|
@@ -25,6 +25,7 @@ license = {text = "MIT"}
|
|
|
25
25
|
dependencies = [
|
|
26
26
|
"pydantic >= 2.0.0",
|
|
27
27
|
"typing-extensions >= 4.0.0",
|
|
28
|
+
"microsoft-agents-hosting-core >= 0.4.0, < 0.6.0",
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
[project.urls]
|
|
@@ -51,6 +52,7 @@ include-package-data = true
|
|
|
51
52
|
|
|
52
53
|
[tool.setuptools.package-data]
|
|
53
54
|
"*" = ["../../LICENSE"]
|
|
55
|
+
"microsoft_agents_a365.tooling" = ["py.typed"]
|
|
54
56
|
|
|
55
57
|
[tool.black]
|
|
56
58
|
line-length = 100
|
|
@@ -60,6 +62,10 @@ target-version = ['py311']
|
|
|
60
62
|
line-length = 100
|
|
61
63
|
target-version = "py311"
|
|
62
64
|
|
|
65
|
+
[tool.ruff.lint.flake8-copyright]
|
|
66
|
+
notice-rgx = "# Copyright \\(c\\) Microsoft Corporation\\.\\r?\\n# Licensed under the MIT License\\."
|
|
67
|
+
min-file-size = 1
|
|
68
|
+
|
|
63
69
|
[tool.mypy]
|
|
64
70
|
python_version = "3.11"
|
|
65
71
|
strict = true
|
{microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/setup.py
RENAMED
|
@@ -13,7 +13,7 @@ package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0")
|
|
|
13
13
|
helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper"
|
|
14
14
|
sys.path.insert(0, str(helper_path))
|
|
15
15
|
|
|
16
|
-
from setup_utils import get_dynamic_dependencies
|
|
16
|
+
from setup_utils import get_dynamic_dependencies # noqa: E402
|
|
17
17
|
|
|
18
18
|
# Use minimum version strategy:
|
|
19
19
|
# - Internal packages get: >= current_base_version (e.g., >= 0.1.0)
|
{microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microsoft_agents_a365_tooling-0.2.0.dev5 → microsoft_agents_a365_tooling-0.2.1.dev0}/setup.cfg
RENAMED
|
File without changes
|