datarobot-genai 0.1.70__py3-none-any.whl → 0.1.72__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.
@@ -38,7 +38,7 @@ def _apply_green(text: str) -> str:
38
38
  return "\n".join(colored_lines)
39
39
 
40
40
 
41
- DR_LOGO_ASCII = _apply_green("""\
41
+ DR_LOGO_ASCII = _apply_green(r"""
42
42
  ____ _ ____ _ _
43
43
  | _ \ __ _| |_ __ _| _ \ ___ | |__ ___ | |_
44
44
  | | | |/ _` | __/ _` | |_) / _ \| '_ \ / _ \| __|
@@ -84,21 +84,38 @@ class LangGraphAgent(BaseAgent[BaseTool], abc.ABC):
84
84
  async def wrapped_generator() -> AsyncGenerator[
85
85
  tuple[str, Any | None, UsageMetrics], None
86
86
  ]:
87
- async with mcp_tools_context(
88
- authorization_context=self._authorization_context,
89
- forwarded_headers=self.forwarded_headers,
90
- ) as mcp_tools:
91
- self.set_mcp_tools(mcp_tools)
92
- result = await self._invoke(completion_create_params)
93
-
94
- # Yield all items from the result generator
95
- # The context will be closed when this generator is exhausted
96
- # Cast to async generator since we know stream=True means it's a generator
97
- result_generator = cast(
98
- AsyncGenerator[tuple[str, Any | None, UsageMetrics], None], result
99
- )
100
- async for item in result_generator:
101
- yield item
87
+ try:
88
+ async with mcp_tools_context(
89
+ authorization_context=self._authorization_context,
90
+ forwarded_headers=self.forwarded_headers,
91
+ ) as mcp_tools:
92
+ self.set_mcp_tools(mcp_tools)
93
+ result = await self._invoke(completion_create_params)
94
+
95
+ # Yield all items from the result generator
96
+ # The context will be closed when this generator is exhausted
97
+ # Cast to async generator since we know stream=True means it's a generator
98
+ result_generator = cast(
99
+ AsyncGenerator[tuple[str, Any | None, UsageMetrics], None], result
100
+ )
101
+ async for item in result_generator:
102
+ yield item
103
+ except RuntimeError as e:
104
+ error_message = str(e).lower()
105
+ if "different task" in error_message and "cancel scope" in error_message:
106
+ # Due to anyio task group constraints when consuming async generators
107
+ # across task boundaries, we cannot always clean up properly.
108
+ # The underlying HTTP client/connection pool should handle resource cleanup
109
+ # via timeouts and connection pooling, but this
110
+ # may lead to delayed resource release.
111
+ logger.debug(
112
+ "MCP context cleanup attempted in different task. "
113
+ "This is a limitation when consuming async generators "
114
+ "across task boundaries."
115
+ )
116
+ else:
117
+ # Re-raise if it's a different RuntimeError
118
+ raise
102
119
 
103
120
  return wrapped_generator()
104
121
  else:
@@ -23,6 +23,7 @@ from nat.builder.builder import Builder
23
23
  from nat.builder.framework_enum import LLMFrameworkEnum
24
24
  from nat.cli.register_workflow import register_llm_client
25
25
 
26
+ from ..nat.datarobot_llm_providers import DataRobotLLMComponentModelConfig
26
27
  from ..nat.datarobot_llm_providers import DataRobotLLMDeploymentModelConfig
27
28
  from ..nat.datarobot_llm_providers import DataRobotLLMGatewayModelConfig
28
29
  from ..nat.datarobot_llm_providers import DataRobotNIMModelConfig
