datarobot-genai 0.2.30__tar.gz → 0.2.32__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.
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/PKG-INFO +1 -1
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/pyproject.toml +1 -1
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/__init__.py +2 -2
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/exceptions.py +0 -4
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/logging.py +2 -2
- datarobot_genai-0.2.32/src/datarobot_genai/drmcp/test_utils/clients/anthropic.py +68 -0
- datarobot_genai-0.2.30/src/datarobot_genai/drmcp/test_utils/openai_llm_mcp_client.py → datarobot_genai-0.2.32/src/datarobot_genai/drmcp/test_utils/clients/base.py +38 -40
- datarobot_genai-0.2.32/src/datarobot_genai/drmcp/test_utils/clients/dr_gateway.py +58 -0
- datarobot_genai-0.2.32/src/datarobot_genai/drmcp/test_utils/clients/openai.py +68 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/test_utils/test_interactive.py +16 -16
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/test_utils/tool_base_ete.py +1 -1
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/test_utils/utils.py +1 -1
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/clients/gdrive.py +91 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/gdrive/tools.py +100 -1
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/predictive/data.py +5 -5
- datarobot_genai-0.2.32/src/datarobot_genai/drmcp/tools/predictive/model.py +183 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/predictive/project.py +2 -2
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/predictive/training.py +14 -14
- datarobot_genai-0.2.32/src/datarobot_genai/py.typed +0 -0
- datarobot_genai-0.2.30/src/datarobot_genai/drmcp/tools/predictive/model.py +0 -148
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/.gitignore +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/AUTHORS +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/LICENSE +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/README.md +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/agents/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/agents/base.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/chat/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/chat/auth.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/chat/client.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/chat/responses.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/cli/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/cli/agent_environment.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/cli/agent_kernel.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/custom_model.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/mcp/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/mcp/common.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/telemetry_agent.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/utils/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/utils/auth.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/core/utils/urls.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/crewai/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/crewai/agent.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/crewai/base.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/crewai/events.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/crewai/mcp.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/auth.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/clients.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/config.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/config_utils.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/constants.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/credentials.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dr_mcp_server.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dr_mcp_server_logo.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_prompts/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_prompts/controllers.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_prompts/dr_lib.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_prompts/register.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_prompts/utils.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/adapters/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/adapters/base.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/adapters/default.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/adapters/drum.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/config.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/controllers.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/metadata.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/register.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/schemas/drum_agentic_fallback_schema.json +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/deployment/schemas/drum_prediction_fallback_schema.json +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/register.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/dynamic_tools/schema.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/mcp_instance.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/memory_management/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/memory_management/manager.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/memory_management/memory_tools.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/routes.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/routes_utils.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/server_life_cycle.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/telemetry.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/tool_config.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/tool_filter.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/core/utils.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/server.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/test_utils/__init__.py +0 -0
- {datarobot_genai-0.2.30/src/datarobot_genai/drmcp/tools/gdrive → datarobot_genai-0.2.32/src/datarobot_genai/drmcp/test_utils/clients}/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/test_utils/elicitation_test_tool.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/test_utils/integration_mcp_server.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/test_utils/mcp_utils_ete.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/test_utils/mcp_utils_integration.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/clients/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/clients/atlassian.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/clients/confluence.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/clients/jira.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/clients/microsoft_graph.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/clients/s3.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/confluence/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/confluence/tools.py +0 -0
- {datarobot_genai-0.2.30/src/datarobot_genai/langgraph → datarobot_genai-0.2.32/src/datarobot_genai/drmcp/tools/gdrive}/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/jira/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/jira/tools.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/microsoft_graph/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/microsoft_graph/tools.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/predictive/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/predictive/deployment.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/predictive/deployment_info.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/predictive/predict.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/predictive/predict_realtime.py +0 -0
- {datarobot_genai-0.2.30/src/datarobot_genai/nat → datarobot_genai-0.2.32/src/datarobot_genai/langgraph}/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/langgraph/agent.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/langgraph/mcp.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/llama_index/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/llama_index/agent.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/llama_index/base.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/llama_index/mcp.py +0 -0
- /datarobot_genai-0.2.30/src/datarobot_genai/py.typed → /datarobot_genai-0.2.32/src/datarobot_genai/nat/__init__.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/nat/agent.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/nat/datarobot_auth_provider.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/nat/datarobot_llm_clients.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/nat/datarobot_llm_providers.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/nat/datarobot_mcp_client.py +0 -0
- {datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/nat/helpers.py +0 -0
|
@@ -19,11 +19,11 @@ A reusable library for building Model Context Protocol (MCP) servers with DataRo
|
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
# Export main server components
|
|
22
|
+
from datarobot_genai.drmcp.test_utils.clients.openai import OpenAILLMMCPClient
|
|
22
23
|
from datarobot_genai.drmcp.test_utils.mcp_utils_ete import ete_test_mcp_session
|
|
23
24
|
from datarobot_genai.drmcp.test_utils.mcp_utils_ete import get_dr_mcp_server_url
|
|
24
25
|
from datarobot_genai.drmcp.test_utils.mcp_utils_ete import get_headers
|
|
25
26
|
from datarobot_genai.drmcp.test_utils.mcp_utils_integration import integration_test_mcp_session
|
|
26
|
-
from datarobot_genai.drmcp.test_utils.openai_llm_mcp_client import LLMMCPClient
|
|
27
27
|
from datarobot_genai.drmcp.test_utils.tool_base_ete import ETETestExpectations
|
|
28
28
|
from datarobot_genai.drmcp.test_utils.tool_base_ete import ToolBaseE2E
|
|
29
29
|
from datarobot_genai.drmcp.test_utils.tool_base_ete import ToolCallTestExpectations
|
|
@@ -70,7 +70,7 @@ __all__ = [
|
|
|
70
70
|
"get_dr_mcp_server_url",
|
|
71
71
|
"get_headers",
|
|
72
72
|
"ete_test_mcp_session",
|
|
73
|
-
"
|
|
73
|
+
"OpenAILLMMCPClient",
|
|
74
74
|
"ETETestExpectations",
|
|
75
75
|
"ToolBaseE2E",
|
|
76
76
|
"ToolCallTestExpectations",
|
|
@@ -20,7 +20,7 @@ from collections.abc import Callable
|
|
|
20
20
|
from typing import Any
|
|
21
21
|
from typing import TypeVar
|
|
22
22
|
|
|
23
|
-
from .exceptions import
|
|
23
|
+
from fastmcp.exceptions import ToolError
|
|
24
24
|
|
|
25
25
|
# Secret patterns to redact from logs
|
|
26
26
|
SECRET_PATTERNS = [
|
|
@@ -93,6 +93,6 @@ def log_execution(func: F) -> F:
|
|
|
93
93
|
return result
|
|
94
94
|
except Exception as e:
|
|
95
95
|
error_msg = _log_error(logger, func.__name__, e, args=args, kwargs=kwargs)
|
|
96
|
-
raise
|
|
96
|
+
raise ToolError(error_msg)
|
|
97
97
|
|
|
98
98
|
return wrapper # type: ignore[return-value]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Copyright 2026 DataRobot, Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Anthropic LLM MCP Client implementation (example).
|
|
16
|
+
|
|
17
|
+
This is an example implementation showing how easy it is to add a new LLM provider.
|
|
18
|
+
Anthropic's API is OpenAI-compatible, so we can use the OpenAI SDK with their endpoint.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import openai
|
|
22
|
+
|
|
23
|
+
from .base import BaseLLMMCPClient
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AnthropicMCPClient(BaseLLMMCPClient):
|
|
27
|
+
"""
|
|
28
|
+
Client for interacting with LLMs via MCP using Anthropic Claude.
|
|
29
|
+
|
|
30
|
+
Note: Elicitation is handled at the protocol level by FastMCP's ctx.elicit().
|
|
31
|
+
Tools using FastMCP's built-in elicitation will work automatically.
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
```python
|
|
35
|
+
config = {
|
|
36
|
+
"anthropic_api_key": "sk-ant-...",
|
|
37
|
+
"model": "claude-3-5-sonnet-20241022",
|
|
38
|
+
}
|
|
39
|
+
client = AnthropicMCPClient(str(config))
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
config: str | dict,
|
|
46
|
+
):
|
|
47
|
+
"""
|
|
48
|
+
Initialize the LLM MCP client.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
config: Configuration string or dict with:
|
|
52
|
+
- anthropic_api_key: Anthropic API key
|
|
53
|
+
- model: Model name (default: "claude-3-5-sonnet-20241022")
|
|
54
|
+
- save_llm_responses: Whether to save responses (default: True)
|
|
55
|
+
"""
|
|
56
|
+
super().__init__(config)
|
|
57
|
+
|
|
58
|
+
def _create_llm_client(self, config_dict: dict) -> tuple[openai.OpenAI, str]:
|
|
59
|
+
"""Create the LLM client for Anthropic (OpenAI-compatible endpoint)."""
|
|
60
|
+
anthropic_api_key = config_dict.get("anthropic_api_key")
|
|
61
|
+
model = config_dict.get("model", "claude-3-5-sonnet-20241022")
|
|
62
|
+
|
|
63
|
+
# Anthropic provides an OpenAI-compatible endpoint
|
|
64
|
+
client = openai.OpenAI(
|
|
65
|
+
api_key=anthropic_api_key,
|
|
66
|
+
base_url="https://api.anthropic.com/v1",
|
|
67
|
+
)
|
|
68
|
+
return client, model
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2026 DataRobot, Inc.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
"""Base classes for LLM MCP clients."""
|
|
16
|
+
|
|
15
17
|
import json
|
|
18
|
+
from abc import ABC
|
|
19
|
+
from abc import abstractmethod
|
|
16
20
|
from ast import literal_eval
|
|
17
21
|
from typing import Any
|
|
18
22
|
|
|
@@ -23,7 +27,7 @@ from mcp.types import ListToolsResult
|
|
|
23
27
|
from mcp.types import TextContent
|
|
24
28
|
from openai.types.chat.chat_completion import ChatCompletion
|
|
25
29
|
|
|
26
|
-
from .utils import save_response_to_file
|
|
30
|
+
from datarobot_genai.drmcp.test_utils.utils import save_response_to_file
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
class ToolCall:
|
|
@@ -44,9 +48,9 @@ class LLMResponse:
|
|
|
44
48
|
self.tool_results = tool_results
|
|
45
49
|
|
|
46
50
|
|
|
47
|
-
class
|
|
51
|
+
class BaseLLMMCPClient(ABC):
|
|
48
52
|
"""
|
|
49
|
-
|
|
53
|
+
Base class for LLM MCP clients.
|
|
50
54
|
|
|
51
55
|
Note: Elicitation is handled at the protocol level by FastMCP's ctx.elicit().
|
|
52
56
|
Tools using FastMCP's built-in elicitation will work automatically.
|
|
@@ -54,54 +58,48 @@ class LLMMCPClient:
|
|
|
54
58
|
|
|
55
59
|
def __init__(
|
|
56
60
|
self,
|
|
57
|
-
config: str,
|
|
61
|
+
config: str | dict,
|
|
58
62
|
):
|
|
59
63
|
"""
|
|
60
64
|
Initialize the LLM MCP client.
|
|
61
65
|
|
|
62
66
|
Args:
|
|
63
|
-
config: Configuration string or dict with
|
|
64
|
-
- openai_api_key: OpenAI API key
|
|
65
|
-
- openai_api_base: Optional Azure OpenAI endpoint
|
|
66
|
-
- openai_api_deployment_id: Optional Azure deployment ID
|
|
67
|
-
- openai_api_version: Optional Azure API version
|
|
68
|
-
- model: Model name (default: "gpt-3.5-turbo")
|
|
69
|
-
- save_llm_responses: Whether to save responses (default: True)
|
|
67
|
+
config: Configuration string or dict with provider-specific keys.
|
|
70
68
|
"""
|
|
71
|
-
|
|
69
|
+
config_dict = self._parse_config(config)
|
|
70
|
+
self.openai_client, self.model = self._create_llm_client(config_dict)
|
|
71
|
+
self.save_llm_responses = config_dict.get("save_llm_responses", True)
|
|
72
|
+
self.available_tools: list[dict[str, Any]] = []
|
|
73
|
+
self.available_prompts: list[dict[str, Any]] = []
|
|
74
|
+
self.available_resources: list[dict[str, Any]] = []
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def _parse_config(config: str | dict) -> dict:
|
|
78
|
+
"""Parse config string to dict."""
|
|
72
79
|
if isinstance(config, str):
|
|
73
80
|
# Try JSON first (safer), fall back to literal_eval for Python dict strings
|
|
74
81
|
try:
|
|
75
|
-
|
|
82
|
+
return json.loads(config)
|
|
76
83
|
except json.JSONDecodeError:
|
|
77
84
|
# Fall back to literal_eval for Python dict literal strings
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
config_dict = config
|
|
81
|
-
|
|
82
|
-
openai_api_key = config_dict.get("openai_api_key")
|
|
83
|
-
openai_api_base = config_dict.get("openai_api_base")
|
|
84
|
-
openai_api_deployment_id = config_dict.get("openai_api_deployment_id")
|
|
85
|
-
model = config_dict.get("model", "gpt-3.5-turbo")
|
|
86
|
-
save_llm_responses = config_dict.get("save_llm_responses", True)
|
|
87
|
-
|
|
88
|
-
if openai_api_base and openai_api_deployment_id:
|
|
89
|
-
# Azure OpenAI
|
|
90
|
-
self.openai_client = openai.AzureOpenAI(
|
|
91
|
-
api_key=openai_api_key,
|
|
92
|
-
azure_endpoint=openai_api_base,
|
|
93
|
-
api_version=config_dict.get("openai_api_version", "2024-02-15-preview"),
|
|
94
|
-
)
|
|
95
|
-
self.model = openai_api_deployment_id
|
|
96
|
-
else:
|
|
97
|
-
# Regular OpenAI
|
|
98
|
-
self.openai_client = openai.OpenAI(api_key=openai_api_key) # type: ignore[assignment]
|
|
99
|
-
self.model = model
|
|
85
|
+
return literal_eval(config)
|
|
86
|
+
return config
|
|
100
87
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
self
|
|
104
|
-
|
|
88
|
+
@abstractmethod
|
|
89
|
+
def _create_llm_client(
|
|
90
|
+
self, config_dict: dict
|
|
91
|
+
) -> tuple[openai.OpenAI | openai.AzureOpenAI, str]:
|
|
92
|
+
"""
|
|
93
|
+
Create the LLM client.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
config_dict: Parsed configuration dictionary
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
Tuple of (LLM client instance, model name)
|
|
101
|
+
"""
|
|
102
|
+
pass
|
|
105
103
|
|
|
106
104
|
async def _add_mcp_tool_to_available_tools(self, mcp_session: ClientSession) -> None:
|
|
107
105
|
"""Add a tool to the available tools."""
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Copyright 2026 DataRobot, Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""DataRobot LLM Gateway MCP Client implementation."""
|
|
16
|
+
|
|
17
|
+
import openai
|
|
18
|
+
|
|
19
|
+
from .base import BaseLLMMCPClient
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DRLLMGatewayMCPClient(BaseLLMMCPClient):
|
|
23
|
+
"""
|
|
24
|
+
Client for interacting with LLMs via MCP using DataRobot LLM Gateway.
|
|
25
|
+
|
|
26
|
+
Note: Elicitation is handled at the protocol level by FastMCP's ctx.elicit().
|
|
27
|
+
Tools using FastMCP's built-in elicitation will work automatically.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
config: str | dict,
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Initialize the LLM MCP client.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
config: Configuration string or dict with:
|
|
39
|
+
- datarobot_api_token: DataRobot API token
|
|
40
|
+
- datarobot_endpoint: DataRobot endpoint URL (default: "https://app.datarobot.com/api/v2")
|
|
41
|
+
- model: Model name (default: "gpt-4o-mini")
|
|
42
|
+
- save_llm_responses: Whether to save responses (default: True)
|
|
43
|
+
"""
|
|
44
|
+
super().__init__(config)
|
|
45
|
+
|
|
46
|
+
def _create_llm_client(self, config_dict: dict) -> tuple[openai.OpenAI, str]:
|
|
47
|
+
"""Create the LLM client for DataRobot LLM Gateway."""
|
|
48
|
+
datarobot_api_token = config_dict.get("datarobot_api_token")
|
|
49
|
+
datarobot_endpoint = config_dict.get(
|
|
50
|
+
"datarobot_endpoint", "https://app.datarobot.com/api/v2"
|
|
51
|
+
)
|
|
52
|
+
model = config_dict.get("model", "gpt-4o-mini")
|
|
53
|
+
|
|
54
|
+
# Build gateway URL: {endpoint}/genai/llmgw
|
|
55
|
+
gateway_url = datarobot_endpoint.rstrip("/") + "/genai/llmgw"
|
|
56
|
+
|
|
57
|
+
client = openai.OpenAI(api_key=datarobot_api_token, base_url=gateway_url)
|
|
58
|
+
return client, model
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Copyright 2026 DataRobot, Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""OpenAI LLM MCP Client implementation."""
|
|
16
|
+
|
|
17
|
+
import openai
|
|
18
|
+
|
|
19
|
+
from .base import BaseLLMMCPClient
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OpenAILLMMCPClient(BaseLLMMCPClient):
|
|
23
|
+
"""
|
|
24
|
+
Client for interacting with LLMs via MCP using OpenAI or Azure OpenAI.
|
|
25
|
+
|
|
26
|
+
Note: Elicitation is handled at the protocol level by FastMCP's ctx.elicit().
|
|
27
|
+
Tools using FastMCP's built-in elicitation will work automatically.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
config: str | dict,
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Initialize the LLM MCP client.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
config: Configuration string or dict with:
|
|
39
|
+
- openai_api_key: OpenAI API key
|
|
40
|
+
- openai_api_base: Optional Azure OpenAI endpoint
|
|
41
|
+
- openai_api_deployment_id: Optional Azure deployment ID
|
|
42
|
+
- openai_api_version: Optional Azure API version
|
|
43
|
+
- model: Model name (default: "gpt-3.5-turbo")
|
|
44
|
+
- save_llm_responses: Whether to save responses (default: True)
|
|
45
|
+
"""
|
|
46
|
+
super().__init__(config)
|
|
47
|
+
|
|
48
|
+
def _create_llm_client(
|
|
49
|
+
self, config_dict: dict
|
|
50
|
+
) -> tuple[openai.OpenAI | openai.AzureOpenAI, str]:
|
|
51
|
+
"""Create the LLM client for OpenAI or Azure OpenAI."""
|
|
52
|
+
openai_api_key = config_dict.get("openai_api_key")
|
|
53
|
+
openai_api_base = config_dict.get("openai_api_base")
|
|
54
|
+
openai_api_deployment_id = config_dict.get("openai_api_deployment_id")
|
|
55
|
+
model = config_dict.get("model", "gpt-3.5-turbo")
|
|
56
|
+
|
|
57
|
+
if openai_api_base and openai_api_deployment_id:
|
|
58
|
+
# Azure OpenAI
|
|
59
|
+
client = openai.AzureOpenAI(
|
|
60
|
+
api_key=openai_api_key,
|
|
61
|
+
azure_endpoint=openai_api_base,
|
|
62
|
+
api_version=config_dict.get("openai_api_version", "2024-02-15-preview"),
|
|
63
|
+
)
|
|
64
|
+
return client, openai_api_deployment_id
|
|
65
|
+
else:
|
|
66
|
+
# Regular OpenAI
|
|
67
|
+
client = openai.OpenAI(api_key=openai_api_key) # type: ignore[assignment]
|
|
68
|
+
return client, model
|
|
@@ -40,40 +40,40 @@ from mcp.types import ElicitResult
|
|
|
40
40
|
|
|
41
41
|
from datarobot_genai.drmcp import get_dr_mcp_server_url
|
|
42
42
|
from datarobot_genai.drmcp import get_headers
|
|
43
|
-
from datarobot_genai.drmcp.test_utils.
|
|
44
|
-
from datarobot_genai.drmcp.test_utils.
|
|
45
|
-
from datarobot_genai.drmcp.test_utils.
|
|
43
|
+
from datarobot_genai.drmcp.test_utils.clients.base import LLMResponse
|
|
44
|
+
from datarobot_genai.drmcp.test_utils.clients.base import ToolCall
|
|
45
|
+
from datarobot_genai.drmcp.test_utils.clients.dr_gateway import DRLLMGatewayMCPClient
|
|
46
46
|
|
|
47
47
|
# Re-export for backwards compatibility
|
|
48
|
-
__all__ = ["
|
|
48
|
+
__all__ = ["DRLLMGatewayMCPClient", "LLMResponse", "ToolCall", "test_mcp_interactive"]
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
async def test_mcp_interactive() -> None:
|
|
52
52
|
"""Test the MCP server interactively with LLM agent."""
|
|
53
53
|
# Check for required environment variables
|
|
54
|
-
|
|
55
|
-
if not
|
|
56
|
-
print("❌ Error:
|
|
54
|
+
datarobot_api_token = os.environ.get("DATAROBOT_API_TOKEN")
|
|
55
|
+
if not datarobot_api_token:
|
|
56
|
+
print("❌ Error: DATAROBOT_API_TOKEN environment variable is required")
|
|
57
57
|
print("Please set it in your .env file or export it")
|
|
58
58
|
return
|
|
59
59
|
|
|
60
|
-
# Optional
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
openai_api_version = os.environ.get("OPENAI_API_VERSION")
|
|
60
|
+
# Optional DataRobot settings
|
|
61
|
+
datarobot_endpoint = os.environ.get("DATAROBOT_ENDPOINT")
|
|
62
|
+
model = os.environ.get("MODEL")
|
|
64
63
|
|
|
65
64
|
print("🤖 Initializing LLM MCP Client...")
|
|
66
65
|
|
|
67
66
|
# Initialize the LLM client with elicitation handler
|
|
68
67
|
config = {
|
|
69
|
-
"
|
|
70
|
-
"openai_api_base": openai_api_base,
|
|
71
|
-
"openai_api_deployment_id": openai_api_deployment_id,
|
|
72
|
-
"openai_api_version": openai_api_version,
|
|
68
|
+
"datarobot_api_token": datarobot_api_token,
|
|
73
69
|
"save_llm_responses": False,
|
|
74
70
|
}
|
|
71
|
+
if datarobot_endpoint:
|
|
72
|
+
config["datarobot_endpoint"] = datarobot_endpoint
|
|
73
|
+
if model:
|
|
74
|
+
config["model"] = model
|
|
75
75
|
|
|
76
|
-
llm_client =
|
|
76
|
+
llm_client = DRLLMGatewayMCPClient(str(config))
|
|
77
77
|
|
|
78
78
|
# Get MCP server URL
|
|
79
79
|
mcp_server_url = get_dr_mcp_server_url()
|
{datarobot_genai-0.2.30 → datarobot_genai-0.2.32}/src/datarobot_genai/drmcp/tools/clients/gdrive.py
RENAMED
|
@@ -20,6 +20,7 @@ import logging
|
|
|
20
20
|
import uuid
|
|
21
21
|
from typing import Annotated
|
|
22
22
|
from typing import Any
|
|
23
|
+
from typing import Literal
|
|
23
24
|
|
|
24
25
|
import httpx
|
|
25
26
|
from datarobot.auth.datarobot.exceptions import OAuthServiceClientErr
|
|
@@ -821,6 +822,96 @@ class GoogleDriveClient:
|
|
|
821
822
|
headers={"Content-Type": f"multipart/related; boundary={boundary}"},
|
|
822
823
|
)
|
|
823
824
|
|
|
825
|
+
async def manage_access(
|
|
826
|
+
self,
|
|
827
|
+
*,
|
|
828
|
+
file_id: str,
|
|
829
|
+
action: Literal["add", "update", "remove"],
|
|
830
|
+
role: Literal["reader", "commenter", "writer", "fileOrganizer", "organizer", "owner"]
|
|
831
|
+
| None = None,
|
|
832
|
+
email_address: str | None = None,
|
|
833
|
+
permission_id: str | None = None,
|
|
834
|
+
transfer_ownership: bool = False,
|
|
835
|
+
) -> str:
|
|
836
|
+
"""Manage access permissions for a Google Drive file or folder.
|
|
837
|
+
|
|
838
|
+
Adds, updates, or removes sharing permissions on an existing Google Drive
|
|
839
|
+
file or folder using the Google Drive Permissions API.
|
|
840
|
+
|
|
841
|
+
This method supports granting access to users or groups, changing access
|
|
842
|
+
roles, and revoking permissions. Ownership transfer is supported for files
|
|
843
|
+
in "My Drive" when explicitly requested.
|
|
844
|
+
|
|
845
|
+
Args:
|
|
846
|
+
file_id: The ID of the Google Drive file or folder whose permissions
|
|
847
|
+
are being managed.
|
|
848
|
+
action: The permission operation to perform.
|
|
849
|
+
role: The access role to assign or update. Valid values include
|
|
850
|
+
Required for "add" and "update" actions.
|
|
851
|
+
email_address: The email address of the user or group to grant access to.
|
|
852
|
+
Required for the "add" action.
|
|
853
|
+
permission_id: The ID of the permission to update or remove.
|
|
854
|
+
Required for "update" and "remove" actions.
|
|
855
|
+
transfer_ownership: Whether to transfer ownership of the file.
|
|
856
|
+
Only applicable when action="update" and role="owner".
|
|
857
|
+
|
|
858
|
+
Returns
|
|
859
|
+
-------
|
|
860
|
+
Permission id.
|
|
861
|
+
For "add" its newly added permission.
|
|
862
|
+
For "update"/"remove" its previous permission.
|
|
863
|
+
|
|
864
|
+
Raises
|
|
865
|
+
------
|
|
866
|
+
GoogleDriveError: If the permission operation fails (invalid arguments,
|
|
867
|
+
insufficient permissions, resource not found, ownership transfer
|
|
868
|
+
not allowed, rate limited, etc.).
|
|
869
|
+
"""
|
|
870
|
+
if not file_id.strip():
|
|
871
|
+
raise GoogleDriveError("Argument validation error: 'file_id' cannot be empty.")
|
|
872
|
+
|
|
873
|
+
if action == "add" and not email_address:
|
|
874
|
+
raise GoogleDriveError("'email_address' is required for action 'add'.")
|
|
875
|
+
|
|
876
|
+
if action in ("update", "remove") and not permission_id:
|
|
877
|
+
raise GoogleDriveError("'permission_id' is required for action 'update' or 'remove'.")
|
|
878
|
+
|
|
879
|
+
if action != "remove" and not role:
|
|
880
|
+
raise GoogleDriveError("'role' is required for action 'add' or 'update'.")
|
|
881
|
+
|
|
882
|
+
if action == "add":
|
|
883
|
+
response = await self._client.post(
|
|
884
|
+
url=f"/{file_id}/permissions",
|
|
885
|
+
json={
|
|
886
|
+
"type": "user",
|
|
887
|
+
"role": role,
|
|
888
|
+
"emailAddress": email_address,
|
|
889
|
+
},
|
|
890
|
+
params={"sendNotificationEmail": False, "supportsAllDrives": True},
|
|
891
|
+
)
|
|
892
|
+
|
|
893
|
+
elif action == "update":
|
|
894
|
+
response = await self._client.patch(
|
|
895
|
+
url=f"/{file_id}/permissions/{permission_id}",
|
|
896
|
+
json={"role": role},
|
|
897
|
+
params={"transferOwnership": transfer_ownership, "supportsAllDrives": True},
|
|
898
|
+
)
|
|
899
|
+
|
|
900
|
+
elif action == "remove":
|
|
901
|
+
response = await self._client.delete(url=f"/{file_id}/permissions/{permission_id}")
|
|
902
|
+
|
|
903
|
+
else:
|
|
904
|
+
raise GoogleDriveError(f"Invalid action '{action}'")
|
|
905
|
+
|
|
906
|
+
if response.status_code not in (200, 201, 204):
|
|
907
|
+
raise GoogleDriveError(f"Drive API error {response.status_code}: {response.text}")
|
|
908
|
+
|
|
909
|
+
if action == "add":
|
|
910
|
+
return response.json()["id"]
|
|
911
|
+
|
|
912
|
+
# Cannot be null here because of above validators
|
|
913
|
+
return permission_id # type: ignore
|
|
914
|
+
|
|
824
915
|
async def __aenter__(self) -> "GoogleDriveClient":
|
|
825
916
|
"""Async context manager entry."""
|
|
826
917
|
return self
|