datarobot-genai 0.2.0__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.
- datarobot_genai/__init__.py +19 -0
- datarobot_genai/core/__init__.py +0 -0
- datarobot_genai/core/agents/__init__.py +43 -0
- datarobot_genai/core/agents/base.py +195 -0
- datarobot_genai/core/chat/__init__.py +19 -0
- datarobot_genai/core/chat/auth.py +146 -0
- datarobot_genai/core/chat/client.py +178 -0
- datarobot_genai/core/chat/responses.py +297 -0
- datarobot_genai/core/cli/__init__.py +18 -0
- datarobot_genai/core/cli/agent_environment.py +47 -0
- datarobot_genai/core/cli/agent_kernel.py +211 -0
- datarobot_genai/core/custom_model.py +141 -0
- datarobot_genai/core/mcp/__init__.py +0 -0
- datarobot_genai/core/mcp/common.py +218 -0
- datarobot_genai/core/telemetry_agent.py +126 -0
- datarobot_genai/core/utils/__init__.py +3 -0
- datarobot_genai/core/utils/auth.py +234 -0
- datarobot_genai/core/utils/urls.py +64 -0
- datarobot_genai/crewai/__init__.py +24 -0
- datarobot_genai/crewai/agent.py +42 -0
- datarobot_genai/crewai/base.py +159 -0
- datarobot_genai/crewai/events.py +117 -0
- datarobot_genai/crewai/mcp.py +59 -0
- datarobot_genai/drmcp/__init__.py +78 -0
- datarobot_genai/drmcp/core/__init__.py +13 -0
- datarobot_genai/drmcp/core/auth.py +165 -0
- datarobot_genai/drmcp/core/clients.py +180 -0
- datarobot_genai/drmcp/core/config.py +250 -0
- datarobot_genai/drmcp/core/config_utils.py +174 -0
- datarobot_genai/drmcp/core/constants.py +18 -0
- datarobot_genai/drmcp/core/credentials.py +190 -0
- datarobot_genai/drmcp/core/dr_mcp_server.py +316 -0
- datarobot_genai/drmcp/core/dr_mcp_server_logo.py +136 -0
- datarobot_genai/drmcp/core/dynamic_prompts/__init__.py +13 -0
- datarobot_genai/drmcp/core/dynamic_prompts/controllers.py +130 -0
- datarobot_genai/drmcp/core/dynamic_prompts/dr_lib.py +128 -0
- datarobot_genai/drmcp/core/dynamic_prompts/register.py +206 -0
- datarobot_genai/drmcp/core/dynamic_prompts/utils.py +33 -0
- datarobot_genai/drmcp/core/dynamic_tools/__init__.py +14 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/__init__.py +0 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/adapters/__init__.py +14 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/adapters/base.py +72 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/adapters/default.py +82 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/adapters/drum.py +238 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/config.py +228 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/controllers.py +63 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/metadata.py +162 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/register.py +87 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/schemas/drum_agentic_fallback_schema.json +36 -0
- datarobot_genai/drmcp/core/dynamic_tools/deployment/schemas/drum_prediction_fallback_schema.json +10 -0
- datarobot_genai/drmcp/core/dynamic_tools/register.py +254 -0
- datarobot_genai/drmcp/core/dynamic_tools/schema.py +532 -0
- datarobot_genai/drmcp/core/exceptions.py +25 -0
- datarobot_genai/drmcp/core/logging.py +98 -0
- datarobot_genai/drmcp/core/mcp_instance.py +542 -0
- datarobot_genai/drmcp/core/mcp_server_tools.py +129 -0
- datarobot_genai/drmcp/core/memory_management/__init__.py +13 -0
- datarobot_genai/drmcp/core/memory_management/manager.py +820 -0
- datarobot_genai/drmcp/core/memory_management/memory_tools.py +201 -0
- datarobot_genai/drmcp/core/routes.py +436 -0
- datarobot_genai/drmcp/core/routes_utils.py +30 -0
- datarobot_genai/drmcp/core/server_life_cycle.py +107 -0
- datarobot_genai/drmcp/core/telemetry.py +424 -0
- datarobot_genai/drmcp/core/tool_filter.py +108 -0
- datarobot_genai/drmcp/core/utils.py +131 -0
- datarobot_genai/drmcp/server.py +19 -0
- datarobot_genai/drmcp/test_utils/__init__.py +13 -0
- datarobot_genai/drmcp/test_utils/integration_mcp_server.py +102 -0
- datarobot_genai/drmcp/test_utils/mcp_utils_ete.py +96 -0
- datarobot_genai/drmcp/test_utils/mcp_utils_integration.py +94 -0
- datarobot_genai/drmcp/test_utils/openai_llm_mcp_client.py +234 -0
- datarobot_genai/drmcp/test_utils/tool_base_ete.py +151 -0
- datarobot_genai/drmcp/test_utils/utils.py +91 -0
- datarobot_genai/drmcp/tools/__init__.py +14 -0
- datarobot_genai/drmcp/tools/predictive/__init__.py +27 -0
- datarobot_genai/drmcp/tools/predictive/data.py +97 -0
- datarobot_genai/drmcp/tools/predictive/deployment.py +91 -0
- datarobot_genai/drmcp/tools/predictive/deployment_info.py +392 -0
- datarobot_genai/drmcp/tools/predictive/model.py +148 -0
- datarobot_genai/drmcp/tools/predictive/predict.py +254 -0
- datarobot_genai/drmcp/tools/predictive/predict_realtime.py +307 -0
- datarobot_genai/drmcp/tools/predictive/project.py +72 -0
- datarobot_genai/drmcp/tools/predictive/training.py +651 -0
- datarobot_genai/langgraph/__init__.py +0 -0
- datarobot_genai/langgraph/agent.py +341 -0
- datarobot_genai/langgraph/mcp.py +73 -0
- datarobot_genai/llama_index/__init__.py +16 -0
- datarobot_genai/llama_index/agent.py +50 -0
- datarobot_genai/llama_index/base.py +299 -0
- datarobot_genai/llama_index/mcp.py +79 -0
- datarobot_genai/nat/__init__.py +0 -0
- datarobot_genai/nat/agent.py +258 -0
- datarobot_genai/nat/datarobot_llm_clients.py +249 -0
- datarobot_genai/nat/datarobot_llm_providers.py +130 -0
- datarobot_genai/py.typed +0 -0
- datarobot_genai-0.2.0.dist-info/METADATA +139 -0
- datarobot_genai-0.2.0.dist-info/RECORD +101 -0
- datarobot_genai-0.2.0.dist-info/WHEEL +4 -0
- datarobot_genai-0.2.0.dist-info/entry_points.txt +3 -0
- datarobot_genai-0.2.0.dist-info/licenses/AUTHORS +2 -0
- datarobot_genai-0.2.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
import json
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel
|
|
19
|
+
|
|
20
|
+
from .openai_llm_mcp_client import LLMResponse
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ToolCallTestExpectations(BaseModel):
|
|
24
|
+
"""Class to store tool call information."""
|
|
25
|
+
|
|
26
|
+
name: str
|
|
27
|
+
parameters: dict[str, Any]
|
|
28
|
+
result: str | dict[str, Any]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ETETestExpectations(BaseModel):
|
|
32
|
+
"""Class to store test expectations for ETE tests."""
|
|
33
|
+
|
|
34
|
+
potential_no_tool_calls: bool = False
|
|
35
|
+
tool_calls_expected: list[ToolCallTestExpectations]
|
|
36
|
+
llm_response_content_contains_expectations: list[str]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
SHOULD_NOT_BE_EMPTY = "SHOULD_NOT_BE_EMPTY"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _check_dict_has_keys(
|
|
43
|
+
expected: dict[str, Any],
|
|
44
|
+
actual: dict[str, Any] | list[dict[str, Any]],
|
|
45
|
+
path: str = "",
|
|
46
|
+
) -> bool:
|
|
47
|
+
"""
|
|
48
|
+
Recursively check if all keys in expected dict exist in actual dict or in each item of
|
|
49
|
+
actual list.
|
|
50
|
+
Returns True if all expected keys exist, False otherwise.
|
|
51
|
+
"""
|
|
52
|
+
# If actual is a list, check each item against the expected structure
|
|
53
|
+
if isinstance(actual, list):
|
|
54
|
+
if not actual: # Empty list
|
|
55
|
+
return False
|
|
56
|
+
# Check first item against expected structure
|
|
57
|
+
return _check_dict_has_keys(expected, actual[0], path)
|
|
58
|
+
|
|
59
|
+
# Regular dict check
|
|
60
|
+
for key, value in expected.items():
|
|
61
|
+
current_path = f"{path}.{key}" if path else key
|
|
62
|
+
if key not in actual:
|
|
63
|
+
return False
|
|
64
|
+
if isinstance(value, dict):
|
|
65
|
+
if not isinstance(actual[key], dict):
|
|
66
|
+
return False
|
|
67
|
+
if not _check_dict_has_keys(value, actual[key], current_path):
|
|
68
|
+
return False
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ToolBaseE2E:
|
|
73
|
+
"""Base class for end-to-end tests."""
|
|
74
|
+
|
|
75
|
+
async def _run_test_with_expectations(
|
|
76
|
+
self,
|
|
77
|
+
prompt: str,
|
|
78
|
+
test_expectations: ETETestExpectations,
|
|
79
|
+
openai_llm_client: Any,
|
|
80
|
+
mcp_session: Any,
|
|
81
|
+
test_name: str,
|
|
82
|
+
) -> None:
|
|
83
|
+
"""
|
|
84
|
+
Run a test with given expectations and validate the results.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
prompt: The prompt to send to the LLM
|
|
88
|
+
test_expectations: ETETestExpectations object containing test expectations with keys:
|
|
89
|
+
- tool_calls_expected: List of expected tool calls with their parameters and results
|
|
90
|
+
- llm_response_content_contains_expectations: Expected content in the LLM response
|
|
91
|
+
openai_llm_client: The OpenAI LLM client
|
|
92
|
+
mcp_session: The test session
|
|
93
|
+
test_name: The name of the test (e.g. test_get_best_model_success)
|
|
94
|
+
"""
|
|
95
|
+
# Get the test file name from the class name
|
|
96
|
+
file_name = self.__class__.__name__.lower().replace("e2e", "").replace("test", "")
|
|
97
|
+
output_file_name = f"{file_name}_{test_name}"
|
|
98
|
+
|
|
99
|
+
# Act
|
|
100
|
+
response: LLMResponse = await openai_llm_client.process_prompt_with_mcp_support(
|
|
101
|
+
prompt, mcp_session, output_file_name
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# sometimes llm are too smart and doesn't call tools especially for the case when file
|
|
105
|
+
# doesn't exist
|
|
106
|
+
if test_expectations.potential_no_tool_calls and len(response.tool_calls) == 0:
|
|
107
|
+
pass
|
|
108
|
+
else:
|
|
109
|
+
# Verify LLM decided to use tools
|
|
110
|
+
assert len(response.tool_calls) == len(test_expectations.tool_calls_expected), (
|
|
111
|
+
"LLM should have decided to call tools"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
for i, tool_call in enumerate(response.tool_calls):
|
|
115
|
+
assert tool_call.tool_name == test_expectations.tool_calls_expected[i].name, (
|
|
116
|
+
f"Should have called {test_expectations.tool_calls_expected[i].name} tool, but "
|
|
117
|
+
f"got: {tool_call.tool_name}"
|
|
118
|
+
)
|
|
119
|
+
assert tool_call.parameters == test_expectations.tool_calls_expected[i].parameters, (
|
|
120
|
+
f"Should have called {tool_call.tool_name} tool with the correct parameters, but "
|
|
121
|
+
f"got: {tool_call.parameters}"
|
|
122
|
+
)
|
|
123
|
+
if test_expectations.tool_calls_expected[i].result != SHOULD_NOT_BE_EMPTY:
|
|
124
|
+
expected_result = test_expectations.tool_calls_expected[i].result
|
|
125
|
+
if isinstance(expected_result, str):
|
|
126
|
+
assert expected_result in response.tool_results[i], (
|
|
127
|
+
f"Should have called {tool_call.tool_name} tool with the correct result, "
|
|
128
|
+
f"but got: {response.tool_results[i]}"
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
actual_result = json.loads(response.tool_results[i])
|
|
132
|
+
assert _check_dict_has_keys(expected_result, actual_result), (
|
|
133
|
+
f"Should have called {tool_call.tool_name} tool with the correct result "
|
|
134
|
+
f"structure, but got: {response.tool_results[i]}"
|
|
135
|
+
)
|
|
136
|
+
else:
|
|
137
|
+
assert len(response.tool_results[i]) > 0, (
|
|
138
|
+
f"Should have called {tool_call.tool_name} tool with non-empty result, but "
|
|
139
|
+
f"got: {response.tool_results[i]}"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Verify LLM provided comprehensive response
|
|
143
|
+
assert len(response.content) > 100, "LLM should provide detailed response"
|
|
144
|
+
assert any(
|
|
145
|
+
expected_response.lower() in response.content
|
|
146
|
+
for expected_response in test_expectations.llm_response_content_contains_expectations
|
|
147
|
+
), (
|
|
148
|
+
f"Response should mention "
|
|
149
|
+
f"{test_expectations.llm_response_content_contains_expectations}, "
|
|
150
|
+
f"but got: {response.content}"
|
|
151
|
+
)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
import datetime
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
from typing import TYPE_CHECKING
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from .openai_llm_mcp_client import LLMResponse
|
|
23
|
+
|
|
24
|
+
from dotenv import load_dotenv
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def load_env() -> None:
|
|
28
|
+
load_dotenv(dotenv_path=".env", verbose=True, override=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def format_tool_call(tool_call: dict[str, Any]) -> str:
|
|
32
|
+
"""Format a single tool call in a readable way."""
|
|
33
|
+
return (
|
|
34
|
+
f"Tool: {tool_call['tool_name']}\n"
|
|
35
|
+
f"Parameters: {json.dumps(tool_call['parameters'], indent=2)}\n"
|
|
36
|
+
f"Reasoning: {tool_call['reasoning']}"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def format_response(response: "LLMResponse") -> str:
|
|
41
|
+
"""Format the LLM response in a readable way."""
|
|
42
|
+
formatted_parts = []
|
|
43
|
+
|
|
44
|
+
# Format the main content
|
|
45
|
+
formatted_parts.append("=== LLM Response ===\n")
|
|
46
|
+
formatted_parts.append(response.content)
|
|
47
|
+
|
|
48
|
+
# Format tool calls if any
|
|
49
|
+
if response.tool_calls:
|
|
50
|
+
formatted_parts.append("\n=== Tools Used ===")
|
|
51
|
+
for i, tool_call in enumerate(response.tool_calls, 1):
|
|
52
|
+
formatted_parts.append(f"\nTool Call #{i}:")
|
|
53
|
+
formatted_parts.append(
|
|
54
|
+
format_tool_call(
|
|
55
|
+
{
|
|
56
|
+
"tool_name": tool_call.tool_name,
|
|
57
|
+
"parameters": tool_call.parameters,
|
|
58
|
+
"reasoning": tool_call.reasoning,
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Format tool results if any
|
|
64
|
+
if response.tool_results:
|
|
65
|
+
formatted_parts.append("\n=== Tool Results ===")
|
|
66
|
+
for i, result in enumerate(response.tool_results, 1):
|
|
67
|
+
formatted_parts.append(f"\nResult #{i}:")
|
|
68
|
+
formatted_parts.append(result)
|
|
69
|
+
|
|
70
|
+
return "\n".join(formatted_parts)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def save_response_to_file(response: "LLMResponse", name: str | None = None) -> None:
|
|
74
|
+
"""Save the response to a file in a readable format.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
response: The LLM response to save
|
|
78
|
+
name: Optional name to use in the filename. If not provided,
|
|
79
|
+
will use a timestamp only.
|
|
80
|
+
"""
|
|
81
|
+
# Create responses directory with timestamp
|
|
82
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d")
|
|
83
|
+
dir_path = "test_results/drmcp/.ete_responses/" + timestamp
|
|
84
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
# Save both raw JSON and formatted text
|
|
87
|
+
base_name = f"{name}" if name else "response"
|
|
88
|
+
|
|
89
|
+
# Save formatted text
|
|
90
|
+
with open(f"{dir_path}/{base_name}.txt", "w") as f:
|
|
91
|
+
f.write(format_response(response))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
"""
|
|
16
|
+
DataRobot MCP tools for interacting with DataRobot platform.
|
|
17
|
+
|
|
18
|
+
This package contains pre-built tools for DataRobot operations:
|
|
19
|
+
- data: Upload and manage datasets
|
|
20
|
+
- project: Create and manage projects
|
|
21
|
+
- model: List and interact with models
|
|
22
|
+
- training: Train models with autopilot
|
|
23
|
+
- deployment: Deploy and manage models
|
|
24
|
+
- predict: Make batch predictions
|
|
25
|
+
- predict_realtime: Make real-time predictions
|
|
26
|
+
- deployment_info: Get deployment information
|
|
27
|
+
"""
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
from datarobot_genai.drmcp.core.clients import get_sdk_client
|
|
19
|
+
from datarobot_genai.drmcp.core.mcp_instance import dr_mcp_tool
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dr_mcp_tool(tags={"data", "management", "upload"})
|
|
25
|
+
async def upload_dataset_to_ai_catalog(file_path: str) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Upload a dataset to the DataRobot AI Catalog.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
file_path: Path to the file to upload.
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
A string summary of the upload result.
|
|
35
|
+
"""
|
|
36
|
+
client = get_sdk_client()
|
|
37
|
+
if not os.path.exists(file_path):
|
|
38
|
+
logger.error(f"File not found: {file_path}")
|
|
39
|
+
return f"File not found: {file_path}"
|
|
40
|
+
catalog_item = client.Dataset.create_from_file(file_path)
|
|
41
|
+
logger.info(f"Successfully uploaded dataset: {catalog_item.id}")
|
|
42
|
+
return f"AI Catalog ID: {catalog_item.id}"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dr_mcp_tool(tags={"data", "management", "list"})
|
|
46
|
+
async def list_ai_catalog_items() -> str:
|
|
47
|
+
"""
|
|
48
|
+
List all AI Catalog items (datasets) for the authenticated user.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
A string summary of the AI Catalog items with their IDs and names.
|
|
53
|
+
"""
|
|
54
|
+
client = get_sdk_client()
|
|
55
|
+
datasets = client.Dataset.list()
|
|
56
|
+
if not datasets:
|
|
57
|
+
logger.info("No AI Catalog items found")
|
|
58
|
+
return "No AI Catalog items found."
|
|
59
|
+
result = "\n".join(f"{ds.id}: {ds.name}" for ds in datasets)
|
|
60
|
+
logger.info(f"Found {len(datasets)} AI Catalog items")
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# from fastmcp import Context
|
|
65
|
+
|
|
66
|
+
# from datarobot_genai.drmcp.core.memory_management import MemoryManager, get_memory_manager
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# @dr_mcp_tool()
|
|
70
|
+
# async def list_ai_catalog_items(
|
|
71
|
+
# ctx: Context, agent_id: str = None, storage_id: str = None
|
|
72
|
+
# ) -> str:
|
|
73
|
+
# """
|
|
74
|
+
# List all AI Catalog items (datasets) for the authenticated user.
|
|
75
|
+
|
|
76
|
+
# Returns:
|
|
77
|
+
# a resource id that can be used to retrieve the list of AI Catalog items using the
|
|
78
|
+
# get_resource tool
|
|
79
|
+
# """
|
|
80
|
+
# client = get_sdk_client()
|
|
81
|
+
# datasets = client.Dataset.list()
|
|
82
|
+
# if not datasets:
|
|
83
|
+
# logger.info("No AI Catalog items found")
|
|
84
|
+
# return "No AI Catalog items found."
|
|
85
|
+
# result = "\n".join(f"{ds.id}: {ds.name}" for ds in datasets)
|
|
86
|
+
|
|
87
|
+
# if MemoryManager.is_initialized():
|
|
88
|
+
# resource_id = await get_memory_manager().store_resource(
|
|
89
|
+
# data=result,
|
|
90
|
+
# memory_storage_id=storage_id,
|
|
91
|
+
# agent_identifier=agent_id,
|
|
92
|
+
# )
|
|
93
|
+
# else:
|
|
94
|
+
# raise ValueError("MemoryManager is not initialized")
|
|
95
|
+
|
|
96
|
+
# logger.info(f"Found {len(datasets)} AI Catalog items")
|
|
97
|
+
# return resource_id
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
|
|
18
|
+
from datarobot_genai.drmcp.core.clients import get_sdk_client
|
|
19
|
+
from datarobot_genai.drmcp.core.mcp_instance import dr_mcp_tool
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dr_mcp_tool(tags={"deployment", "management", "list"})
|
|
25
|
+
async def list_deployments() -> str:
|
|
26
|
+
"""
|
|
27
|
+
List all DataRobot deployments for the authenticated user.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
A string summary of the user's DataRobot deployments.
|
|
32
|
+
"""
|
|
33
|
+
client = get_sdk_client()
|
|
34
|
+
deployments = client.Deployment.list()
|
|
35
|
+
if not deployments:
|
|
36
|
+
logger.info("No deployments found")
|
|
37
|
+
return "No deployments found."
|
|
38
|
+
result = "\n".join(f"{d.id}: {d.label}" for d in deployments)
|
|
39
|
+
logger.info(f"Found {len(deployments)} deployments")
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dr_mcp_tool(tags={"deployment", "model", "info"})
|
|
44
|
+
async def get_model_info_from_deployment(deployment_id: str) -> str:
|
|
45
|
+
"""
|
|
46
|
+
Get model info associated with a given deployment ID.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
deployment_id: The ID of the DataRobot deployment.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
The model info associated with the deployment as a JSON string.
|
|
54
|
+
"""
|
|
55
|
+
client = get_sdk_client()
|
|
56
|
+
deployment = client.Deployment.get(deployment_id)
|
|
57
|
+
logger.info(f"Retrieved model info for deployment {deployment_id}")
|
|
58
|
+
return json.dumps(deployment.model, indent=2)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dr_mcp_tool(tags={"deployment", "model", "create"})
|
|
62
|
+
async def deploy_model(model_id: str, label: str, description: str = "") -> str:
|
|
63
|
+
"""
|
|
64
|
+
Deploy a model by creating a new DataRobot deployment.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
model_id: The ID of the DataRobot model to deploy.
|
|
68
|
+
label: The label/name for the deployment.
|
|
69
|
+
description: Optional description for the deployment.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
JSON string with deployment ID and label, or error message.
|
|
74
|
+
"""
|
|
75
|
+
client = get_sdk_client()
|
|
76
|
+
try:
|
|
77
|
+
prediction_servers = client.PredictionServer.list()
|
|
78
|
+
if not prediction_servers:
|
|
79
|
+
logger.error("No prediction servers available")
|
|
80
|
+
return json.dumps({"error": "No prediction servers available"})
|
|
81
|
+
deployment = client.Deployment.create_from_learning_model(
|
|
82
|
+
model_id=model_id,
|
|
83
|
+
label=label,
|
|
84
|
+
description=description,
|
|
85
|
+
default_prediction_server_id=prediction_servers[0].id,
|
|
86
|
+
)
|
|
87
|
+
logger.info(f"Created deployment {deployment.id} with label {label}")
|
|
88
|
+
return json.dumps({"deployment_id": deployment.id, "label": label})
|
|
89
|
+
except Exception as e:
|
|
90
|
+
logger.error(f"Error deploying model {model_id}: {type(e).__name__}: {e}")
|
|
91
|
+
return json.dumps({"error": f"Error deploying model {model_id}: {type(e).__name__}: {e}"})
|