datarobot-genai 0.2.29__py3-none-any.whl → 0.2.37__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.
Files changed (28) hide show
  1. datarobot_genai/core/cli/agent_kernel.py +4 -1
  2. datarobot_genai/drmcp/__init__.py +2 -2
  3. datarobot_genai/drmcp/core/exceptions.py +0 -4
  4. datarobot_genai/drmcp/core/logging.py +2 -2
  5. datarobot_genai/drmcp/test_utils/clients/__init__.py +0 -0
  6. datarobot_genai/drmcp/test_utils/clients/anthropic.py +68 -0
  7. datarobot_genai/drmcp/test_utils/{openai_llm_mcp_client.py → clients/base.py} +38 -40
  8. datarobot_genai/drmcp/test_utils/clients/dr_gateway.py +58 -0
  9. datarobot_genai/drmcp/test_utils/clients/openai.py +68 -0
  10. datarobot_genai/drmcp/test_utils/mcp_utils_ete.py +20 -0
  11. datarobot_genai/drmcp/test_utils/test_interactive.py +16 -16
  12. datarobot_genai/drmcp/test_utils/tool_base_ete.py +1 -1
  13. datarobot_genai/drmcp/test_utils/utils.py +1 -1
  14. datarobot_genai/drmcp/tools/clients/gdrive.py +187 -1
  15. datarobot_genai/drmcp/tools/clients/microsoft_graph.py +126 -0
  16. datarobot_genai/drmcp/tools/gdrive/tools.py +186 -10
  17. datarobot_genai/drmcp/tools/microsoft_graph/tools.py +79 -0
  18. datarobot_genai/drmcp/tools/predictive/data.py +5 -5
  19. datarobot_genai/drmcp/tools/predictive/deployment.py +52 -46
  20. datarobot_genai/drmcp/tools/predictive/model.py +87 -52
  21. datarobot_genai/drmcp/tools/predictive/project.py +2 -2
  22. datarobot_genai/drmcp/tools/predictive/training.py +52 -24
  23. {datarobot_genai-0.2.29.dist-info → datarobot_genai-0.2.37.dist-info}/METADATA +1 -1
  24. {datarobot_genai-0.2.29.dist-info → datarobot_genai-0.2.37.dist-info}/RECORD +28 -24
  25. {datarobot_genai-0.2.29.dist-info → datarobot_genai-0.2.37.dist-info}/WHEEL +0 -0
  26. {datarobot_genai-0.2.29.dist-info → datarobot_genai-0.2.37.dist-info}/entry_points.txt +0 -0
  27. {datarobot_genai-0.2.29.dist-info → datarobot_genai-0.2.37.dist-info}/licenses/AUTHORS +0 -0
  28. {datarobot_genai-0.2.29.dist-info → datarobot_genai-0.2.37.dist-info}/licenses/LICENSE +0 -0
@@ -35,9 +35,9 @@ async def upload_dataset_to_ai_catalog(
35
35
  ) -> ToolError | ToolResult:
36
36
  """Upload a dataset to the DataRobot AI Catalog / Data Registry."""
37
37
  if not file_path and not file_url:
38
- return ToolError("Either file_path or file_url must be provided.")
38
+ raise ToolError("Either file_path or file_url must be provided.")
39
39
  if file_path and file_url:
40
- return ToolError("Please provide either file_path or file_url, not both.")
40
+ raise ToolError("Please provide either file_path or file_url, not both.")
41
41
 
42
42
  # Get client
43
43
  client = get_sdk_client()
