datarobot-moderations 11.2.9__py3-none-any.whl → 11.2.11__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.
@@ -0,0 +1,259 @@
1
+ # ---------------------------------------------------------------------------------
2
+ # Copyright (c) 2026 DataRobot, Inc. and its affiliates. All rights reserved.
3
+ # Last updated 2025.
4
+ #
5
+ # DataRobot, Inc. Confidential.
6
+ # This is proprietary source code of DataRobot, Inc. and its affiliates.
7
+ #
8
+ # This file and its contents are subject to DataRobot Tool and Utility Agreement.
9
+ # For details, see
10
+ # https://www.datarobot.com/wp-content/uploads/2021/07/DataRobot-Tool-and-Utility-Agreement.pdf.
11
+ # ---------------------------------------------------------------------------------
12
+ from abc import ABC
13
+
14
+ from datarobot.enums import CustomMetricAggregationType
15
+ from datarobot.enums import CustomMetricDirectionality
16
+
17
+ from datarobot_dome.constants import AGENT_GOAL_ACCURACY_COLUMN_NAME
18
+ from datarobot_dome.constants import COST_COLUMN_NAME
19
+ from datarobot_dome.constants import CUSTOM_METRIC_DESCRIPTION_SUFFIX
20
+ from datarobot_dome.constants import DEFAULT_PROMPT_COLUMN_NAME
21
+ from datarobot_dome.constants import DEFAULT_RESPONSE_COLUMN_NAME
22
+ from datarobot_dome.constants import FAITHFULLNESS_COLUMN_NAME
23
+ from datarobot_dome.constants import GUIDELINE_ADHERENCE_COLUMN_NAME
24
+ from datarobot_dome.constants import NEMO_GUARD_COLUMN_NAME
25
+ from datarobot_dome.constants import ROUGE_1_COLUMN_NAME
26
+ from datarobot_dome.constants import SPAN_PREFIX
27
+ from datarobot_dome.constants import TASK_ADHERENCE_SCORE_COLUMN_NAME
28
+ from datarobot_dome.constants import TOKEN_COUNT_COLUMN_NAME
29
+ from datarobot_dome.constants import GuardAction
30
+ from datarobot_dome.constants import GuardStage
31
+ from datarobot_dome.constants import GuardType
32
+ from datarobot_dome.constants import NemoEvaluatorType
33
+ from datarobot_dome.constants import OOTBType
34
+
35
+
36
+ def get_metric_column_name(
37
+ guard_type: GuardType,
38
+ ootb_type: OOTBType | None,
39
+ stage: GuardStage,
40
+ model_guard_target_name: str | None = None,
41
+ metric_name: str | None = None,
42
+ nemo_evaluator_type: str | None = None,
43
+ ) -> str:
44
+ """Gets the metric column name. Note that this function gets used in buzok code. If you update
45
+ it, please also update the moderation library in the buzok worker image.
46
+ """
47
+ if guard_type == GuardType.MODEL:
48
+ if model_guard_target_name is None:
49
+ raise ValueError(
50
+ "For the model guard type, a valid model_guard_target_name has to be provided."
51
+ )
52
+ metric_result_key = Guard.get_stage_str(stage) + "_" + model_guard_target_name
53
+ elif guard_type == GuardType.OOTB:
54
+ if ootb_type is None:
55
+ raise ValueError("For the OOTB type, a valid OOTB guard type has to be provided.")
56
+ elif ootb_type == OOTBType.TOKEN_COUNT:
57
+ metric_result_key = Guard.get_stage_str(stage) + "_" + TOKEN_COUNT_COLUMN_NAME
58
+ elif ootb_type == OOTBType.ROUGE_1:
59
+ metric_result_key = Guard.get_stage_str(stage) + "_" + ROUGE_1_COLUMN_NAME
60
+ elif ootb_type == OOTBType.FAITHFULNESS:
61
+ metric_result_key = Guard.get_stage_str(stage) + "_" + FAITHFULLNESS_COLUMN_NAME
62
+ elif ootb_type == OOTBType.AGENT_GOAL_ACCURACY:
63
+ metric_result_key = AGENT_GOAL_ACCURACY_COLUMN_NAME
64
+ elif ootb_type == OOTBType.CUSTOM_METRIC:
65
+ if metric_name is None:
66
+ raise ValueError(
67
+ "For the custom metric type, a valid metric_name has to be provided."
68
+ )
69
+ metric_result_key = Guard.get_stage_str(stage) + "_" + metric_name
70
+ elif ootb_type == OOTBType.COST:
71
+ metric_result_key = COST_COLUMN_NAME
72
+ elif ootb_type == OOTBType.TASK_ADHERENCE:
73
+ metric_result_key = TASK_ADHERENCE_SCORE_COLUMN_NAME
74
+ elif ootb_type == OOTBType.GUIDELINE_ADHERENCE:
75
+ metric_result_key = GUIDELINE_ADHERENCE_COLUMN_NAME
76
+ else:
77
+ raise ValueError("The provided OOTB type is not implemented.")
78
+ elif guard_type == GuardType.NEMO_GUARDRAILS:
79
+ metric_result_key = Guard.get_stage_str(stage) + "_" + NEMO_GUARD_COLUMN_NAME
80
+ elif guard_type == GuardType.NEMO_EVALUATOR:
81
+ if nemo_evaluator_type == NemoEvaluatorType.LLM_JUDGE:
82
+ metric_result_key = f"{Guard.get_stage_str(stage)}_nemo_{nemo_evaluator_type}"
83
+ elif nemo_evaluator_type in NemoEvaluatorType.ALL:
84
+ metric_result_key = f"nemo_{nemo_evaluator_type}"
85
+ else:
86
+ raise ValueError("The provided NeMo Evaluator type is not implemented.")
87
+ else:
88
+ raise ValueError("The provided guard type is not implemented.")
89
+ return metric_result_key
90
+
91
+
92
+ class GuardIntervention:
93
+ def __init__(self, intervention_config: dict) -> None:
94
+ self.action = intervention_config["action"]
95
+ self.message = intervention_config.get("message")
96
+ self.threshold = None
97
+ self.comparator = None
98
+ if (
99
+ "conditions" in intervention_config
100
+ and intervention_config["conditions"] is not None
101
+ and len(intervention_config["conditions"]) > 0
102
+ ):
103
+ self.threshold = intervention_config["conditions"][0].get("comparand")
104
+ self.comparator = intervention_config["conditions"][0].get("comparator")
105
+
106
+
107
+ class Guard(ABC):
108
+ def __init__(self, config: dict, stage=None):
109
+ self._name = config["name"]
110
+ self._description = config.get("description")
111
+ self._type = config["type"]
112
+ self._stage = stage if stage else config["stage"]
113
+ self._pipeline = None
114
+ self.intervention = None
115
+ self._deployment_id = config.get("deployment_id")
116
+ self._dr_cm = None
117
+ self._faas_url = config.get("faas_url")
118
+ self._copy_citations = config["copy_citations"]
119
+ self.is_agentic = config.get("is_agentic", False)
120
+ self.metric_column_name = get_metric_column_name(
121
+ config["type"],
122
+ config.get("ootb_type"),
123
+ self._stage,
124
+ config.get("model_info", {}).get("target_name"),
125
+ config["name"],
126
+ config.get("nemo_evaluator_type"),
127
+ )
128
+ if config.get("intervention"):
129
+ self.intervention = GuardIntervention(config["intervention"])
130
+
131
+ @property
132
+ def name(self) -> str:
133
+ return self._name
134
+
135
+ @property
136
+ def description(self) -> str:
137
+ return self._description
138
+
139
+ @property
140
+ def type(self) -> GuardType:
141
+ return self._type
142
+
143
+ @property
144
+ def stage(self) -> GuardStage:
145
+ return self._stage
146
+
147
+ @property
148
+ def faas_url(self) -> str:
149
+ return self._faas_url
150
+
151
+ @property
152
+ def copy_citations(self) -> str:
153
+ return self._copy_citations
154
+
155
+ def set_pipeline(self, pipeline):
156
+ self._pipeline = pipeline
157
+
158
+ @property
159
+ def llm_type(self):
160
+ return self._llm_type
161
+
162
+ @staticmethod
163
+ def get_stage_str(stage):
164
+ return "Prompts" if stage == GuardStage.PROMPT else "Responses"
165
+
166
+ def get_input_column_name(self, stage) -> str:
167
+ match stage:
168
+ case GuardStage.PROMPT:
169
+ return DEFAULT_PROMPT_COLUMN_NAME
170
+ case GuardStage.RESPONSE:
171
+ return DEFAULT_RESPONSE_COLUMN_NAME
172
+ case _:
173
+ raise ValueError(f"Stage ({stage}) is not supported.")
174
+
175
+ def has_latency_custom_metric(self) -> bool:
176
+ """Determines if latency metric is tracked for this guard type. Default is True."""
177
+ return True
178
+
179
+ def get_latency_custom_metric_name(self):
180
+ return f"{self.name} Guard Latency"
181
+
182
+ def get_latency_custom_metric(self):
183
+ return {
184
+ "name": self.get_latency_custom_metric_name(),
185
+ "directionality": CustomMetricDirectionality.LOWER_IS_BETTER,
186
+ "units": "seconds",
187
+ "type": CustomMetricAggregationType.AVERAGE,
188
+ "baselineValue": 0,
189
+ "isModelSpecific": True,
190
+ "timeStep": "hour",
191
+ "description": (
192
+ f"{self.get_latency_custom_metric_name()}. {CUSTOM_METRIC_DESCRIPTION_SUFFIX}"
193
+ ),
194
+ }
195
+
196
+ def has_average_score_custom_metric(self) -> bool:
197
+ """Determines if an average score metric is tracked for this guard type. Default is True."""
198
+ return True
199
+
200
+ def get_average_score_custom_metric_name(self, stage):
201
+ return f"{self.name} Guard Average Score for {self.get_stage_str(stage)}"
202
+
203
+ def get_average_score_metric(self, stage):
204
+ return {
205
+ "name": self.get_average_score_custom_metric_name(stage),
206
+ "directionality": CustomMetricDirectionality.LOWER_IS_BETTER,
207
+ "units": "probability",
208
+ "type": CustomMetricAggregationType.AVERAGE,
209
+ "baselineValue": 0,
210
+ "isModelSpecific": True,
211
+ "timeStep": "hour",
212
+ "description": (
213
+ f"{self.get_average_score_custom_metric_name(stage)}. "
214
+ f" {CUSTOM_METRIC_DESCRIPTION_SUFFIX}"
215
+ ),
216
+ }
217
+
218
+ def get_guard_enforced_custom_metric_name(self, stage, moderation_method):
219
+ if moderation_method == GuardAction.REPLACE:
220
+ return f"{self.name} Guard replaced {self.get_stage_str(stage)}"
221
+ return f"{self.name} Guard {moderation_method}ed {self.get_stage_str(stage)}"
222
+
223
+ def get_enforced_custom_metric(self, stage, moderation_method):
224
+ return {
225
+ "name": self.get_guard_enforced_custom_metric_name(stage, moderation_method),
226
+ "directionality": CustomMetricDirectionality.LOWER_IS_BETTER,
227
+ "units": "count",
228
+ "type": CustomMetricAggregationType.SUM,
229
+ "baselineValue": 0,
230
+ "isModelSpecific": True,
231
+ "timeStep": "hour",
232
+ "description": (
233
+ f"Number of {self.get_stage_str(stage)} {moderation_method}ed by the "
234
+ f"{self.name} guard. {CUSTOM_METRIC_DESCRIPTION_SUFFIX}"
235
+ ),
236
+ }
237
+
238
+ def get_intervention_action(self):
239
+ if not self.intervention:
240
+ return GuardAction.NONE
241
+ return self.intervention.action
242
+
243
+ def get_comparand(self):
244
+ return self.intervention.threshold
245
+
246
+ def get_enforced_span_attribute_name(self, stage):
247
+ intervention_action = self.get_intervention_action()
248
+ if intervention_action in [GuardAction.BLOCK, GuardAction.REPORT]:
249
+ return f"{SPAN_PREFIX}.{stage.lower()}.{intervention_action}ed"
250
+ elif intervention_action == GuardAction.REPLACE:
251
+ return f"{SPAN_PREFIX}.{stage.lower()}.replaced"
252
+ else:
253
+ raise NotImplementedError
254
+
255
+ def get_span_column_name(self, _):
256
+ raise NotImplementedError
257
+
258
+ def get_span_attribute_name(self, _):
259
+ raise NotImplementedError
@@ -1,5 +1,5 @@
1
1
  # ---------------------------------------------------------------------------------
