nvidia-nat-crewai 1.2rc9__py3-none-any.whl → 1.4.0a20251121__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.

Potentially problematic release.


This version of nvidia-nat-crewai might be problematic. Click here for more details.

@@ -41,6 +41,7 @@ class CrewAIProfilerHandler(BaseProfilerCallback):
41
41
  A callback manager/handler for CrewAI that intercepts calls to:
42
42
  - ToolUsage._use
43
43
  - LLM Calls
44
+
44
45
  to collect usage statistics (tokens, inputs, outputs, time intervals, etc.)
45
46
  and store them in NAT's usage_stats queue for subsequent analysis.
46
47
  """
@@ -94,7 +95,7 @@ class CrewAIProfilerHandler(BaseProfilerCallback):
94
95
  if tool_info:
95
96
  tool_name = tool_info.name
96
97
  except Exception as e:
97
- logger.exception("Error getting tool name: %s", e, exc_info=True)
98
+ logger.exception("Error getting tool name: %s", e)
98
99
 
99
100
  try:
100
101
  # Pre-call usage event
@@ -132,7 +133,7 @@ class CrewAIProfilerHandler(BaseProfilerCallback):
132
133
  return result
133
134
 
134
135
  except Exception as e:
135
- logger.exception("ToolUsage._use error: %s", e)
136
+ logger.error("ToolUsage._use error: %s", e)
136
137
  raise
137
138
 
138
139
  return wrapped_tool_use
@@ -153,12 +154,15 @@ class CrewAIProfilerHandler(BaseProfilerCallback):
153
154
  seconds_between_calls = int(now - self.last_call_ts)
154
155
  model_name = kwargs.get('model', "")
155
156
 
156
- model_input = ""
157
+ model_input = []
157
158
  try:
158
159
  for message in kwargs.get('messages', []):
159
- model_input += message.get('content', "")
160
+ content = message.get('content', "")
161
+ model_input.append("" if content is None else str(content))
160
162
  except Exception as e:
161
- logger.exception("Error getting model input: %s", e, exc_info=True)
163
+ logger.exception("Error getting model input: %s", e)
164
+
165
+ model_input = "".join(model_input)
162
166
 
163
167
  # Record the start event
164
168
  input_stats = IntermediateStepPayload(
@@ -176,13 +180,16 @@ class CrewAIProfilerHandler(BaseProfilerCallback):
176
180
  # Call the original litellm.completion(...)
177
181
  output = original_func(*args, **kwargs)
178
182
 
179
- model_output = ""
183
+ model_output = []
180
184
  try:
181
185
  for choice in output.choices:
182
186
  msg = choice.model_extra["message"]
183
- model_output += msg.get('content', "")
187
+ content = msg.get('content', "")
188
+ model_output.append("" if content is None else str(content))
184
189
  except Exception as e:
185
- logger.exception("Error getting model output: %s", e, exc_info=True)
190
+ logger.exception("Error getting model output: %s", e)
191
+
192
+ model_output = "".join(model_output)
186
193
 
187
194
  now = time.time()
188
195
  # Record the end event
nat/plugins/crewai/llm.py CHANGED
@@ -14,67 +14,149 @@
14
14
  # limitations under the License.
15
15
 
16
16
  import os
17
+ from typing import TypeVar
17
18
 
18
19
  from nat.builder.builder import Builder
19
20
  from nat.builder.framework_enum import LLMFrameworkEnum
20
21
  from nat.cli.register_workflow import register_llm_client
22
+ from nat.data_models.common import get_secret_value
23
+ from nat.data_models.llm import LLMBaseConfig
21
24
  from nat.data_models.retry_mixin import RetryMixin
25
+ from nat.data_models.thinking_mixin import ThinkingMixin
26
+ from nat.llm.azure_openai_llm import AzureOpenAIModelConfig
27
+ from nat.llm.litellm_llm import LiteLlmModelConfig
22
28
  from nat.llm.nim_llm import NIMModelConfig
23
29
  from nat.llm.openai_llm import OpenAIModelConfig
30
+ from nat.llm.utils.thinking import BaseThinkingInjector
31
+ from nat.llm.utils.thinking import FunctionArgumentWrapper
32
+ from nat.llm.utils.thinking import patch_with_thinking
24
33
  from nat.utils.exception_handlers.automatic_retries import patch_with_retry
34
+ from nat.utils.responses_api import validate_no_responses_api
35
+ from nat.utils.type_utils import override
25
36
 
37
+ ModelType = TypeVar("ModelType")
26
38
 
27
- @register_llm_client(config_type=NIMModelConfig, wrapper_type=LLMFrameworkEnum.CREWAI)
28
- async def nim_crewai(llm_config: NIMModelConfig, builder: Builder):
29
-
30
- from crewai import LLM
31
-
32
- config_obj = {
33
- **llm_config.model_dump(exclude={"type"}, by_alias=True),
34
- "model": f"nvidia_nim/{llm_config.model_name}",
35
- }
36
-
37
- # Because CrewAI uses a different environment variable for the API key, we need to set it here manually
38
- if ("api_key" not in config_obj or config_obj["api_key"] is None):
39
39
 
40
- if ("NVIDIA_NIM_API_KEY" in os.environ):
41
- # Dont need to do anything. User has already set the correct key
42
- pass
43
- else:
44
- nvidai_api_key = os.getenv("NVIDIA_API_KEY")
40
+ def _patch_llm_based_on_config(client: ModelType, llm_config: LLMBaseConfig) -> ModelType:
45
41
 
46
- if (nvidai_api_key is not None):
47
- # Transfer the key to the correct environment variable for LiteLLM
48
- os.environ["NVIDIA_NIM_API_KEY"] = nvidai_api_key
42
+ class CrewAIThinkingInjector(BaseThinkingInjector):
49
43
 
50
- client = LLM(**config_obj)
44
+ @override
45
+ def inject(self, messages: list[dict[str, str]], *args, **kwargs) -> FunctionArgumentWrapper:
46
+ # Attempt to inject the system prompt into the first system message
47
+ for i, message in enumerate(messages):
48
+ if message["role"] == "system":
49
+ if self.system_prompt not in message["content"]:
50
+ messages = list(messages)
51
+ messages[i] = {"role": "system", "content": f"{message['content']}\n{self.system_prompt}"}
52
+ break
53
+ else:
54
+ messages = list(messages)
55
+ messages.insert(0, {"role": "system", "content": self.system_prompt})
56
+ return FunctionArgumentWrapper(messages, *args, **kwargs)
51
57
 
52
58
  if isinstance(llm_config, RetryMixin):
53
-
54
59
  client = patch_with_retry(client,
55
60
  retries=llm_config.num_retries,
56
61
  retry_codes=llm_config.retry_on_status_codes,
57
62
  retry_on_messages=llm_config.retry_on_errors)
58
63
 
59
- yield client
64
+ if isinstance(llm_config, ThinkingMixin) and llm_config.thinking_system_prompt is not None:
65
+ client = patch_with_thinking(
66
+ client, CrewAIThinkingInjector(
67
+ system_prompt=llm_config.thinking_system_prompt,
68
+ function_names=["call"],
69
+ ))
70
+
71
+ return client
72
+
73
+
74
+ @register_llm_client(config_type=AzureOpenAIModelConfig, wrapper_type=LLMFrameworkEnum.CREWAI)
75
+ async def azure_openai_crewai(llm_config: AzureOpenAIModelConfig, _builder: Builder):
76
+
77
+ from crewai import LLM
78
+
79
+ validate_no_responses_api(llm_config, LLMFrameworkEnum.CREWAI)
80
+
81
+ # https://docs.crewai.com/en/concepts/llms#azure
82
+
83
+ api_key = get_secret_value(llm_config.api_key) if llm_config.api_key else os.environ.get(
84
+ "AZURE_OPENAI_API_KEY") or os.environ.get("AZURE_API_KEY")
85
+ if api_key is None:
86
+ raise ValueError("Azure API key is not set")
87
+ os.environ["AZURE_API_KEY"] = api_key
88
+ api_base = (llm_config.azure_endpoint or os.environ.get("AZURE_OPENAI_ENDPOINT")
89
+ or os.environ.get("AZURE_API_BASE"))
90
+ if api_base is None:
91
+ raise ValueError("Azure endpoint is not set")
92
+ os.environ["AZURE_API_BASE"] = api_base
93
+
94
+ os.environ["AZURE_API_VERSION"] = llm_config.api_version
95
+ model = llm_config.azure_deployment or os.environ.get("AZURE_MODEL_DEPLOYMENT")
96
+ if model is None:
97
+ raise ValueError("Azure model deployment is not set")
98
+
99
+ client = LLM(
100
+ **llm_config.model_dump(
101
+ exclude={"type", "api_key", "azure_endpoint", "azure_deployment", "thinking", "api_type", "api_version"},
102
+ by_alias=True,
103
+ exclude_none=True,
104
+ exclude_unset=True,
105
+ ),
106
+ model=model,
107
+ api_version=llm_config.api_version,
108
+ )
109
+
110
+ yield _patch_llm_based_on_config(client, llm_config)
111
+
112
+
113
+ @register_llm_client(config_type=NIMModelConfig, wrapper_type=LLMFrameworkEnum.CREWAI)
114
+ async def nim_crewai(llm_config: NIMModelConfig, _builder: Builder):
115
+
116
+ from crewai import LLM
117
+
118
+ validate_no_responses_api(llm_config, LLMFrameworkEnum.CREWAI)
119
+
120
+ # Because CrewAI uses a different environment variable for the API key, we need to set it here manually
121
+ if llm_config.api_key is None and "NVIDIA_NIM_API_KEY" not in os.environ:
122
+ nvidia_api_key = os.getenv("NVIDIA_API_KEY")
123
+ if nvidia_api_key is not None:
124
+ os.environ["NVIDIA_NIM_API_KEY"] = nvidia_api_key
125
+
126
+ client = LLM(
127
+ **llm_config.model_dump(
128
+ exclude={"type", "model_name", "thinking", "api_type"},
129
+ by_alias=True,
130
+ exclude_none=True,
131
+ exclude_unset=True,
132
+ ),
133
+ model=f"nvidia_nim/{llm_config.model_name}",
134
+ )
135
+
136
+ yield _patch_llm_based_on_config(client, llm_config)
60
137
 
61
138
 
62
139
  @register_llm_client(config_type=OpenAIModelConfig, wrapper_type=LLMFrameworkEnum.CREWAI)
63
- async def openai_crewai(llm_config: OpenAIModelConfig, builder: Builder):
140
+ async def openai_crewai(llm_config: OpenAIModelConfig, _builder: Builder):
64
141
 
65
142
  from crewai import LLM
66
143
 
67
- config_obj = {
68
- **llm_config.model_dump(exclude={"type"}, by_alias=True),
69
- }
144
+ validate_no_responses_api(llm_config, LLMFrameworkEnum.CREWAI)
70
145
 
71
- client = LLM(**config_obj)
146
+ client = LLM(**llm_config.model_dump(
147
+ exclude={"type", "thinking", "api_type"}, by_alias=True, exclude_none=True, exclude_unset=True))
72
148
 
73
- if isinstance(llm_config, RetryMixin):
149
+ yield _patch_llm_based_on_config(client, llm_config)
74
150
 
75
- client = patch_with_retry(client,
76
- retries=llm_config.num_retries,
77
- retry_codes=llm_config.retry_on_status_codes,
78
- retry_on_messages=llm_config.retry_on_errors)
79
151
 
80
- yield client
152
+ @register_llm_client(config_type=LiteLlmModelConfig, wrapper_type=LLMFrameworkEnum.CREWAI)
153
+ async def litellm_crewai(llm_config: LiteLlmModelConfig, _builder: Builder):
154
+
155
+ from crewai import LLM
156
+
157
+ validate_no_responses_api(llm_config, LLMFrameworkEnum.CREWAI)
158
+
159
+ client = LLM(**llm_config.model_dump(
160
+ exclude={"type", "thinking", "api_type"}, by_alias=True, exclude_none=True, exclude_unset=True))
161
+
162
+ yield _patch_llm_based_on_config(client, llm_config)
@@ -13,7 +13,6 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- # pylint: disable=unused-import
17
16
  # flake8: noqa
18
17
  # isort:skip_file
19
18
 
@@ -1,13 +1,24 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nvidia-nat-crewai
3
- Version: 1.2rc9
3
+ Version: 1.4.0a20251121
4
4
  Summary: Subpackage for CrewAI integration in NeMo Agent toolkit
5
+ Author: NVIDIA Corporation
6
+ Maintainer: NVIDIA Corporation
7
+ License: Apache-2.0
8
+ Project-URL: documentation, https://docs.nvidia.com/nemo/agent-toolkit/latest/
9
+ Project-URL: source, https://github.com/NVIDIA/NeMo-Agent-Toolkit
5
10
  Keywords: ai,rag,agents
6
11
  Classifier: Programming Language :: Python
7
- Requires-Python: <3.13,>=3.11
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Python: <3.14,>=3.11
8
16
  Description-Content-Type: text/markdown
9
- Requires-Dist: nvidia-nat==v1.2-rc9
10
- Requires-Dist: crewai~=0.95.0
17
+ License-File: LICENSE-3rd-party.txt
18
+ License-File: LICENSE.md
19
+ Requires-Dist: nvidia-nat[litellm]==v1.4.0a20251121
20
+ Requires-Dist: crewai~=0.193.2
21
+ Dynamic: license-file
11
22
 
12
23
  <!--
13
24
  SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
@@ -0,0 +1,13 @@
1
+ nat/meta/pypi.md,sha256=T68FnThRzDGFf1LR8u-okM-r11-skSnKqSyI6HOktQY,1107
2
+ nat/plugins/crewai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ nat/plugins/crewai/crewai_callback_handler.py,sha256=il537F5tD9pFL1P9Q38ReOZasD-GgcBrm8BX_w0-xdo,8582
4
+ nat/plugins/crewai/llm.py,sha256=WiIvjAGGYJ7Ofu9YtEwHoLv2xLNCANbOunu8v7Oxn54,6602
5
+ nat/plugins/crewai/register.py,sha256=_R3bhGmz___696_NwyIcpw3koMBiWqIFoWEFJ0VAgXs,831
6
+ nat/plugins/crewai/tool_wrapper.py,sha256=BNKEPQQCLKtXNzGDAKBLCdmGJXe9lBOVI1hObha8hoI,1569
7
+ nvidia_nat_crewai-1.4.0a20251121.dist-info/licenses/LICENSE-3rd-party.txt,sha256=fOk5jMmCX9YoKWyYzTtfgl-SUy477audFC5hNY4oP7Q,284609
8
+ nvidia_nat_crewai-1.4.0a20251121.dist-info/licenses/LICENSE.md,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
9
+ nvidia_nat_crewai-1.4.0a20251121.dist-info/METADATA,sha256=3AP03dQTtuR1nGrtsi5aqsdhuWQaZZSug_KtwNTkzYs,1922
10
+ nvidia_nat_crewai-1.4.0a20251121.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ nvidia_nat_crewai-1.4.0a20251121.dist-info/entry_points.txt,sha256=YF5PUdQGr_OUDXB4TykElHJTsKT8yKkuE0bMX5n_RXs,58
12
+ nvidia_nat_crewai-1.4.0a20251121.dist-info/top_level.txt,sha256=8-CJ2cP6-f0ZReXe5Hzqp-5pvzzHz-5Ds5H2bGqh1-U,4
13
+ nvidia_nat_crewai-1.4.0a20251121.dist-info/RECORD,,