datarobot-genai 0.1.62__py3-none-any.whl → 0.1.64__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.
@@ -11,7 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
-
14
+ from collections import defaultdict
15
15
  from dataclasses import dataclass
16
16
 
17
17
  import datarobot as dr
@@ -34,6 +34,7 @@ class DrVariable:
34
34
  @dataclass
35
35
  class DrPromptVersion:
36
36
  id: str
37
+ prompt_template_id: str
37
38
  version: int
38
39
  prompt_text: str
39
40
  variables: list[DrVariable]
@@ -45,6 +46,7 @@ class DrPromptVersion:
45
46
  ]
46
47
  return cls(
47
48
  id=d["id"],
49
+ prompt_template_id=d["promptTemplateId"],
48
50
  version=d["version"],
49
51
  prompt_text=d["promptText"],
50
52
  variables=variables,
@@ -58,7 +60,9 @@ class DrPrompt:
58
60
  description: str
59
61
 
60
62
  def get_latest_version(self) -> DrPromptVersion | None:
61
- prompt_template_versions = get_datarobot_prompt_template_versions(self.id)
63
+ all_prompt_template_versions = get_datarobot_prompt_template_versions([self.id])
64
+ prompt_template_versions = all_prompt_template_versions.get(self.id)
65
+
62
66
  if not prompt_template_versions:
63
67
  return None
64
68
  latest_version = max(prompt_template_versions, key=lambda v: v.version)
@@ -77,15 +81,21 @@ def get_datarobot_prompt_templates() -> list[DrPrompt]:
77
81
  return [DrPrompt.from_dict(prompt_template) for prompt_template in prompt_templates_data]
78
82
 
79
83
 
80
- def get_datarobot_prompt_template_versions(prompt_template_id: str) -> list[DrPromptVersion]:
84
+ def get_datarobot_prompt_template_versions(
85
+ prompt_template_ids: list[str],
86
+ ) -> dict[str, list[DrPromptVersion]]:
81
87
  prompt_template_versions_data = dr.utils.pagination.unpaginate(
82
- initial_url=f"genai/promptTemplates/{prompt_template_id}/versions/",
83
- initial_params={},
88
+ initial_url="genai/promptTemplates/versions/",
89
+ initial_params={
90
+ "promptTemplateIds": prompt_template_ids,
91
+ },
84
92
  client=get_api_client(),
85
93
  )
86
- prompt_template_versions = []
94
+ prompt_template_versions = defaultdict(list)
87
95
  for prompt_template_version in prompt_template_versions_data:
88
- prompt_template_versions.append(DrPromptVersion.from_dict(prompt_template_version))
96
+ prompt_template_versions[prompt_template_version["promptTemplateId"]].append(
97
+ DrPromptVersion.from_dict(prompt_template_version)
98
+ )
89
99
  return prompt_template_versions
90
100
 
91
101
 
@@ -27,6 +27,7 @@ from datarobot_genai.drmcp.core.mcp_instance import register_prompt
27
27
  from .dr_lib import DrPrompt
28
28
  from .dr_lib import DrPromptVersion
29
29
  from .dr_lib import DrVariable
30
+ from .dr_lib import get_datarobot_prompt_template_versions
30
31
  from .dr_lib import get_datarobot_prompt_templates
31
32
 
32
33
  logger = logging.getLogger(__name__)
@@ -36,11 +37,21 @@ async def register_prompts_from_datarobot_prompt_management() -> None:
36
37
  """Register prompts from DataRobot Prompt Management."""
37
38
  prompts = get_datarobot_prompt_templates()
38
39
  logger.info(f"Found {len(prompts)} prompts in Prompts Management.")
40
+ all_prompts_versions = get_datarobot_prompt_template_versions(
41
+ prompt_template_ids=list({prompt.id for prompt in prompts})
42
+ )
39
43
 
40
44
  # Try to register each prompt, continue on failure
41
45
  for prompt in prompts:
46
+ prompt_versions = all_prompts_versions.get(prompt.id)
47
+ if not prompt_versions:
48
+ logger.warning(f"Prompt template id {prompt.id} has no versions.")
49
+ continue
50
+
51
+ latest_version = max(prompt_versions, key=lambda v: v.version)
52
+
42
53
  try:
43
- await register_prompt_from_datarobot_prompt_management(prompt)
54
+ await register_prompt_from_datarobot_prompt_management(prompt, latest_version)
44
55
  except DynamicPromptRegistrationError:
45
56
  pass
46
57
 
@@ -114,15 +125,36 @@ async def register_prompt_from_datarobot_prompt_management(
114
125
  ) from exc
115
126
 
116
127
 
128
+ def _escape_non_ascii(s: str) -> str:
129
+ out = []
130
+ for ch in s:
131
+ # If its space -> change to underscore
132
+ if ch.isspace():
133
+ out.append("_")
134
+ # ASCII letter, digit or underscore -> keep
135
+ elif ch.isascii() and (ch.isalnum() or ch == "_"):
136
+ out.append(ch)
137
+ # Everything else -> encode as 'xHEX'
138
+ else:
139
+ out.append(f"x{ord(ch):x}")
140
+ return "".join(out)
141
+
142
+
117
143
  def to_valid_mcp_prompt_name(s: str) -> str:
118
144
  """Convert an arbitrary string into a valid MCP prompt name."""
119
- # Replace any sequence of invalid characters with '_'
120
- s = re.sub(r"[^0-9a-zA-Z_]+", "_", s)
121
-
122
145
  # If its ONLY numbers return "prompt_[number]"
123
146
  if s.isdigit():
124
147
  return f"prompt_{s}"
125
148
 
149
+ # First, ASCII-transliterate using hex escape for non-ASCII
150
+ if not s.isascii():
151
+ # whole string non-ascii? -> escape and prefix with prompt_
152
+ encoded = _escape_non_ascii(s)
153
+ return f"prompt_{encoded}"
154
+
155
+ # Replace any sequence of invalid characters with '_'
156
+ s = re.sub(r"[^0-9a-zA-Z_]+", "_", s)
157
+
126
158
  # Remove leading characters that are not letters or underscores (can't start with a digit or _)
127
159
  s = re.sub(r"^[^a-zA-Z]+", "", s)
128
160
 
@@ -32,6 +32,7 @@ from datarobot_genai.core.agents.base import BaseAgent
32
32
  from datarobot_genai.core.agents.base import InvokeReturn
33
33
  from datarobot_genai.core.agents.base import UsageMetrics
34
34
  from datarobot_genai.core.agents.base import extract_user_prompt_content
35
+ from datarobot_genai.core.agents.base import is_streaming
35
36
  from datarobot_genai.langgraph.mcp import mcp_tools_context
36
37
 
37
38
  logger = logging.getLogger(__name__)
@@ -136,10 +137,10 @@ class LangGraphAgent(BaseAgent[BaseTool], abc.ABC):
136
137
  # The following code demonstrate both a synchronous and streaming response.
137
138
  # You can choose one or the other based on your use case, they function the same.
138
139
  # The main difference is returning a generator for streaming or a final response for sync.
139
- if completion_create_params.get("stream"):
140
+ if is_streaming(completion_create_params):
140
141
  # Streaming response: yield each message as it is generated
141
142
  async def stream_generator() -> AsyncGenerator[
142
- tuple[str, Any | None, UsageMetrics], None
143
+ tuple[str, MultiTurnSample | None, UsageMetrics], None
143
144
  ]:
144
145
  # Iterate over the graph stream. For message events, yield the content.
145
146
  # For update events, accumulate the usage metrics.
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
  import asyncio
15
15
  import logging
16
+ from collections.abc import AsyncGenerator
16
17
  from typing import Any
17
18
 
18
19
  from nat.builder.context import Context
@@ -32,6 +33,7 @@ from datarobot_genai.core.agents.base import BaseAgent
32
33
  from datarobot_genai.core.agents.base import InvokeReturn
33
34
  from datarobot_genai.core.agents.base import UsageMetrics
34
35
  from datarobot_genai.core.agents.base import extract_user_prompt_content
36
+ from datarobot_genai.core.agents.base import is_streaming
35
37
 
36
38
  logger = logging.getLogger(__name__)
37
39
 
@@ -160,6 +162,49 @@ class NatAgent(BaseAgent[None]):
160
162
  # Print commands may need flush=True to ensure they are displayed in real-time.
161
163
  print("Running agent with user prompt:", chat_request.messages[0].content, flush=True)
162
164
 
165
+ if is_streaming(completion_create_params):
166
+
167
+ async def stream_generator() -> AsyncGenerator[
168
+ tuple[str, MultiTurnSample | None, UsageMetrics], None
169
+ ]:
170
+ usage_metrics: UsageMetrics = {
171
+ "completion_tokens": 0,
172
+ "prompt_tokens": 0,
173
+ "total_tokens": 0,
174
+ }
175
+ async with load_workflow(self.workflow_path) as workflow:
176
+ async with workflow.run(chat_request) as runner:
177
+ intermediate_future = pull_intermediate_structured()
178
+ async for result in runner.result_stream():
179
+ if isinstance(result, ChatResponse):
180
+ result_text = result.choices[0].message.content
181
+ else:
182
+ result_text = str(result)
183
+
184
+ yield (
185
+ result_text,
186
+ None,
187
+ usage_metrics,
188
+ )
189
+
190
+ steps = await intermediate_future
191
+ llm_end_steps = [
192
+ step
193
+ for step in steps
194
+ if step.event_type == IntermediateStepType.LLM_END
195
+ ]
196
+ for step in llm_end_steps:
197
+ if step.usage_info:
198
+ token_usage = step.usage_info.token_usage
199
+ usage_metrics["total_tokens"] += token_usage.total_tokens
200
+ usage_metrics["prompt_tokens"] += token_usage.prompt_tokens
201
+ usage_metrics["completion_tokens"] += token_usage.completion_tokens
202
+
203
+ pipeline_interactions = create_pipeline_interactions_from_steps(steps)
204
+ yield "", pipeline_interactions, usage_metrics
205
+
206
+ return stream_generator()
207
+
163
208
  # Create and invoke the NAT (Nemo Agent Toolkit) Agentic Workflow with the inputs
164
209
  result, steps = await self.run_nat_workflow(self.workflow_path, chat_request)
165
210
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datarobot-genai
3
- Version: 0.1.62
3
+ Version: 0.1.64
4
4
  Summary: Generic helpers for GenAI
5
5
  Project-URL: Homepage, https://github.com/datarobot-oss/datarobot-genai
6
6
  Author: DataRobot, Inc.
@@ -45,8 +45,8 @@ datarobot_genai/drmcp/core/tool_filter.py,sha256=tLOcG50QBvS48cOVHM6OqoODYiiS6Ke
45
45
  datarobot_genai/drmcp/core/utils.py,sha256=dSjrayWVcnC5GxQcvOIOSHaoEymPIVtG_s2ZBMlmSOw,4336
46
46
  datarobot_genai/drmcp/core/dynamic_prompts/__init__.py,sha256=y4yapzp3KnFMzSR6HlNDS4uSuyNT7I1iPBvaCLsS0sU,577
47
47
  datarobot_genai/drmcp/core/dynamic_prompts/controllers.py,sha256=vCMYxwYjNDadRxSlRk6p8pHK6h_2K-PkbBTTW_lqBJ0,3318
48
- datarobot_genai/drmcp/core/dynamic_prompts/dr_lib.py,sha256=QEySFLKdTxsXk5VoGsHobbZ_olTB9nzPGo7M99TL5mc,3894
49
- datarobot_genai/drmcp/core/dynamic_prompts/register.py,sha256=Q2c9zuOuywS63AcGpD4774OhQKSGHX2tpLXqW_C4JKM,6058
48
+ datarobot_genai/drmcp/core/dynamic_prompts/dr_lib.py,sha256=IEdD2Gqm4SfUdiXJB99RiWxkN6frGaxJ2SfATetMM3c,4243
49
+ datarobot_genai/drmcp/core/dynamic_prompts/register.py,sha256=5AEh1m8GX-gPZHUdiE1VATt7IKJQk-eThcxh01sWn0I,7204
50
50
  datarobot_genai/drmcp/core/dynamic_prompts/utils.py,sha256=BZ3792AgfvYlwL0_J0MzQfGecyEA5_OKUMynEZYzCds,1136
51
51
  datarobot_genai/drmcp/core/dynamic_tools/__init__.py,sha256=0kq9vMkF7EBsS6lkEdiLibmUrghTQqosHbZ5k-V9a5g,578
52
52
  datarobot_genai/drmcp/core/dynamic_tools/register.py,sha256=3M5-F0mhUYTZJWmFDmqzsj3QAd7ut7b0kPv-JZyaTzg,9204
@@ -83,19 +83,19 @@ 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=SpVjiA9ARqkivVVnuk3z6Vu9boE4OJqgMzIrcNQWccM,9669
86
+ datarobot_genai/langgraph/agent.py,sha256=us-DXSgmI2271tsbzOOtDbRHdStCsOro-YRQ30ePpkA,9739
87
87
  datarobot_genai/langgraph/mcp.py,sha256=_ggxlRFkydhJgJVcAdwDWZL0WWCejctNC5X9ycAWeUM,2610
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
90
90
  datarobot_genai/llama_index/base.py,sha256=MwhSlRCwsP0Z9IxaTJ4tMZO2mCMX3pvqNFl3s9aj1P8,14427
91
91
  datarobot_genai/llama_index/mcp.py,sha256=3XULVxtLBV_ItM491zWRg2KMHGszy6OTyPzHW08zO2k,2439
92
92
  datarobot_genai/nat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
- datarobot_genai/nat/agent.py,sha256=egp3U1KAe2nW8LpoRaMYOpNmwL6KRWNUULMoHkmKCgs,8135
93
+ datarobot_genai/nat/agent.py,sha256=siBLDWAff2-JwZ8Q3iNpM_e4_IoSwG9IvY0hyEjNenw,10292
94
94
  datarobot_genai/nat/datarobot_llm_clients.py,sha256=IZq_kooUL8QyDTkpEreszLQk9vCzg6-FbTjIkXR9wc0,7203
95
95
  datarobot_genai/nat/datarobot_llm_providers.py,sha256=lOVaL_0Fl6-7GFYl3HmfqttqKpKt-2w8o92P3T7B6cU,3683
96
- datarobot_genai-0.1.62.dist-info/METADATA,sha256=Pyfx4lnxprmZK96X4rycMhtz-JuuvhX6b4MOJhYoYLA,5911
97
- datarobot_genai-0.1.62.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
98
- datarobot_genai-0.1.62.dist-info/entry_points.txt,sha256=CZhmZcSyt_RBltgLN_b9xasJD6J5SaDc_z7K0wuOY9Y,150
99
- datarobot_genai-0.1.62.dist-info/licenses/AUTHORS,sha256=isJGUXdjq1U7XZ_B_9AH8Qf0u4eX0XyQifJZ_Sxm4sA,80
100
- datarobot_genai-0.1.62.dist-info/licenses/LICENSE,sha256=U2_VkLIktQoa60Nf6Tbt7E4RMlfhFSjWjcJJfVC-YCE,11341
101
- datarobot_genai-0.1.62.dist-info/RECORD,,
96
+ datarobot_genai-0.1.64.dist-info/METADATA,sha256=xx80UKcDU-NjYz1wE6vHRUx_nJpEhX01MUi7tLU5wNs,5911
97
+ datarobot_genai-0.1.64.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
98
+ datarobot_genai-0.1.64.dist-info/entry_points.txt,sha256=CZhmZcSyt_RBltgLN_b9xasJD6J5SaDc_z7K0wuOY9Y,150
99
+ datarobot_genai-0.1.64.dist-info/licenses/AUTHORS,sha256=isJGUXdjq1U7XZ_B_9AH8Qf0u4eX0XyQifJZ_Sxm4sA,80
100
+ datarobot_genai-0.1.64.dist-info/licenses/LICENSE,sha256=U2_VkLIktQoa60Nf6Tbt7E4RMlfhFSjWjcJJfVC-YCE,11341
101
+ datarobot_genai-0.1.64.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any