2
- # Copyright (c) 2025 DataRobot, Inc. and its affiliates. All rights reserved.
2
+ # Copyright (c) 2026 DataRobot, Inc. and its affiliates. All rights reserved.
3
3
  # Last updated 2025.
4
4
  #
5
5
  # DataRobot, Inc. Confidential.
@@ -94,6 +94,8 @@ class GuardLLMMixin:
94
94
  return f"{secret_env_var_name_prefix}{OOTBType.AGENT_GOAL_ACCURACY}_{llm_type_str}"
95
95
  elif config["ootb_type"] == OOTBType.TASK_ADHERENCE:
96
96
  return f"{secret_env_var_name_prefix}{OOTBType.TASK_ADHERENCE}_{llm_type_str}"
97
+ elif config["ootb_type"] == OOTBType.GUIDELINE_ADHERENCE:
98
+ return f"{secret_env_var_name_prefix}{OOTBType.GUIDELINE_ADHERENCE}_{llm_type_str}"
97
99
  else:
98
100
  raise Exception("Invalid guard config for building env var name")
99
101
  else:
@@ -0,0 +1,84 @@
1
+ # ---------------------------------------------------------------------------------
2
+ # Copyright (c) 2026 DataRobot, Inc. and its affiliates. All rights reserved.
3
+ # Last updated 2025.
4
+ #
5
+ # DataRobot, Inc. Confidential.
6
+ # This is proprietary source code of DataRobot, Inc. and its affiliates.
7
+ #
8
+ # This file and its contents are subject to DataRobot Tool and Utility Agreement.
9
+ # For details, see
10
+ # https://www.datarobot.com/wp-content/uploads/2021/07/DataRobot-Tool-and-Utility-Agreement.pdf.
11
+ # ---------------------------------------------------------------------------------
12
+ import datarobot as dr
13
+
14
+ from datarobot_dome.constants import SPAN_PREFIX
15
+
16
+ from .base import Guard
17
+
18
+
19
+ class GuardModelInfo:
20
+ def __init__(self, model_config: dict):
21
+ self._model_id = model_config.get("model_id")
22
+ self._target_name = model_config["target_name"]
23
+ self._target_type = model_config["target_type"]
24
+ self._class_names = model_config.get("class_names", [])
25
+ self._input_column_name = model_config["input_column_name"]
26
+ self._replacement_text_column_name = model_config.get("replacement_text_column_name")
27
+
28
+ @property
29
+ def model_id(self) -> str:
30
+ return self._model_id
31
+
32
+ @property
33
+ def target_name(self) -> str:
34
+ return self._target_name
35
+
36
+ @property
37
+ def target_type(self) -> str:
38
+ return self._target_type
39
+
40
+ @property
41
+ def class_names(self) -> list[str]:
42
+ return self._class_names
43
+
44
+ @property
45
+ def input_column_name(self) -> str:
46
+ return self._input_column_name
47
+
48
+ @property
49
+ def replacement_text_column_name(self) -> str:
50
+ return self._replacement_text_column_name
51
+
52
+
53
+ class ModelGuard(Guard):
54
+ def __init__(self, config: dict, stage=None):
55
+ super().__init__(config, stage)
56
+ self._deployment_id = config["deployment_id"]
57
+ self._model_info = GuardModelInfo(config["model_info"])
58
+ # dr.Client is set in the Pipeline init, Lets query the deployment
59
+ # to get the prediction server information
60
+ self.deployment = dr.Deployment.get(self._deployment_id)
61
+
62
+ @property
63
+ def deployment_id(self) -> str:
64
+ return self._deployment_id
65
+
66
+ @property
67
+ def model_info(self):
68
+ return self._model_info
69
+
70
+ def get_input_column_name(self, stage) -> str:
71
+ return self._model_info.input_column_name
72
+
73
+ def get_span_column_name(self, _):
74
+ if self.model_info is None:
75
+ raise NotImplementedError("Missing model_info for model guard")
76
+ # Typically 0th index is the target name
77
+ return self._model_info.target_name.split("_")[0]
78
+
79
+ def get_span_attribute_name(self, stage):
80
+ return f"{SPAN_PREFIX}.{stage.lower()}.{self.get_span_column_name(stage)}"
81
+
82
+ def has_average_score_custom_metric(self) -> bool:
83
+ """A couple ModelGuard types do not have an average score metric"""
84
+ return self.model_info.target_type not in ["Multiclass", "TextGeneration"]
@@ -0,0 +1,73 @@
1
+ # ---------------------------------------------------------------------------------
2
+ # Copyright (c) 2026 DataRobot, Inc. and its affiliates. All rights reserved.
3
+ # Last updated 2025.
4
+ #
5
+ # DataRobot, Inc. Confidential.
6
+ # This is proprietary source code of DataRobot, Inc. and its affiliates.
7
+ #
8
+ # This file and its contents are subject to DataRobot Tool and Utility Agreement.
9
+ # For details, see
10
+ # https://www.datarobot.com/wp-content/uploads/2021/07/DataRobot-Tool-and-Utility-Agreement.pdf.
11
+ # ---------------------------------------------------------------------------------
12
+ from functools import cached_property
13
+
14
+ from nemo_microservices import AsyncNeMoMicroservices
15
+
16
+ from .base import Guard
17
+
18
+
19
+ class NeMoEvaluatorGuard(Guard):
20
+ def __init__(self, config: dict, stage=None):
21
+ super().__init__(config, stage)
22
+ self.nemo_evaluator_type = config["nemo_evaluator_type"]
23
+ self._llm_type = config["llm_type"]
24
+ self.llm_deployment_id = config.get("deployment_id")
25
+
26
+ @cached_property
27
+ def _client(self) -> AsyncNeMoMicroservices:
28
+ """
29
+ Using localhost for development purpose only.
30
+ It will be replaced with url to a deployed NeMo evaluator instance later in the PBMP.
31
+ """
32
+ return AsyncNeMoMicroservices(base_url="http://localhost:8080")
33
+
34
+ def has_average_score_custom_metric(self) -> bool:
35
+ return False
36
+
37
+ async def evaluate(self, prompt: str, response: str) -> float:
38
+ raise NotImplementedError
39
+
40
+
41
+ class NeMoLLMJudgeGuard(NeMoEvaluatorGuard):
42
+ def __init__(self, config: dict, stage=None):
43
+ super().__init__(config, stage)
44
+
45
+
46
+ class NeMoContextRelevanceGuard(NeMoEvaluatorGuard):
47
+ def __init__(self, config: dict, stage=None):
48
+ super().__init__(config, stage)
49
+
50
+
51
+ class NeMoResponseGroundednessGuard(NeMoEvaluatorGuard):
52
+ def __init__(self, config: dict, stage=None):
53
+ super().__init__(config, stage)
54
+
55
+
56
+ class NeMoTopicAdherenceGuard(NeMoEvaluatorGuard):
57
+ def __init__(self, config: dict, stage=None):
58
+ super().__init__(config, stage)
59
+
60
+
61
+ class NeMoAgentGoalAccuracyGuard(NeMoEvaluatorGuard):
62
+ def __init__(self, config: dict, stage=None):
63
+ super().__init__(config, stage)
64
+
65
+
66
+ class NeMoResponseRelevancyGuard(NeMoEvaluatorGuard):
67
+ def __init__(self, config: dict, stage=None):
68
+ super().__init__(config, stage)
69
+
70
+
71
+ class NeMoFaithfulnessGuard(NeMoEvaluatorGuard):
72
+ def __init__(self, config: dict, stage=None):
73
+ super().__init__(config, stage)
@@ -0,0 +1,146 @@
1
+ # ---------------------------------------------------------------------------------
2
+ # Copyright (c) 2026 DataRobot, Inc. and its affiliates. All rights reserved.
3
+ # Last updated 2025.
4
+ #
5
+ # DataRobot, Inc. Confidential.
6
+ # This is proprietary source code of DataRobot, Inc. and its affiliates.
7
+ #
8
+ # This file and its contents are subject to DataRobot Tool and Utility Agreement.
9
+ # For details, see
10
+ # https://www.datarobot.com/wp-content/uploads/2021/07/DataRobot-Tool-and-Utility-Agreement.pdf.
11
+ # ---------------------------------------------------------------------------------
12
+ import logging
13
+ import os
14
+
15
+ import datarobot as dr
16
+ from nemoguardrails import LLMRails
17
+ from nemoguardrails import RailsConfig
18
+
19
+ from datarobot_dome.constants import NEMO_GUARDRAILS_DIR
20
+ from datarobot_dome.constants import GuardLLMType
21
+ from datarobot_dome.constants import GuardOperatorType
22
+ from datarobot_dome.guard_helpers import DEFAULT_OPEN_AI_API_VERSION
23
+ from datarobot_dome.guard_helpers import get_azure_openai_client
24
+ from datarobot_dome.guard_helpers import get_chat_nvidia_llm
25
+ from datarobot_dome.guard_helpers import get_datarobot_endpoint_and_token
26
+ from datarobot_dome.guard_helpers import get_llm_gateway_client
27
+ from datarobot_dome.guard_helpers import use_llm_gateway_inference
28
+ from datarobot_dome.guards.base import Guard
29
+ from datarobot_dome.guards.guard_llm_mixin import GuardLLMMixin
30
+
31
+ NEMO_THRESHOLD = "TRUE"
32
+
33
+
34
+ class NeMoGuard(Guard, GuardLLMMixin):
35
+ def __init__(self, config: dict, stage=None, model_dir: str = os.getcwd()):
36
+ super().__init__(config, stage)
37
+ # NeMo guard only takes a boolean as threshold and equal to as comparator.
38
+ # Threshold bool == TRUE is defined in the colang file as the output of
39
+ # `bot should intervene`
40
+ if self.intervention:
41
+ if not self.intervention.threshold:
42
+ self.intervention.threshold = NEMO_THRESHOLD
43
+ if not self.intervention.comparator:
44
+ self.intervention.comparator = GuardOperatorType.EQUALS
45
+
46
+ # Default LLM Type for NeMo is set to OpenAI
47
+ self._llm_type = config.get("llm_type", GuardLLMType.OPENAI)
48
+ self.openai_api_base = config.get("openai_api_base")
49
+ self.openai_deployment_id = config.get("openai_deployment_id")
50
+ llm_id = None
51
+ credentials = None
52
+ use_llm_gateway = use_llm_gateway_inference(self._llm_type)
53
+ try:
54
+ self.openai_api_key = self.get_openai_api_key(config, self._llm_type)
55
+ if self._llm_type != GuardLLMType.NIM and self.openai_api_key is None:
56
+ raise ValueError("OpenAI API key is required for NeMo Guardrails")
57
+
58
+ if self.llm_type == GuardLLMType.OPENAI:
59
+ credentials = {
60
+ "credential_type": "openai",
61
+ "api_key": self.openai_api_key,
62
+ }
63
+ os.environ["OPENAI_API_KEY"] = self.openai_api_key
64
+ llm = None
65
+ elif self.llm_type == GuardLLMType.AZURE_OPENAI:
66
+ if self.openai_api_base is None:
67
+ raise ValueError("Azure OpenAI API base url is required for LLM Guard")
68
+ if self.openai_deployment_id is None:
69
+ raise ValueError("Azure OpenAI deployment ID is required for LLM Guard")
70
+ credentials = {
71
+ "credential_type": "azure_openai",
72
+ "api_base": self.openai_api_base,
73
+ "api_version": DEFAULT_OPEN_AI_API_VERSION,
74
+ "api_key": self.openai_api_key,
75
+ }
76
+ azure_openai_client = get_azure_openai_client(
77
+ openai_api_key=self.openai_api_key,
78
+ openai_api_base=self.openai_api_base,
79
+ openai_deployment_id=self.openai_deployment_id,
80
+ )
81
+ llm = azure_openai_client
82
+ elif self.llm_type == GuardLLMType.GOOGLE:
83
+ # llm_id = config["google_model"]
84
+ raise NotImplementedError
85
+ elif self.llm_type == GuardLLMType.AMAZON:
86
+ # llm_id = config["aws_model"]
87
+ raise NotImplementedError
88
+ elif self.llm_type == GuardLLMType.DATAROBOT:
89
+ raise NotImplementedError
90
+ elif self.llm_type == GuardLLMType.NIM:
91
+ if config.get("deployment_id") is None:
92
+ if self.openai_api_base is None:
93
+ raise ValueError("NIM DataRobot deployment id is required for NIM LLM Type")
94
+ else:
95
+ logging.warning(
96
+ "Using 'openai_api_base' is being deprecated and will be removed "
97
+ "in the next release. Please configure NIM DataRobot deployment "
98
+ "using deployment_id"
99
+ )
100
+ if self.openai_api_key is None:
101
+ raise ValueError("OpenAI API key is required for NeMo Guardrails")
102
+ else:
103
+ self.deployment = dr.Deployment.get(self._deployment_id)
104
+ datarobot_endpoint, self.openai_api_key = get_datarobot_endpoint_and_token()
105
+ self.openai_api_base = (
106
+ f"{datarobot_endpoint}/deployments/{str(self._deployment_id)}"
107
+ )
108
+ llm = get_chat_nvidia_llm(
109
+ api_key=self.openai_api_key,
110
+ base_url=self.openai_api_base,
111
+ )
112
+ else:
113
+ raise ValueError(f"Invalid LLMType: {self.llm_type}")
114
+
115
+ except Exception as e:
116
+ # no valid user credentials provided, raise if not using LLM Gateway
117
+ credentials = None
118
+ if not use_llm_gateway:
119
+ raise e
120
+
121
+ if use_llm_gateway:
122
+ # Currently only OPENAI and AZURE_OPENAI are supported by NeMoGuard
123
+ # For Bedrock and Vertex the model in the config is actually the LLM ID
124
+ # For OpenAI we use the default model defined in get_llm_gateway_client
125
+ # For Azure we use the deployment ID
126
+ llm = get_llm_gateway_client(
127
+ llm_id=llm_id,
128
+ openai_deployment_id=self.openai_deployment_id,
129
+ credentials=credentials,
130
+ )
131
+
132
+ # Use guard stage to determine whether to read from prompt/response subdirectory
133
+ # for nemo configurations. "nemo_guardrails" folder is at same level of custom.py
134
+ # So, the config path becomes model_dir + "nemo_guardrails"
135
+ nemo_config_path = os.path.join(model_dir, NEMO_GUARDRAILS_DIR)
136
+ self.nemo_rails_config_path = os.path.join(nemo_config_path, self.stage)
137
+ nemo_rails_config = RailsConfig.from_path(config_path=self.nemo_rails_config_path)
138
+ self._nemo_llm_rails = LLMRails(nemo_rails_config, llm=llm)
139
+
140
+ def has_average_score_custom_metric(self) -> bool:
141
+ """No average score metrics for NemoGuard's"""
142
+ return False
143
+
144
+ @property
145
+ def nemo_llm_rails(self):
146
+ return self._nemo_llm_rails