@@ -75,6 +76,7 @@ async def datarobot_llm_gateway_langchain(
75
76
  config = llm_config.model_dump(exclude={"type", "thinking"}, by_alias=True, exclude_none=True)
76
77
  config["base_url"] = config["base_url"] + "/genai/llmgw"
77
78
  config["stream_options"] = {"include_usage": True}
79
+ config["model"] = config["model"].removeprefix("datarobot/")
78
80
  yield DataRobotChatOpenAI(**config)
79
81
 
80
82
 
@@ -85,7 +87,8 @@ async def datarobot_llm_gateway_crewai(
85
87
  llm_config: DataRobotLLMGatewayModelConfig, builder: Builder
86
88
  ) -> AsyncGenerator[LLM]:
87
89
  config = llm_config.model_dump(exclude={"type", "thinking"}, by_alias=True, exclude_none=True)
88
- config["model"] = "datarobot/" + config["model"]
90
+ if not config["model"].startswith("datarobot/"):
91
+ config["model"] = "datarobot/" + config["model"]
89
92
  config["base_url"] = config["base_url"].removesuffix("/api/v2")
90
93
  yield LLM(**config)
91
94
 
@@ -97,7 +100,8 @@ async def datarobot_llm_gateway_llamaindex(
97
100
  llm_config: DataRobotLLMGatewayModelConfig, builder: Builder
98
101
  ) -> AsyncGenerator[LLM]:
99
102
  config = llm_config.model_dump(exclude={"type", "thinking"}, by_alias=True, exclude_none=True)
100
- config["model"] = "datarobot/" + config["model"]
103
+ if not config["model"].startswith("datarobot/"):
104
+ config["model"] = "datarobot/" + config["model"]
101
105
  config["api_base"] = config.pop("base_url").removesuffix("/api/v2")
102
106
  yield DataRobotLiteLLM(**config)
103
107
 
@@ -109,11 +113,12 @@ async def datarobot_llm_deployment_langchain(
109
113
  llm_config: DataRobotLLMDeploymentModelConfig, builder: Builder
110
114
  ) -> AsyncGenerator[ChatOpenAI]:
111
115
  config = llm_config.model_dump(
112
- exclude={"type", "thinking", "datarobot_endpoint", "llm_deployment_id"},
116
+ exclude={"type", "thinking"},
113
117
  by_alias=True,
114
118
  exclude_none=True,
115
119
  )
116
120
  config["stream_options"] = {"include_usage": True}
121
+ config["model"] = config["model"].removeprefix("datarobot/")
117
122
  yield DataRobotChatOpenAI(**config)
118
123
 
119
124
 
@@ -128,7 +133,8 @@ async def datarobot_llm_deployment_crewai(
128
133
  by_alias=True,
129
134
  exclude_none=True,
130
135
  )
131
- config["model"] = "datarobot/" + config["model"]
136
+ if not config["model"].startswith("datarobot/"):
137
+ config["model"] = "datarobot/" + config["model"]
132
138
  config["api_base"] = config.pop("base_url") + "/chat/completions"
133
139
  yield LLM(**config)
134
140
 
@@ -144,7 +150,8 @@ async def datarobot_llm_deployment_llamaindex(
144
150
  by_alias=True,
145
151
  exclude_none=True,
146
152
  )
147
- config["model"] = "datarobot/" + config["model"]
153
+ if not config["model"].startswith("datarobot/"):
154
+ config["model"] = "datarobot/" + config["model"]
148
155
  config["api_base"] = config.pop("base_url") + "/chat/completions"
149
156
  yield DataRobotLiteLLM(**config)
150
157
 
@@ -159,6 +166,7 @@ async def datarobot_nim_langchain(
159
166
  exclude_none=True,
160
167
  )
161
168
  config["stream_options"] = {"include_usage": True}
169
+ config["model"] = config["model"].removeprefix("datarobot/")
162
170
  yield DataRobotChatOpenAI(**config)
163
171
 
164
172
 
@@ -171,7 +179,8 @@ async def datarobot_nim_crewai(
171
179
  by_alias=True,
172
180
  exclude_none=True,
173
181
  )
174
- config["model"] = "datarobot/" + config["model"]
182
+ if not config["model"].startswith("datarobot/"):
183
+ config["model"] = "datarobot/" + config["model"]
175
184
  config["api_base"] = config.pop("base_url") + "/chat/completions"
176
185
  yield LLM(**config)
177
186
 
@@ -185,6 +194,56 @@ async def datarobot_nim_llamaindex(
185
194
  by_alias=True,
186
195
  exclude_none=True,
187
196
  )
188
- config["model"] = "datarobot/" + config["model"]
197
+ if not config["model"].startswith("datarobot/"):
198
+ config["model"] = "datarobot/" + config["model"]
189
199
  config["api_base"] = config.pop("base_url") + "/chat/completions"
190
200
  yield DataRobotLiteLLM(**config)
201
+
202
+
203
+ @register_llm_client(
204
+ config_type=DataRobotLLMComponentModelConfig, wrapper_type=LLMFrameworkEnum.LANGCHAIN
205
+ )
206
+ async def datarobot_llm_component_langchain(
207
+ llm_config: DataRobotLLMComponentModelConfig, builder: Builder
208
+ ) -> AsyncGenerator[ChatOpenAI]:
209
+ config = llm_config.model_dump(exclude={"type", "thinking"}, by_alias=True, exclude_none=True)
210
+ if config["use_datarobot_llm_gateway"]:
211
+ config["base_url"] = config["base_url"] + "/genai/llmgw"
212
+ config["stream_options"] = {"include_usage": True}
213
+ config["model"] = config["model"].removeprefix("datarobot/")
214
+ config.pop("use_datarobot_llm_gateway")
215
+ yield DataRobotChatOpenAI(**config)
216
+
217
+
218
+ @register_llm_client(
219
+ config_type=DataRobotLLMComponentModelConfig, wrapper_type=LLMFrameworkEnum.CREWAI
220
+ )
221
+ async def datarobot_llm_component_crewai(
222
+ llm_config: DataRobotLLMComponentModelConfig, builder: Builder
223
+ ) -> AsyncGenerator[LLM]:
224
+ config = llm_config.model_dump(exclude={"type", "thinking"}, by_alias=True, exclude_none=True)
225
+ if not config["model"].startswith("datarobot/"):
226
+ config["model"] = "datarobot/" + config["model"]
227
+ if config["use_datarobot_llm_gateway"]:
228
+ config["base_url"] = config["base_url"].removesuffix("/api/v2")
229
+ else:
230
+ config["api_base"] = config.pop("base_url") + "/chat/completions"
231
+ config.pop("use_datarobot_llm_gateway")
232
+ yield LLM(**config)
233
+
234
+
235
+ @register_llm_client(
236
+ config_type=DataRobotLLMComponentModelConfig, wrapper_type=LLMFrameworkEnum.LLAMA_INDEX
237
+ )
238
+ async def datarobot_llm_component_llamaindex(
239
+ llm_config: DataRobotLLMComponentModelConfig, builder: Builder
240
+ ) -> AsyncGenerator[LLM]:
241
+ config = llm_config.model_dump(exclude={"type", "thinking"}, by_alias=True, exclude_none=True)
242
+ if not config["model"].startswith("datarobot/"):
243
+ config["model"] = "datarobot/" + config["model"]
244
+ if config["use_datarobot_llm_gateway"]:
245
+ config["api_base"] = config.pop("base_url").removesuffix("/api/v2")
246
+ else:
247
+ config["api_base"] = config.pop("base_url") + "/chat/completions"
248
+ config.pop("use_datarobot_llm_gateway")
249
+ yield DataRobotLiteLLM(**config)
@@ -32,11 +32,43 @@ class Config(DataRobotAppFrameworkBaseSettings):
32
32
  datarobot_api_token: str | None = None
33
33
  llm_deployment_id: str | None = None
34
34
  nim_deployment_id: str | None = None
35
+ use_datarobot_llm_gateway: bool = False
36
+ llm_default_model: str | None = None
35
37
 
36
38
 
37
39
  config = Config()
38
40
 
39
41
 
42
+ class DataRobotLLMComponentModelConfig(OpenAIModelConfig, name="datarobot-llm-component"): # type: ignore[call-arg]
43
+ """A DataRobot LLM provider to be used with an LLM client."""
44
+
45
+ api_key: str | None = Field(
46
+ default=config.datarobot_api_token, description="DataRobot API key."
47
+ )
48
+ base_url: str | None = Field(
49
+ default=config.datarobot_endpoint.rstrip("/")
50
+ if config.use_datarobot_llm_gateway
51
+ else config.datarobot_endpoint + f"/deployments/{config.llm_deployment_id}",
52
+ description="DataRobot LLM URL.",
53
+ )
54
+ model_name: str = Field(
55
+ validation_alias=AliasChoices("model_name", "model"),
56
+ serialization_alias="model",
57
+ description="The model name.",
58
+ default=config.llm_default_model or "datarobot-deployed-llm",
59
+ )
60
+ use_datarobot_llm_gateway: bool = config.use_datarobot_llm_gateway
61
+
62
+
63
+ @register_llm_provider(config_type=DataRobotLLMComponentModelConfig)
64
+ async def datarobot_llm_component(
65
+ config: DataRobotLLMComponentModelConfig, _builder: Builder
66
+ ) -> LLMProviderInfo:
67
+ yield LLMProviderInfo(
68
+ config=config, description="DataRobot LLM Component for use with an LLM client."
69
+ )
70
+
71
+
40
72
  class DataRobotLLMGatewayModelConfig(OpenAIModelConfig, name="datarobot-llm-gateway"): # type: ignore[call-arg]
41
73
  """A DataRobot LLM provider to be used with an LLM client."""
42
74
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datarobot-genai
3
- Version: 0.1.70
3
+ Version: 0.1.72
4
4
  Summary: Generic helpers for GenAI
5
5
  Project-URL: Homepage, https://github.com/datarobot-oss/datarobot-genai
6
6
  Author: DataRobot, Inc.
@@ -23,7 +23,7 @@ Requires-Dist: ragas<0.4.0,>=0.3.8
23
23
  Requires-Dist: requests<3.0.0,>=2.32.4
24
24
  Provides-Extra: crewai
25
25
  Requires-Dist: crewai-tools[mcp]<0.77.0,>=0.69.0; extra == 'crewai'
26
- Requires-Dist: crewai<1.0.0,>=0.193.2; extra == 'crewai'
26
+ Requires-Dist: crewai>=1.1.0; extra == 'crewai'
27
27
  Requires-Dist: opentelemetry-instrumentation-crewai<1.0.0,>=0.40.5; extra == 'crewai'
28
28
  Requires-Dist: pybase64<2.0.0,>=1.4.2; extra == 'crewai'
29
29
  Provides-Extra: drmcp
@@ -56,8 +56,8 @@ Requires-Dist: llama-index<0.14.0,>=0.13.6; extra == 'llamaindex'
56
56
  Requires-Dist: opentelemetry-instrumentation-llamaindex<1.0.0,>=0.40.5; extra == 'llamaindex'
57
57
  Requires-Dist: pypdf<7.0.0,>=6.0.0; extra == 'llamaindex'
58
58
  Provides-Extra: nat
59
+ Requires-Dist: crewai>=1.1.0; (python_version >= '3.11') and extra == 'nat'
59
60
  Requires-Dist: llama-index-llms-litellm<0.7.0,>=0.4.1; extra == 'nat'
60
- Requires-Dist: nvidia-nat-crewai==1.3.0; (python_version >= '3.11') and extra == 'nat'
61
61
  Requires-Dist: nvidia-nat-langchain==1.3.0; (python_version >= '3.11') and extra == 'nat'
62
62
  Requires-Dist: nvidia-nat-opentelemetry==1.3.0; (python_version >= '3.11') and extra == 'nat'
63
63
  Requires-Dist: nvidia-nat==1.3.0; (python_version >= '3.11') and extra == 'nat'
@@ -32,7 +32,7 @@ datarobot_genai/drmcp/core/config_utils.py,sha256=U-aieWw7MyP03cGDFIp97JH99ZUfr3
32
32
  datarobot_genai/drmcp/core/constants.py,sha256=lUwoW_PTrbaBGqRJifKqCn3EoFacoEgdO-CpoFVrUoU,739
33
33
  datarobot_genai/drmcp/core/credentials.py,sha256=PYEUDNMVw1BoMzZKLkPVTypNkVevEPtmk3scKnE-zYg,6706
34
34
  datarobot_genai/drmcp/core/dr_mcp_server.py,sha256=7mu5UXHQmKNbIpNoQE0lPJaUI7AZa03avfHZRRtpjNI,12841
35
- datarobot_genai/drmcp/core/dr_mcp_server_logo.py,sha256=Npkn-cPPBv261kiiaNwfeS_y4a2WXlbiMS2dP80uDj8,4708
35
+ datarobot_genai/drmcp/core/dr_mcp_server_logo.py,sha256=hib-nfR1SNTW6CnpFsFCkL9H_OMwa4YYyinV7VNOuLk,4708
36
36
  datarobot_genai/drmcp/core/exceptions.py,sha256=eqsGI-lxybgvWL5w4BFhbm3XzH1eU5tetwjnhJxelpc,905
37
37
  datarobot_genai/drmcp/core/logging.py,sha256=Y_hig4eBWiXGaVV7B_3wBcaYVRNH4ydptbEQhrP9-mY,3414
38
38
  datarobot_genai/drmcp/core/mcp_instance.py,sha256=wMsP39xqTmNBYqd49olEQb5UHTSsxj6BOIoIElorRB0,19235
@@ -83,7 +83,7 @@ datarobot_genai/drmcp/tools/predictive/predict_realtime.py,sha256=t7f28y_ealZoA6
83
83
  datarobot_genai/drmcp/tools/predictive/project.py,sha256=KaMDAvJY4s12j_4ybA7-KcCS1yMOj-KPIKNBgCSE2iM,2536
84
84
  datarobot_genai/drmcp/tools/predictive/training.py,sha256=kxeDVLqUh9ajDk8wK7CZRRydDK8UNuTVZCB3huUihF8,23660
85
85
  datarobot_genai/langgraph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
- datarobot_genai/langgraph/agent.py,sha256=IcNawsi2Avsw3usTQ6Z8Ku0JdCsK3sYa2CQMhUe2Kk4,9859
86
+ datarobot_genai/langgraph/agent.py,sha256=P_vqNO-gFOLREHh8NdUXwc9cRlz6dWUY3B_z0r2vpQQ,10945
87
87
  datarobot_genai/langgraph/mcp.py,sha256=iA2_j46mZAaNaL7ntXT-LW6C-NMJkzr3VfKDDfe7mh8,2851
88
88
  datarobot_genai/llama_index/__init__.py,sha256=JEMkLQLuP8n14kNE3bZ2j08NdajnkJMfYjDQYqj7C0c,407
89
89
  datarobot_genai/llama_index/agent.py,sha256=V6ZsD9GcBDJS-RJo1tJtIHhyW69_78gM6_fOHFV-Piw,1829
@@ -91,11 +91,11 @@ datarobot_genai/llama_index/base.py,sha256=ovcQQtC-djD_hcLrWdn93jg23AmD6NBEj7xtw
91
91
  datarobot_genai/llama_index/mcp.py,sha256=leXqF1C4zhuYEKFwNEfZHY4dsUuGZk3W7KArY-zxVL8,2645
92
92
  datarobot_genai/nat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
93
  datarobot_genai/nat/agent.py,sha256=siBLDWAff2-JwZ8Q3iNpM_e4_IoSwG9IvY0hyEjNenw,10292
94
- datarobot_genai/nat/datarobot_llm_clients.py,sha256=IZq_kooUL8QyDTkpEreszLQk9vCzg6-FbTjIkXR9wc0,7203
95
- datarobot_genai/nat/datarobot_llm_providers.py,sha256=lOVaL_0Fl6-7GFYl3HmfqttqKpKt-2w8o92P3T7B6cU,3683
96
- datarobot_genai-0.1.70.dist-info/METADATA,sha256=Dwm4E8vbAhiaRmO-TcfHOgti00CJP7GyTsKTn5efiew,5918
97
- datarobot_genai-0.1.70.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
98
- datarobot_genai-0.1.70.dist-info/entry_points.txt,sha256=CZhmZcSyt_RBltgLN_b9xasJD6J5SaDc_z7K0wuOY9Y,150
99
- datarobot_genai-0.1.70.dist-info/licenses/AUTHORS,sha256=isJGUXdjq1U7XZ_B_9AH8Qf0u4eX0XyQifJZ_Sxm4sA,80
100
- datarobot_genai-0.1.70.dist-info/licenses/LICENSE,sha256=U2_VkLIktQoa60Nf6Tbt7E4RMlfhFSjWjcJJfVC-YCE,11341
101
- datarobot_genai-0.1.70.dist-info/RECORD,,
94
+ datarobot_genai/nat/datarobot_llm_clients.py,sha256=STzAZ4OF8U-Y_cUTywxmKBGVotwsnbGP6vTojnu6q0g,9921
95
+ datarobot_genai/nat/datarobot_llm_providers.py,sha256=aDoQcTeGI-odqydPXEX9OGGNFbzAtpqzTvHHEkmJuEQ,4963
96
+ datarobot_genai-0.1.72.dist-info/METADATA,sha256=NKR8UuyddfZt7nhQslbYM-2G7pnebcE47j3_kI7YjBQ,5898
97
+ datarobot_genai-0.1.72.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
98
+ datarobot_genai-0.1.72.dist-info/entry_points.txt,sha256=CZhmZcSyt_RBltgLN_b9xasJD6J5SaDc_z7K0wuOY9Y,150
99
+ datarobot_genai-0.1.72.dist-info/licenses/AUTHORS,sha256=isJGUXdjq1U7XZ_B_9AH8Qf0u4eX0XyQifJZ_Sxm4sA,80
100
+ datarobot_genai-0.1.72.dist-info/licenses/LICENSE,sha256=U2_VkLIktQoa60Nf6Tbt7E4RMlfhFSjWjcJJfVC-YCE,11341
101
+ datarobot_genai-0.1.72.dist-info/RECORD,,