@@ -47,17 +47,17 @@ async def upload_dataset_to_ai_catalog(
47
47
  # Does file exist?
48
48
  if not os.path.exists(file_path):
49
49
  logger.error("File not found: %s", file_path)
50
- return ToolError(f"File not found: {file_path}")
50
+ raise ToolError(f"File not found: {file_path}")
51
51
  catalog_item = client.Dataset.create_from_file(file_path)
52
52
  else:
53
53
  # Does URL exist?
54
54
  if file_url is None or not is_valid_url(file_url):
55
55
  logger.error("Invalid file URL: %s", file_url)
56
- return ToolError(f"Invalid file URL: {file_url}")
56
+ raise ToolError(f"Invalid file URL: {file_url}")
57
57
  catalog_item = client.Dataset.create_from_url(file_url)
58
58
 
59
59
  if not catalog_item:
60
- return ToolError("Failed to upload dataset.")
60
+ raise ToolError("Failed to upload dataset.")
61
61
 
62
62
  return ToolResult(
63
63
  content=f"Successfully uploaded dataset: {catalog_item.id}",
@@ -14,6 +14,10 @@
14
14
 
15
15
  import json
16
16
  import logging
17
+ from typing import Annotated
18
+
19
+ from fastmcp.exceptions import ToolError
20
+ from fastmcp.tools.tool import ToolResult
17
21
 
18
22
  from datarobot_genai.drmcp.core.clients import get_sdk_client
19
23
  from datarobot_genai.drmcp.core.mcp_instance import dr_mcp_tool
@@ -21,71 +25,73 @@ from datarobot_genai.drmcp.core.mcp_instance import dr_mcp_tool
21
25
  logger = logging.getLogger(__name__)
22
26
 
23
27
 
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
- """
28
+ @dr_mcp_tool(tags={"predictive", "deployment", "read", "management", "list"})
29
+ async def list_deployments() -> ToolResult:
30
+ """List all DataRobot deployments for the authenticated user."""
33
31
  client = get_sdk_client()
34
32
  deployments = client.Deployment.list()
35
33
  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
-
34
+ return ToolResult(
35
+ content="No deployments found.",
36
+ structured_content={"deployments": []},
37
+ )
38
+ deployments_dict = {d.id: d.label for d in deployments}
39
+ return ToolResult(
40
+ content="\n".join(f"{d.id}: {d.label}" for d in deployments),
41
+ structured_content={"deployments": deployments_dict},
42
+ )
42
43
 
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
44
 
48
- Args:
49
- deployment_id: The ID of the DataRobot deployment.
45
+ @dr_mcp_tool(tags={"predictive", "deployment", "read", "model", "info"})
46
+ async def get_model_info_from_deployment(
47
+ *,
48
+ deployment_id: Annotated[str, "The ID of the DataRobot deployment"] | None = None,
49
+ ) -> ToolError | ToolResult:
50
+ """Retrieve model info associated with a given deployment ID."""
51
+ if not deployment_id:
52
+ raise ToolError("Deployment ID must be provided")
50
53
 
51
- Returns
52
- -------
53
- The model info associated with the deployment as a JSON string.
54
- """
55
54
  client = get_sdk_client()
56
55
  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
-
56
+ return ToolResult(
57
+ content=(
58
+ f"Retrieved model info for deployment {deployment_id}, here are the details:\n"
59
+ f"{json.dumps(deployment.model, indent=2)}"
60
+ ),
61
+ structured_content=deployment.model,
62
+ )
60
63
 
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
64
 
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.
65
+ @dr_mcp_tool(tags={"predictive", "deployment", "write", "model", "create"})
66
+ async def deploy_model(
67
+ *,
68
+ model_id: Annotated[str, "The ID of the DataRobot model to deploy"] | None = None,
69
+ label: Annotated[str, "The label/name for the deployment"] | None = None,
70
+ description: Annotated[str, "Optional description for the deployment"] | None = None,
71
+ ) -> ToolError | ToolResult:
72
+ """Deploy a model by creating a new DataRobot deployment."""
73
+ if not model_id:
74
+ raise ToolError("Model ID must be provided")
75
+ if not label:
76
+ raise ToolError("Model label must be provided")
70
77
 
71
- Returns
72
- -------
73
- JSON string with deployment ID and label, or error message.
74
- """
75
78
  client = get_sdk_client()
76
79
  try:
77
80
  prediction_servers = client.PredictionServer.list()
78
81
  if not prediction_servers:
79
- logger.error("No prediction servers available")
80
- return json.dumps({"error": "No prediction servers available"})
82
+ raise ToolError("No prediction servers available for deployment.")
81
83
  deployment = client.Deployment.create_from_learning_model(
82
84
  model_id=model_id,
83
85
  label=label,
84
86
  description=description,
85
87
  default_prediction_server_id=prediction_servers[0].id,
86
88
  )
87
- logger.info(f"Created deployment {deployment.id} with label {label}")
88
- return json.dumps({"deployment_id": deployment.id, "label": label})
89
+ return ToolResult(
90
+ content=f"Created deployment {deployment.id} with label {label}",
91
+ structured_content={
92
+ "deployment_id": deployment.id,
93
+ "label": label,
94
+ },
95
+ )
89
96
  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}"})
97
+ raise ToolError(f"Error deploying model {model_id}: {type(e).__name__}: {e}")
@@ -14,9 +14,12 @@
14
14
 
15
15
  import json
16
16
  import logging
17
+ from typing import Annotated
17
18
  from typing import Any
18
19
 
19
20
  from datarobot.models.model import Model
21
+ from fastmcp.exceptions import ToolError
22
+ from fastmcp.tools.tool import ToolResult
20
23
 
21
24
  from datarobot_genai.drmcp.core.clients import get_sdk_client
22
25
  from datarobot_genai.drmcp.core.mcp_instance import dr_mcp_tool
@@ -50,33 +53,25 @@ class ModelEncoder(json.JSONEncoder):
50
53
  return super().default(obj)
51
54
 
52
55
 
53
- @dr_mcp_tool(tags={"model", "management", "info"})
54
- async def get_best_model(project_id: str, metric: str | None = None) -> str:
55
- """
56
- Get the best model for a DataRobot project, optionally by a specific metric.
56
+ @dr_mcp_tool(tags={"predictive", "model", "read", "management", "info"})
57
+ async def get_best_model(
58
+ *,
59
+ project_id: Annotated[str, "The DataRobot project ID"] | None = None,
60
+ metric: Annotated[str, "The metric to use for best model selection (e.g., 'AUC', 'LogLoss')"]
61
+ | None = None,
62
+ ) -> ToolError | ToolResult:
63
+ """Get the best model for a DataRobot project, optionally by a specific metric."""
64
+ if not project_id:
65
+ raise ToolError("Project ID must be provided")
57
66
 
58
- Args:
59
- project_id: The ID of the DataRobot project.
60
- metric: (Optional) The metric to use for best model selection (e.g., 'AUC', 'LogLoss').
61
-
62
- Returns
63
- -------
64
- A formatted string describing the best model.
65
-
66
- Raises
67
- ------
68
- Exception: If project not found or no models exist in the project.
69
- """
70
67
  client = get_sdk_client()
71
68
  project = client.Project.get(project_id)
72
69
  if not project:
73
- logger.error(f"Project with ID {project_id} not found")
74
- raise Exception(f"Project with ID {project_id} not found.")
70
+ raise ToolError(f"Project with ID {project_id} not found.")
75
71
 
76
72
  leaderboard = project.get_models()
77
73
  if not leaderboard:
78
- logger.info(f"No models found for project {project_id}")
79
- raise Exception("No models found for this project.")
74
+ raise ToolError("No models found for this project.")
80
75
 
81
76
  if metric:
82
77
  reverse_sort = metric.upper() in [
@@ -98,51 +93,91 @@ async def get_best_model(project_id: str, metric: str | None = None) -> str:
98
93
  best_model = leaderboard[0]
99
94
  logger.info(f"Found best model {best_model.id} for project {project_id}")
100
95
 
101
- # Format the response as a human-readable string
102
96
  metric_info = ""
97
+ metric_value = None
98
+
103
99
  if metric and best_model.metrics and metric in best_model.metrics:
104
100
  metric_value = best_model.metrics[metric].get("validation")
105
101
  if metric_value is not None:
106
102
  metric_info = f" with {metric}: {metric_value:.2f}"
107
103
 
108
- return f"Best model: {best_model.model_type}{metric_info}"
109
-
110
-
111
- @dr_mcp_tool(tags={"model", "prediction", "scoring"})
112
- async def score_dataset_with_model(project_id: str, model_id: str, dataset_url: str) -> str:
113
- """
114
- Score a dataset using a specific DataRobot model.
104
+ # Include full metrics in the response
105
+ best_model_dict = model_to_dict(best_model)
106
+ best_model_dict["metric"] = metric
107
+ best_model_dict["metric_value"] = metric_value
108
+
109
+ # Format metrics for human-readable content
110
+ metrics_text = ""
111
+ if best_model.metrics:
112
+ metrics_list = []
113
+ for metric_name, metric_data in best_model.metrics.items():
114
+ if isinstance(metric_data, dict) and "validation" in metric_data:
115
+ val = metric_data["validation"]
116
+ if val is not None:
117
+ metrics_list.append(f"{metric_name}: {val:.4f}")
118
+ if metrics_list:
119
+ metrics_text = "\nPerformance metrics:\n" + "\n".join(f" - {m}" for m in metrics_list)
120
+
121
+ return ToolResult(
122
+ content=f"Best model: {best_model.model_type}{metric_info}{metrics_text}",
123
+ structured_content={
124
+ "project_id": project_id,
125
+ "best_model": best_model_dict,
126
+ },
127
+ )
128
+
129
+
130
+ @dr_mcp_tool(tags={"predictive", "model", "read", "scoring", "dataset"})
131
+ async def score_dataset_with_model(
132
+ *,
133
+ project_id: Annotated[str, "The DataRobot project ID"] | None = None,
134
+ model_id: Annotated[str, "The DataRobot model ID"] | None = None,
135
+ dataset_url: Annotated[str, "The dataset URL"] | None = None,
136
+ ) -> ToolError | ToolResult:
137
+ """Score a dataset using a specific DataRobot model."""
138
+ if not project_id:
139
+ raise ToolError("Project ID must be provided")
140
+ if not model_id:
141
+ raise ToolError("Model ID must be provided")
142
+ if not dataset_url:
143
+ raise ToolError("Dataset URL must be provided")
115
144
 
116
- Args:
117
- project_id: The ID of the DataRobot project.
118
- model_id: The ID of the DataRobot model to use for scoring.
119
- dataset_url: The URL to the dataset to score (must be accessible to DataRobot).
120
-
121
- Returns
122
- -------
123
- A string summary of the scoring job or a meaningful error message.
124
- """
125
145
  client = get_sdk_client()
126
146
  project = client.Project.get(project_id)
127
147
  model = client.Model.get(project, model_id)
128
148
  job = model.score(dataset_url)
129
- logger.info(f"Started scoring job {job.id} for model {model_id}")
130
- return f"Scoring job started: {job.id}"
131
-
132
149
 
133
- @dr_mcp_tool(tags={"model", "management", "list"})
134
- async def list_models(project_id: str) -> str:
135
- """
136
- List all models in a project.
150
+ return ToolResult(
151
+ content=f"Scoring job started: {job.id}",
152
+ structured_content={
153
+ "scoring_job_id": job.id,
154
+ "project_id": project_id,
155
+ "model_id": model_id,
156
+ "dataset_url": dataset_url,
157
+ },
158
+ )
159
+
160
+
161
+ @dr_mcp_tool(tags={"predictive", "model", "read", "management", "list"})
162
+ async def list_models(
163
+ *,
164
+ project_id: Annotated[str, "The DataRobot project ID"] | None = None,
165
+ ) -> ToolError | ToolResult:
166
+ """List all models in a project."""
167
+ if not project_id:
168
+ raise ToolError("Project ID must be provided")
137
169
 
138
- Args:
139
- project_id: The ID of the DataRobot project.
140
-
141
- Returns
142
- -------
143
- A string summary of the models in the project.
144
- """
145
170
  client = get_sdk_client()
146
171
  project = client.Project.get(project_id)
147
172
  models = project.get_models()
148
- return json.dumps(models, indent=2, cls=ModelEncoder)
173
+
174
+ return ToolResult(
175
+ content=(
176
+ f"Found {len(models)} models in project {project_id}, here are the details:\n"
177
+ f"{json.dumps(models, indent=2, cls=ModelEncoder)}"
178
+ ),
179
+ structured_content={
180
+ "project_id": project_id,
181
+ "models": [model_to_dict(model) for model in models],
182
+ },
183
+ )
@@ -54,9 +54,9 @@ async def get_project_dataset_by_name(
54
54
  The dataset ID and the dataset type (source or prediction) as a string, or an error message.
55
55
  """
56
56
  if not project_id:
57
- return ToolError("Project ID is required.")
57
+ raise ToolError("Project ID is required.")
58
58
  if not dataset_name:
59
- return ToolError("Dataset name is required.")
59
+ raise ToolError("Dataset name is required.")
60
60
 
61
61
  client = get_sdk_client()
62
62
  project = client.Project.get(project_id)
@@ -19,6 +19,7 @@ import logging
19
19
  from dataclasses import asdict
20
20
  from dataclasses import dataclass
21
21
  from typing import Annotated
22
+ from typing import Any
22
23
 
23
24
  import pandas as pd
24
25
  from fastmcp.exceptions import ToolError
@@ -56,6 +57,36 @@ class DatasetInsight:
56
57
  missing_data_summary: dict[str, float]
57
58
 
58
59
 
60
+ def _get_dataset_or_raise(client: Any, dataset_id: str) -> tuple[Any, pd.DataFrame]:
61
+ """Fetch dataset and return it with its dataframe, with proper error handling.
62
+
63
+ Args:
64
+ client: DataRobot SDK client instance
65
+ dataset_id: The ID of the dataset to fetch
66
+
67
+ Returns
68
+ -------
69
+ Tuple of (dataset object, dataframe)
70
+
71
+ Raises
72
+ ------
73
+ ToolError: If dataset is not found (404) or other error occurs
74
+ """
75
+ try:
76
+ dataset = client.Dataset.get(dataset_id)
77
+ return dataset, dataset.get_as_dataframe()
78
+ except Exception as e:
79
+ error_str = str(e)
80
+ # Check if it's a 404 error (dataset not found)
81
+ if "404" in error_str or "Not Found" in error_str:
82
+ raise ToolError(
83
+ f"Dataset '{dataset_id}' not found. Please verify the dataset ID exists "
84
+ "and you have access to it."
85
+ )
86
+ # For other errors, provide context
87
+ raise ToolError(f"Failed to retrieve dataset '{dataset_id}': {error_str}")
88
+
89
+
59
90
  @dr_mcp_tool(tags={"predictive", "training", "read", "analysis", "dataset"})
60
91
  async def analyze_dataset(
61
92
  *,
@@ -63,11 +94,10 @@ async def analyze_dataset(
63
94
  ) -> ToolError | ToolResult:
64
95
  """Analyze a dataset to understand its structure and potential use cases."""
65
96
  if not dataset_id:
66
- return ToolError("Dataset ID must be provided")
97
+ raise ToolError("Dataset ID must be provided")
67
98
 
68
99
  client = get_sdk_client()
69
- dataset = client.Dataset.get(dataset_id)
70
- df = dataset.get_as_dataframe()
100
+ dataset, df = _get_dataset_or_raise(client, dataset_id)
71
101
 
72
102
  # Analyze dataset structure
73
103
  numerical_cols = df.select_dtypes(include=["int64", "float64"]).columns.tolist()
@@ -116,15 +146,14 @@ async def suggest_use_cases(
116
146
  ) -> ToolError | ToolResult:
117
147
  """Analyze a dataset and suggest potential machine learning use cases."""
118
148
  if not dataset_id:
119
- return ToolError("Dataset ID must be provided")
149
+ raise ToolError("Dataset ID must be provided")
120
150
 
121
151
  client = get_sdk_client()
122
- dataset = client.Dataset.get(dataset_id)
123
- df = dataset.get_as_dataframe()
152
+ dataset, df = _get_dataset_or_raise(client, dataset_id)
124
153
 
125
154
  # Get dataset insights first
126
- insights_json = await analyze_dataset(dataset_id)
127
- insights = json.loads(insights_json)
155
+ insights_result = await analyze_dataset(dataset_id=dataset_id)
156
+ insights = insights_result.structured_content
128
157
 
129
158
  suggestions = []
130
159
  for target_col in insights["potential_targets"]:
@@ -148,15 +177,14 @@ async def get_exploratory_insights(
148
177
  ) -> ToolError | ToolResult:
149
178
  """Generate exploratory data insights for a dataset."""
150
179
  if not dataset_id:
151
- return ToolError("Dataset ID must be provided")
180
+ raise ToolError("Dataset ID must be provided")
152
181
 
153
182
  client = get_sdk_client()
154
- dataset = client.Dataset.get(dataset_id)
155
- df = dataset.get_as_dataframe()
183
+ dataset, df = _get_dataset_or_raise(client, dataset_id)
156
184
 
157
185
  # Get dataset insights first
158
- insights_json = await analyze_dataset(dataset_id)
159
- insights = json.loads(insights_json)
186
+ insights_result = await analyze_dataset(dataset_id=dataset_id)
187
+ insights = insights_result.structured_content
160
188
 
161
189
  eda_insights = {
162
190
  "dataset_summary": {
@@ -481,9 +509,9 @@ async def start_autopilot(
481
509
 
482
510
  if not project_id:
483
511
  if not dataset_url and not dataset_id:
484
- return ToolError("Either dataset_url or dataset_id must be provided")
512
+ raise ToolError("Either dataset_url or dataset_id must be provided")
485
513
  if dataset_url and dataset_id:
486
- return ToolError("Please provide either dataset_url or dataset_id, not both")
514
+ raise ToolError("Please provide either dataset_url or dataset_id, not both")
487
515
 
488
516
  if dataset_url:
489
517
  dataset = client.Dataset.create_from_url(dataset_url)
@@ -497,7 +525,7 @@ async def start_autopilot(
497
525
  project = client.Project.get(project_id)
498
526
 
499
527
  if not target:
500
- return ToolError("Target variable must be specified")
528
+ raise ToolError("Target variable must be specified")
501
529
 
502
530
  try:
503
531
  # Start modeling
@@ -517,7 +545,7 @@ async def start_autopilot(
517
545
  )
518
546
 
519
547
  except Exception as e:
520
- return ToolError(
548
+ raise ToolError(
521
549
  content=json.dumps(
522
550
  {
523
551
  "error": f"Failed to start Autopilot: {str(e)}",
@@ -546,9 +574,9 @@ async def get_model_roc_curve(
546
574
  ) -> ToolError | ToolResult:
547
575
  """Get detailed ROC curve for a specific model."""
548
576
  if not project_id:
549
- return ToolError("Project ID must be provided")
577
+ raise ToolError("Project ID must be provided")
550
578
  if not model_id:
551
- return ToolError("Model ID must be provided")
579
+ raise ToolError("Model ID must be provided")
552
580
 
553
581
  client = get_sdk_client()
554
582
  project = client.Project.get(project_id)
@@ -587,7 +615,7 @@ async def get_model_roc_curve(
587
615
  structured_content={"data": roc_data},
588
616
  )
589
617
  except Exception as e:
590
- return ToolError(f"Failed to get ROC curve: {str(e)}")
618
+ raise ToolError(f"Failed to get ROC curve: {str(e)}")
591
619
 
592
620
 
593
621
  @dr_mcp_tool(tags={"predictive", "training", "read", "model", "evaluation"})
@@ -598,9 +626,9 @@ async def get_model_feature_impact(
598
626
  ) -> ToolError | ToolResult:
599
627
  """Get detailed feature impact for a specific model."""
600
628
  if not project_id:
601
- return ToolError("Project ID must be provided")
629
+ raise ToolError("Project ID must be provided")
602
630
  if not model_id:
603
- return ToolError("Model ID must be provided")
631
+ raise ToolError("Model ID must be provided")
604
632
 
605
633
  client = get_sdk_client()
606
634
  project = client.Project.get(project_id)
@@ -631,9 +659,9 @@ async def get_model_lift_chart(
631
659
  ) -> ToolError | ToolResult:
632
660
  """Get detailed lift chart for a specific model."""
633
661
  if not project_id:
634
- return ToolError("Project ID must be provided")
662
+ raise ToolError("Project ID must be provided")
635
663
  if not model_id:
636
- return ToolError("Model ID must be provided")
664
+ raise ToolError("Model ID must be provided")
637
665
 
638
666
  client = get_sdk_client()
639
667
  project = client.Project.get(project_id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datarobot-genai
3
- Version: 0.2.29
3
+ Version: 0.2.37
4
4
  Summary: Generic helpers for GenAI
5
5
  Project-URL: Homepage, https://github.com/datarobot-oss/datarobot-genai
6
6
  Author: DataRobot, Inc.
@@ -11,7 +11,7 @@ datarobot_genai/core/chat/client.py,sha256=fk8MebXa8_R33VK0_DrXCS0Fgw3wFvPEvsuub
11
11
  datarobot_genai/core/chat/responses.py,sha256=vGxTA433f2AxGVlijV6O4EghyNPJCDmEqpAK2oWnsIs,10583
12
12
  datarobot_genai/core/cli/__init__.py,sha256=B93Yb6VavoZpatrh8ltCL6YglIfR5FHgytXbO9UuxBw,733
13
13
  datarobot_genai/core/cli/agent_environment.py,sha256=BJzQoiDvZF5gW4mFE71U0yeg-l72C--kxiE-fv6W194,1662
14
- datarobot_genai/core/cli/agent_kernel.py,sha256=3XX58DQ6XPpWB_tn5m3iGb3XTfhZf5X3W9tc6ADieU4,7790
14
+ datarobot_genai/core/cli/agent_kernel.py,sha256=Pan1l4tFwQBiJCK0u5jjJmxsyADVcicmiCdt5vVm6CI,7873
15
15
  datarobot_genai/core/mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  datarobot_genai/core/mcp/common.py,sha256=Y8SjuquUODKEfI7T9X-QuTMKdIlpCWFI1b3xs6tmHFA,7812
17
17
  datarobot_genai/core/utils/__init__.py,sha256=VxtRUz6iwb04eFQQy0zqTNXLAkYpPXcJxVoKV0nOdXk,59
@@ -22,7 +22,7 @@ datarobot_genai/crewai/agent.py,sha256=vp8_2LExpeLls7Fpzo0R6ud5I6Ryfu3n3oVTN4Yyi
22
22
  datarobot_genai/crewai/base.py,sha256=JLljEN7sj8zaH8OamYoevFBZzza5BjZ4f0CGHRp2jUU,6447
23
23
  datarobot_genai/crewai/events.py,sha256=K67bO1zwPrxmppz2wh8dFGNbVebyWGXAMD7oodFE2sQ,5462
24
24
  datarobot_genai/crewai/mcp.py,sha256=AJTrs-8KdiRSjRECfBT1lJOsszWMoFoN9NIa1p5_wsM,2115
25
- datarobot_genai/drmcp/__init__.py,sha256=JE83bfpGU7v77VzrDdlb0l8seM5OwUsUbaQErJ2eisc,2983
25
+ datarobot_genai/drmcp/__init__.py,sha256=WyKW7p77kS63EsmuW8cnZpqgcxCzIv1TmRPo2qo-Z8A,2988
26
26
  datarobot_genai/drmcp/server.py,sha256=KE4kjS5f9bfdYftG14HBHrfvxDfCD4pwCXePfvl1OvU,724
27
27
  datarobot_genai/drmcp/core/__init__.py,sha256=y4yapzp3KnFMzSR6HlNDS4uSuyNT7I1iPBvaCLsS0sU,577
28
28
  datarobot_genai/drmcp/core/auth.py,sha256=E-5wrGbBFEBlD5377g6Exddrc7HsazamwX8tWr2RLXY,5815
@@ -33,8 +33,8 @@ datarobot_genai/drmcp/core/constants.py,sha256=lUwoW_PTrbaBGqRJifKqCn3EoFacoEgdO
33
33
  datarobot_genai/drmcp/core/credentials.py,sha256=PYEUDNMVw1BoMzZKLkPVTypNkVevEPtmk3scKnE-zYg,6706
34
34
  datarobot_genai/drmcp/core/dr_mcp_server.py,sha256=czcjbwhZAeW9EtG_Bys0GARPOuQulstkiU7FG48Q9bg,14118
35
35
  datarobot_genai/drmcp/core/dr_mcp_server_logo.py,sha256=hib-nfR1SNTW6CnpFsFCkL9H_OMwa4YYyinV7VNOuLk,4708
36
- datarobot_genai/drmcp/core/exceptions.py,sha256=eqsGI-lxybgvWL5w4BFhbm3XzH1eU5tetwjnhJxelpc,905
37
- datarobot_genai/drmcp/core/logging.py,sha256=Y_hig4eBWiXGaVV7B_3wBcaYVRNH4ydptbEQhrP9-mY,3414
36
+ datarobot_genai/drmcp/core/exceptions.py,sha256=9zoNh5ph6QihWIYuw37ljZ73_iUfy38YVYyFSnEwivc,839
37
+ datarobot_genai/drmcp/core/logging.py,sha256=rnUkws0vIDy_uLevwNj-wgA9uijW1Go774JPCrG0Yfw,3423
38
38
  datarobot_genai/drmcp/core/mcp_instance.py,sha256=nt4gOlAQklMcqmohRIKovYcyhgLdb08NHMo28DBYmOk,18362
39
39
  datarobot_genai/drmcp/core/routes.py,sha256=dqE2M0UzAyyN9vQjlyTjYW4rpju3LT039po5weuO__I,17936
40
40
  datarobot_genai/drmcp/core/routes_utils.py,sha256=vSseXWlplMSnRgoJgtP_rHxWSAVYcx_tpTv4lyTpQoc,944
@@ -68,37 +68,41 @@ datarobot_genai/drmcp/core/memory_management/memory_tools.py,sha256=AxzpwOlldmhh
68
68
  datarobot_genai/drmcp/test_utils/__init__.py,sha256=y4yapzp3KnFMzSR6HlNDS4uSuyNT7I1iPBvaCLsS0sU,577
69
69
  datarobot_genai/drmcp/test_utils/elicitation_test_tool.py,sha256=UVKwy39nl3XcVAh6IATcN-cWL2bfrprgRQ7fbK82jTI,3287
70
70
  datarobot_genai/drmcp/test_utils/integration_mcp_server.py,sha256=YSk19tbaka_0ziqi7LoXie4SJs-cvi9-H00Go0ZtQWE,3575
71
- datarobot_genai/drmcp/test_utils/mcp_utils_ete.py,sha256=46rH0fYYmUj7ygf968iRbdSp5u95v23BEw3Ts_c431Y,4788
71
+ datarobot_genai/drmcp/test_utils/mcp_utils_ete.py,sha256=mM52xKB-1fy_o3g6k4fNtFU2L_0jEi6GY-czdR3R5HE,5491
72
72
  datarobot_genai/drmcp/test_utils/mcp_utils_integration.py,sha256=sHA_BWtpgIAFp9IXiNkUeBartBMjLAauqkV9bYtCr-g,3874
73
- datarobot_genai/drmcp/test_utils/openai_llm_mcp_client.py,sha256=YgyqHK09MB-PBaqT34heqvmvYYFtLpzzSJt7xuTJmDg,11224
74
- datarobot_genai/drmcp/test_utils/test_interactive.py,sha256=guXvR8q2H6VUdmvIjEJcElQJCC6lQ-oTrzbD2EkHeCs,8025
75
- datarobot_genai/drmcp/test_utils/tool_base_ete.py,sha256=3yMfOsz3LdHYywuE5BhdJDpTUowx37HsFSsMdBTxA80,9337
76
- datarobot_genai/drmcp/test_utils/utils.py,sha256=esGKFv8aO31-Qg3owayeWp32BYe1CdYOEutjjdbweCw,3048
73
+ datarobot_genai/drmcp/test_utils/test_interactive.py,sha256=KAScFT65GUkOxuiiBcjli8HHvV1NusVN01nOib3xVCc,7939
74
+ datarobot_genai/drmcp/test_utils/tool_base_ete.py,sha256=2RvVmwHYczl7F6qZHkKYiI77IoL-PaMT3y59t0aQtTE,9328
75
+ datarobot_genai/drmcp/test_utils/utils.py,sha256=JF2W9J4Q8pCqro7dj_bHObHNP7dfybDXesTLFOUsIVM,3039
76
+ datarobot_genai/drmcp/test_utils/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
+ datarobot_genai/drmcp/test_utils/clients/anthropic.py,sha256=qFhLvLZHMpZa2tZwI8pZQaaeG1lsM56VaONt6a9VU8c,2333
78
+ datarobot_genai/drmcp/test_utils/clients/base.py,sha256=WoPdddYmmXGylEuKRtKHPfprcHMjbHqPB9PwzWmORV4,10637
79
+ datarobot_genai/drmcp/test_utils/clients/dr_gateway.py,sha256=qlx0WxEOtTkxt9PiCxgWAp02k5jyUgXcKb9AwCGw6cw,2150
80
+ datarobot_genai/drmcp/test_utils/clients/openai.py,sha256=tyIibvjtFp7u2BoHJqwIRlHn9UPtysKOgStoA9SZUYs,2566
77
81
  datarobot_genai/drmcp/tools/__init__.py,sha256=0kq9vMkF7EBsS6lkEdiLibmUrghTQqosHbZ5k-V9a5g,578
78
82
  datarobot_genai/drmcp/tools/clients/__init__.py,sha256=0kq9vMkF7EBsS6lkEdiLibmUrghTQqosHbZ5k-V9a5g,578
79
83
  datarobot_genai/drmcp/tools/clients/atlassian.py,sha256=__M_uz7FrcbKCYRzeMn24DCEYD6OmFx_LuywHCxgXsA,6472
80
84
  datarobot_genai/drmcp/tools/clients/confluence.py,sha256=h_G0By_kDnJeWDT_d-IREsaZ5-0xB5GoLXOqblYP5MA,20706
81
- datarobot_genai/drmcp/tools/clients/gdrive.py,sha256=8GztWTdpJ7Ir3NIvIoOHPzDscoR1Ui7Ct2IiKmuUzIc,26012
85
+ datarobot_genai/drmcp/tools/clients/gdrive.py,sha256=RK4IISpYb99aK6WgDthesDoglaZxwGpG_PPAAe6xsVM,33064
82
86
  datarobot_genai/drmcp/tools/clients/jira.py,sha256=Rm91JAyrNIqxu66-9rU1YqoRXVnWbEy-Ahvy6f6HlVg,9823
83
- datarobot_genai/drmcp/tools/clients/microsoft_graph.py,sha256=PASGThDPE8zkBZqach8lurJL1y47DWUPLwvf9N6uLGM,19234
87
+ datarobot_genai/drmcp/tools/clients/microsoft_graph.py,sha256=-g0EhaBVElKbujaO2cHdgc86hwFEkkyEyZVAw8pq7yM,24468
84
88
  datarobot_genai/drmcp/tools/clients/s3.py,sha256=GmwzvurFdNfvxOooA8g5S4osRysHYU0S9ypg_177Glg,953
85
89
  datarobot_genai/drmcp/tools/confluence/__init__.py,sha256=0kq9vMkF7EBsS6lkEdiLibmUrghTQqosHbZ5k-V9a5g,578
86
90
  datarobot_genai/drmcp/tools/confluence/tools.py,sha256=_-ws65WLK8KZP_mKkf4yJ7ZunR8qdyoiMwHQX47MSMw,12362
87
91
  datarobot_genai/drmcp/tools/gdrive/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
- datarobot_genai/drmcp/tools/gdrive/tools.py,sha256=G8LlnGEINZqV83Q-b3ZliWkDjouhbozDam3w6GfA7s0,10711
92
+ datarobot_genai/drmcp/tools/gdrive/tools.py,sha256=7bNrp7E3opKwsBDYfLIOsOGfPXW-Ae9KvcimEzetR0A,17631
89
93
  datarobot_genai/drmcp/tools/jira/__init__.py,sha256=0kq9vMkF7EBsS6lkEdiLibmUrghTQqosHbZ5k-V9a5g,578
90
94
  datarobot_genai/drmcp/tools/jira/tools.py,sha256=dfkqTU2HH-7n44hX80ODFacKq0p0LOchFcZtIIKFNMM,9687
91
95
  datarobot_genai/drmcp/tools/microsoft_graph/__init__.py,sha256=CuOaMt1AJo7cHx_GuhO3s_aqxZas_wlDsoBorBsvbeU,577
92
- datarobot_genai/drmcp/tools/microsoft_graph/tools.py,sha256=zJ-UA1TMhPOYcExvgWv0YBjDsSIDPA-U1SEbBrVfAc8,7744
96
+ datarobot_genai/drmcp/tools/microsoft_graph/tools.py,sha256=cNctozv_4lRC5Kva3D2j4taZfeQHDE6LTAjcmeQXwWA,10446
93
97
  datarobot_genai/drmcp/tools/predictive/__init__.py,sha256=WuOHlNNEpEmcF7gVnhckruJRKU2qtmJLE3E7zoCGLDo,1030
94
- datarobot_genai/drmcp/tools/predictive/data.py,sha256=sSFAmO6x0DSuolw8urhMaOj5PwfUH29oc2mEOZI3YU4,4631
95
- datarobot_genai/drmcp/tools/predictive/deployment.py,sha256=lm02Ayuo11L1hP41fgi3QpR1Eyty-Wc16rM0c8SgliM,3277
98
+ datarobot_genai/drmcp/tools/predictive/data.py,sha256=VbGs8ERP8vNFtTTryGhI61JItNVaJsx1gxpRX1ZFZcg,4626
99
+ datarobot_genai/drmcp/tools/predictive/deployment.py,sha256=Pc6lz9V2JOw3Ufw-SsGAhMKf6-YhvbjGoNLRFOIcSSY,3670
96
100
  datarobot_genai/drmcp/tools/predictive/deployment_info.py,sha256=BGEF_dmbxOBJR0n1Tt9TO2-iNTQSBTr-oQUyaxLZ0ZI,15297
97
- datarobot_genai/drmcp/tools/predictive/model.py,sha256=Yih5-KedJ-1yupPLXCJsCXOdyWWi9pRvgapXDlgXWJA,4891
101
+ datarobot_genai/drmcp/tools/predictive/model.py,sha256=BVxOMHh3--liwBU4VB1OWRrqkhJ4y_Rq053f7y94TF8,6276
98
102
  datarobot_genai/drmcp/tools/predictive/predict.py,sha256=Qoob2_t2crfWtyPzkXMRz2ITZumnczU6Dq4C7q9RBMI,9370
99
103
  datarobot_genai/drmcp/tools/predictive/predict_realtime.py,sha256=urq6rPyZFsAP-bPyclSNzrkvb6FTamdlFau8q0IWWJ0,13472
100
- datarobot_genai/drmcp/tools/predictive/project.py,sha256=xC52UdYvuFeNZC7Y5MfXcvzTL70WwAacQXESr6rqN6s,3255
101
- datarobot_genai/drmcp/tools/predictive/training.py,sha256=LzMxbBT8wxKYDrRlVElfmTUrzpmGvwrR-mTGf6YUnIA,23998
104
+ datarobot_genai/drmcp/tools/predictive/project.py,sha256=Mzf7rQogBV6h1-MWQYTwtDHOsMWfjOyyJpSYmmvNNuc,3253
105
+ datarobot_genai/drmcp/tools/predictive/training.py,sha256=jeZGPWJ69PPOd2MhUbACgbllQ0CK7Kz-hNl596mJujQ,25021
102
106
  datarobot_genai/langgraph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
107
  datarobot_genai/langgraph/agent.py,sha256=DRnywmS9KDywyChtuIZZwNKbJs8BpC259EG_kxYbiQ8,15828
104
108
  datarobot_genai/langgraph/mcp.py,sha256=iA2_j46mZAaNaL7ntXT-LW6C-NMJkzr3VfKDDfe7mh8,2851
@@ -113,9 +117,9 @@ datarobot_genai/nat/datarobot_llm_clients.py,sha256=-_q_KlKOVQecIYJd8YRiYnS4ZNaz
113
117
  datarobot_genai/nat/datarobot_llm_providers.py,sha256=aDoQcTeGI-odqydPXEX9OGGNFbzAtpqzTvHHEkmJuEQ,4963
114
118
  datarobot_genai/nat/datarobot_mcp_client.py,sha256=jL8sXb8g4gvt0VYgB2tfMGsMjpB1GV2XIbN0iv_LxVU,10701
115
119
  datarobot_genai/nat/helpers.py,sha256=Q7E3ADZdtFfS8E6OQPyw2wgA6laQ58N3bhLj5CBWwJs,3265
116
- datarobot_genai-0.2.29.dist-info/METADATA,sha256=lFk85PaEbHw_waws2INyPqFxo92s43jFp4vFyz8HHdc,6301
117
- datarobot_genai-0.2.29.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
118
- datarobot_genai-0.2.29.dist-info/entry_points.txt,sha256=jEW3WxDZ8XIK9-ISmTyt5DbmBb047rFlzQuhY09rGrM,284
119
- datarobot_genai-0.2.29.dist-info/licenses/AUTHORS,sha256=isJGUXdjq1U7XZ_B_9AH8Qf0u4eX0XyQifJZ_Sxm4sA,80
120
- datarobot_genai-0.2.29.dist-info/licenses/LICENSE,sha256=U2_VkLIktQoa60Nf6Tbt7E4RMlfhFSjWjcJJfVC-YCE,11341
121
- datarobot_genai-0.2.29.dist-info/RECORD,,
120
+ datarobot_genai-0.2.37.dist-info/METADATA,sha256=JFxYZKfbfrbePIywe1WQ1ZfjQ5W9HAq_GNYAuXxTcB8,6301
121
+ datarobot_genai-0.2.37.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
122
+ datarobot_genai-0.2.37.dist-info/entry_points.txt,sha256=jEW3WxDZ8XIK9-ISmTyt5DbmBb047rFlzQuhY09rGrM,284
123
+ datarobot_genai-0.2.37.dist-info/licenses/AUTHORS,sha256=isJGUXdjq1U7XZ_B_9AH8Qf0u4eX0XyQifJZ_Sxm4sA,80
124
+ datarobot_genai-0.2.37.dist-info/licenses/LICENSE,sha256=U2_VkLIktQoa60Nf6Tbt7E4RMlfhFSjWjcJJfVC-YCE,11341
125
+ datarobot_genai-0.2.37.dist-info/RECORD,,