agentx-python 0.8.25__tar.gz → 0.8.26__tar.gz

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 (102) hide show
  1. {agentx_python-0.8.25 → agentx_python-0.8.26}/PKG-INFO +4 -1
  2. {agentx_python-0.8.25 → agentx_python-0.8.26}/README.md +1 -0
  3. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/__init__.py +1 -0
  4. agentx_python-0.8.26/agentx/integrations/nvidia_nim.py +70 -0
  5. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/openai.py +5 -1
  6. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/version.py +2 -2
  7. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx_python.egg-info/PKG-INFO +4 -1
  8. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx_python.egg-info/SOURCES.txt +1 -0
  9. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx_python.egg-info/requires.txt +3 -0
  10. {agentx_python-0.8.25 → agentx_python-0.8.26}/setup.py +2 -0
  11. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_integrations.py +179 -0
  12. {agentx_python-0.8.25 → agentx_python-0.8.26}/LICENSE +0 -0
  13. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/__init__.py +0 -0
  14. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/agentx.py +0 -0
  15. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/cli.py +0 -0
  16. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/__init__.py +0 -0
  17. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/_term.py +0 -0
  18. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/adapters/__init__.py +0 -0
  19. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/adapters/http_endpoint.py +0 -0
  20. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/adapters/precomputed.py +0 -0
  21. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/adapters/raw.py +0 -0
  22. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/client.py +0 -0
  23. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/datasets.py +0 -0
  24. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/evaluation_settings.py +0 -0
  25. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/models.py +0 -0
  26. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/prompts.py +0 -0
  27. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/reporting.py +0 -0
  28. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/results.py +0 -0
  29. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/runner.py +0 -0
  30. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/tool_schemas.py +0 -0
  31. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/evaluations/tracing.py +0 -0
  32. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/exceptions.py +0 -0
  33. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/export.py +0 -0
  34. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/feedback.py +0 -0
  35. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/_traced_call.py +0 -0
  36. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/anthropic.py +0 -0
  37. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/autogen.py +0 -0
  38. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/crewai.py +0 -0
  39. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/databricks.py +0 -0
  40. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/google_adk.py +0 -0
  41. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/google_genai.py +0 -0
  42. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/langchain.py +0 -0
  43. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/litellm.py +0 -0
  44. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/llamaindex.py +0 -0
  45. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/moveworks.py +0 -0
  46. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/integrations/openai_agents.py +0 -0
  47. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/__init__.py +0 -0
  48. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/_transport.py +0 -0
  49. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/agents.py +0 -0
  50. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/client.py +0 -0
  51. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/improvement_groups.py +0 -0
  52. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/judge_scorers.py +0 -0
  53. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/models.py +0 -0
  54. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/online_evaluators.py +0 -0
  55. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/patterns.py +0 -0
  56. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/profile.py +0 -0
  57. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/review_queue.py +0 -0
  58. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/rules.py +0 -0
  59. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/scorer_groups.py +0 -0
  60. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/scorers.py +0 -0
  61. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/sessions.py +0 -0
  62. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/monitor/signals.py +0 -0
  63. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/outcomes.py +0 -0
  64. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/projects.py +0 -0
  65. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/py.typed +0 -0
  66. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/resources/__init__.py +0 -0
  67. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/resources/agent.py +0 -0
  68. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/resources/conversation.py +0 -0
  69. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/resources/workforce.py +0 -0
  70. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/testing.py +0 -0
  71. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/traces.py +0 -0
  72. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/tracing/__init__.py +0 -0
  73. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/tracing/ci_types.py +0 -0
  74. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/tracing/eval_scope.py +0 -0
  75. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/tracing/framework_detect.py +0 -0
  76. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/tracing/ingest_client.py +0 -0
  77. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/tracing/tracer.py +0 -0
  78. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx/util.py +0 -0
  79. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx_python.egg-info/dependency_links.txt +0 -0
  80. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx_python.egg-info/entry_points.txt +0 -0
  81. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx_python.egg-info/not-zip-safe +0 -0
  82. {agentx_python-0.8.25 → agentx_python-0.8.26}/agentx_python.egg-info/top_level.txt +0 -0
  83. {agentx_python-0.8.25 → agentx_python-0.8.26}/setup.cfg +0 -0
  84. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_cli_launcher.py +0 -0
  85. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_deep_dive_fixes.py +0 -0
  86. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_docs_match_sdk.py +0 -0
  87. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_error_taxonomy.py +0 -0
  88. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_eval_scope.py +0 -0
  89. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_framework_detect.py +0 -0
  90. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_integration.py +0 -0
  91. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_judge_scorers.py +0 -0
  92. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_multi_judge.py +0 -0
  93. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_pairwise.py +0 -0
  94. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_pattern_update_merge.py +0 -0
  95. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_review_queue.py +0 -0
  96. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_runner_features.py +0 -0
  97. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_selfhost_analysis_fallback.py +0 -0
  98. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_selfhost_compat.py +0 -0
  99. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_span_tree.py +0 -0
  100. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_testing.py +0 -0
  101. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_wire_models.py +0 -0
  102. {agentx_python-0.8.25 → agentx_python-0.8.26}/tests/test_workforce_binding.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentx-python
3
- Version: 0.8.25
3
+ Version: 0.8.26
4
4
  Summary: Official Python SDK for AgentX (https://www.agentx.so/)
5
5
  Home-page: https://github.com/AgentX-ai/AgentX-python
6
6
  Author: Robin Wang and AgentX Team
@@ -23,6 +23,8 @@ Provides-Extra: openai-agents
23
23
  Requires-Dist: openai-agents>=0.0.3; extra == "openai-agents"
24
24
  Provides-Extra: openai
25
25
  Requires-Dist: openai>=1.0.0; extra == "openai"
26
+ Provides-Extra: nvidia-nim
27
+ Requires-Dist: openai>=1.0.0; extra == "nvidia-nim"
26
28
  Provides-Extra: anthropic
27
29
  Requires-Dist: anthropic>=0.25.0; extra == "anthropic"
28
30
  Provides-Extra: google-adk
@@ -250,6 +252,7 @@ extra:
250
252
  | CrewAI | `pip install "agentx-python[crewai]"` | `AgentXCrewObserver` |
251
253
  | OpenAI Agents SDK | `pip install "agentx-python[openai-agents]"` | `AgentXTracingProcessor` |
252
254
  | OpenAI (raw client) | `pip install "agentx-python[openai]"` | `patch_openai_client` |
255
+ | NVIDIA NIM | `pip install "agentx-python[nvidia-nim]"` | `patch_nim_client` |
253
256
  | Anthropic | `pip install "agentx-python[anthropic]"` | `patch_anthropic_client` |
254
257
  | Google ADK | `pip install "agentx-python[google-adk]"` | `AgentXADKPlugin` |
255
258
  | Google GenAI (Gemini) | `pip install "agentx-python[google-genai]"` | `patch_genai_client` |
@@ -185,6 +185,7 @@ extra:
185
185
  | CrewAI | `pip install "agentx-python[crewai]"` | `AgentXCrewObserver` |
186
186
  | OpenAI Agents SDK | `pip install "agentx-python[openai-agents]"` | `AgentXTracingProcessor` |
187
187
  | OpenAI (raw client) | `pip install "agentx-python[openai]"` | `patch_openai_client` |
188
+ | NVIDIA NIM | `pip install "agentx-python[nvidia-nim]"` | `patch_nim_client` |
188
189
  | Anthropic | `pip install "agentx-python[anthropic]"` | `patch_anthropic_client` |
189
190
  | Google ADK | `pip install "agentx-python[google-adk]"` | `AgentXADKPlugin` |
190
191
  | Google GenAI (Gemini) | `pip install "agentx-python[google-genai]"` | `patch_genai_client` |
@@ -7,4 +7,5 @@
7
7
  # from agentx.integrations.anthropic import patch_anthropic_client
8
8
  # from agentx.integrations.google_adk import AgentXADKPlugin
9
9
  # from agentx.integrations.google_genai import patch_genai_client
10
+ # from agentx.integrations.nvidia_nim import patch_nim_client
10
11
  # from agentx.integrations.moveworks import MoveworksImporter # Data API pull sync, not in-process
@@ -0,0 +1,70 @@
1
+ """
2
+ NVIDIA NIM integration for AgentX production tracing.
3
+
4
+ NIM (NVIDIA Inference Microservices) serves models behind an OpenAI-compatible
5
+ ``/v1/chat/completions`` API, so the client you patch is the ordinary ``openai``
6
+ Python client pointed at a NIM endpoint - a local NIM container
7
+ (``http://localhost:8000/v1``) or NVIDIA's hosted API
8
+ (``https://integrate.api.nvidia.com/v1``). This module reuses the OpenAI patch
9
+ machinery verbatim and differs in exactly one way: traces are stamped
10
+ ``framework="nvidia-nim"``, so NIM traffic gets its own row in Monitor's
11
+ Platforms chart and the framework filters instead of blending into "openai".
12
+
13
+ Usage::
14
+
15
+ from agentx.integrations.nvidia_nim import patch_nim_client
16
+ import openai
17
+
18
+ nim = openai.OpenAI(
19
+ base_url="http://localhost:8000/v1", # or https://integrate.api.nvidia.com/v1
20
+ api_key=os.environ.get("NVIDIA_API_KEY", "not-needed-for-local-nim"),
21
+ )
22
+ patch_nim_client(nim, agentx.tracer, name="nim-agent")
23
+
24
+ # All subsequent nim.chat.completions.create() calls are now traced.
25
+
26
+ Works with both ``openai.OpenAI`` and ``openai.AsyncOpenAI`` clients. Token
27
+ usage comes straight off the response's OpenAI-shaped ``usage`` block; NIM
28
+ reports no prompt-cache fields, so cache token counts stay unset.
29
+
30
+ Streaming calls (``stream=True``) are passed through untouched and are not
31
+ currently traced - same posture as ``patch_openai_client``, see its docstring.
32
+
33
+ Requires: ``pip install "agentx-python[nvidia-nim]"`` (installs the ``openai``
34
+ client package; there is no separate NIM SDK dependency).
35
+ """
36
+ from __future__ import annotations
37
+
38
+ from typing import Any, Dict, Optional
39
+
40
+ from agentx.tracing.tracer import Tracer
41
+ from agentx.integrations.openai import _patch_chat_completions_create
42
+
43
+ NIM_FRAMEWORK = "nvidia-nim"
44
+
45
+
46
+ def patch_nim_client(
47
+ client: Any,
48
+ tracer: Tracer,
49
+ name: str = "nim-agent",
50
+ metadata: Optional[Dict[str, Any]] = None,
51
+ session_id: Optional[str] = None,
52
+ ) -> None:
53
+ """
54
+ Monkey-patch ``client.chat.completions.create`` on an OpenAI-compatible
55
+ client pointed at a NIM endpoint, sending a trace for every non-streaming
56
+ call with ``framework="nvidia-nim"``.
57
+
58
+ The original method is still called and its return value passed through
59
+ unchanged. Sync and async clients both work; ``stream=True`` calls pass
60
+ through untraced. Patching is idempotent - and because it shares the guard
61
+ with ``patch_openai_client``, whichever of the two patched a given client
62
+ first wins (patch each client with the integration that matches where its
63
+ ``base_url`` actually points).
64
+ """
65
+ chat = getattr(client, "chat", None)
66
+ completions = getattr(chat, "completions", None) if chat is not None else None
67
+ if completions is None:
68
+ raise ValueError("Provided client does not have a .chat.completions attribute")
69
+
70
+ _patch_chat_completions_create(completions, tracer, name, metadata, session_id, framework=NIM_FRAMEWORK)
@@ -113,7 +113,11 @@ def _patch_chat_completions_create(
113
113
  name: str,
114
114
  metadata: Optional[Dict[str, Any]],
115
115
  session_id: Optional[str],
116
+ framework: str = "openai",
116
117
  ) -> None:
118
+ # `framework` exists for OpenAI-compatible endpoints served by other vendors
119
+ # (agentx.integrations.nvidia_nim stamps "nvidia-nim" through here) - the request/response
120
+ # shapes are identical, so they share this machinery instead of duplicating it.
117
121
  original = completions_resource.create
118
122
  if getattr(original, "_agentx_patched", False):
119
123
  return # already patched
@@ -148,7 +152,7 @@ def _patch_chat_completions_create(
148
152
  finish_llm_call(
149
153
  tracer,
150
154
  name=name,
151
- framework="openai",
155
+ framework=framework,
152
156
  metadata=metadata,
153
157
  session_id=session_id,
154
158
  start_t=start_t,
@@ -1,7 +1,7 @@
1
- VERSION = "0.8.25"
1
+ VERSION = "0.8.26"
2
2
 
3
3
  # The AgentX-trace-eval release this SDK version is tested against - what `agentx-trace-eval`
4
4
  # installs and converges to (see agentx/cli.py). Bump together with VERSION when releasing, so
5
5
  # every published SDK names a known-good engine+dashboard pair. Users can override with
6
6
  # AGENTX_TRACE_EVAL_VERSION=<tag|latest>.
7
- ENGINE_VERSION = "v0.3.28"
7
+ ENGINE_VERSION = "v0.3.29"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentx-python
3
- Version: 0.8.25
3
+ Version: 0.8.26
4
4
  Summary: Official Python SDK for AgentX (https://www.agentx.so/)
5
5
  Home-page: https://github.com/AgentX-ai/AgentX-python
6
6
  Author: Robin Wang and AgentX Team
@@ -23,6 +23,8 @@ Provides-Extra: openai-agents
23
23
  Requires-Dist: openai-agents>=0.0.3; extra == "openai-agents"
24
24
  Provides-Extra: openai
25
25
  Requires-Dist: openai>=1.0.0; extra == "openai"
26
+ Provides-Extra: nvidia-nim
27
+ Requires-Dist: openai>=1.0.0; extra == "nvidia-nim"
26
28
  Provides-Extra: anthropic
27
29
  Requires-Dist: anthropic>=0.25.0; extra == "anthropic"
28
30
  Provides-Extra: google-adk
@@ -250,6 +252,7 @@ extra:
250
252
  | CrewAI | `pip install "agentx-python[crewai]"` | `AgentXCrewObserver` |
251
253
  | OpenAI Agents SDK | `pip install "agentx-python[openai-agents]"` | `AgentXTracingProcessor` |
252
254
  | OpenAI (raw client) | `pip install "agentx-python[openai]"` | `patch_openai_client` |
255
+ | NVIDIA NIM | `pip install "agentx-python[nvidia-nim]"` | `patch_nim_client` |
253
256
  | Anthropic | `pip install "agentx-python[anthropic]"` | `patch_anthropic_client` |
254
257
  | Google ADK | `pip install "agentx-python[google-adk]"` | `AgentXADKPlugin` |
255
258
  | Google GenAI (Gemini) | `pip install "agentx-python[google-genai]"` | `patch_genai_client` |
@@ -42,6 +42,7 @@ agentx/integrations/langchain.py
42
42
  agentx/integrations/litellm.py
43
43
  agentx/integrations/llamaindex.py
44
44
  agentx/integrations/moveworks.py
45
+ agentx/integrations/nvidia_nim.py
45
46
  agentx/integrations/openai.py
46
47
  agentx/integrations/openai_agents.py
47
48
  agentx/monitor/__init__.py
@@ -45,6 +45,9 @@ litellm>=1.0.0
45
45
  [llamaindex]
46
46
  llama-index-core>=0.10.0
47
47
 
48
+ [nvidia-nim]
49
+ openai>=1.0.0
50
+
48
51
  [openai]
49
52
  openai>=1.0.0
50
53
 
@@ -51,6 +51,8 @@ setup(
51
51
  "crewai": ["crewai>=0.80.0"],
52
52
  "openai-agents": ["openai-agents>=0.0.3"],
53
53
  "openai": ["openai>=1.0.0"],
54
+ # NIM endpoints speak the OpenAI-compatible API; the client package IS openai.
55
+ "nvidia-nim": ["openai>=1.0.0"],
54
56
  "anthropic": ["anthropic>=0.25.0"],
55
57
  "google-adk": ["google-adk>=1.0.0"],
56
58
  "google-genai": ["google-genai>=1.0.0"],
@@ -513,6 +513,185 @@ def test_openai_sync_client_records_errors():
513
513
  assert kwargs["error"] == "rate limited"
514
514
 
515
515
 
516
+ def test_openai_patch_stamps_openai_framework():
517
+ # Regression guard for the shared-machinery refactor: _patch_chat_completions_create
518
+ # grew a framework parameter for nvidia_nim.py, and the OpenAI default must stay "openai".
519
+ from agentx.integrations.openai import patch_openai_client
520
+
521
+ class FakeCompletions:
522
+ def create(self, **kwargs):
523
+ return _FakeOpenAIChatCompletion("hello")
524
+
525
+ client = _fake_openai_client(FakeCompletions())
526
+ tracer = make_tracer()
527
+ patch_openai_client(client, tracer, name="gpt-agent")
528
+ client.chat.completions.create(model="gpt-4o-mini", messages=[{"role": "user", "content": "hi"}])
529
+
530
+ _, kwargs = tracer._send.call_args
531
+ assert kwargs["framework"] == "openai"
532
+
533
+
534
+ # ---------------------------------------------------------------------------
535
+ # 6b. nvidia_nim.py — the OpenAI-compatible patch with the NIM framework label
536
+ # ---------------------------------------------------------------------------
537
+
538
+ def test_nim_sync_client_traces_call_with_nim_framework():
539
+ from agentx.integrations.nvidia_nim import patch_nim_client
540
+
541
+ response = _FakeOpenAIChatCompletion("hello from nim")
542
+
543
+ class FakeCompletions:
544
+ def create(self, **kwargs):
545
+ return response
546
+
547
+ client = _fake_openai_client(FakeCompletions())
548
+ tracer = make_tracer()
549
+ patch_nim_client(client, tracer, name="nim-agent")
550
+
551
+ result = client.chat.completions.create(
552
+ model="meta/llama-3.1-8b-instruct", messages=[{"role": "user", "content": "hi"}]
553
+ )
554
+
555
+ assert result is response
556
+ tracer._send.assert_called_once()
557
+ _, kwargs = tracer._send.call_args
558
+ assert kwargs["framework"] == "nvidia-nim"
559
+ assert kwargs["name"] == "nim-agent"
560
+ assert kwargs["output"] == "hello from nim"
561
+ assert kwargs["model"] == "meta/llama-3.1-8b-instruct"
562
+ assert kwargs["input_tokens"] == 12
563
+ assert kwargs["output_tokens"] == 6
564
+ # NIM reports no prompt-cache fields; the counts must stay unset, not become 0.
565
+ assert not kwargs.get("cache_read_tokens")
566
+
567
+
568
+ def test_nim_async_client_traces_the_real_response():
569
+ from agentx.integrations.nvidia_nim import patch_nim_client
570
+
571
+ response = _FakeOpenAIChatCompletion("hello from async nim")
572
+
573
+ class FakeAsyncCompletions:
574
+ async def create(self, **kwargs):
575
+ await asyncio.sleep(0.01)
576
+ return response
577
+
578
+ client = _fake_openai_client(FakeAsyncCompletions())
579
+ tracer = make_tracer()
580
+ patch_nim_client(client, tracer, name="nim-agent")
581
+
582
+ result = asyncio.run(
583
+ client.chat.completions.create(model="meta/llama-3.1-8b-instruct", messages=[{"role": "user", "content": "hi"}])
584
+ )
585
+
586
+ assert result is response
587
+ tracer._send.assert_called_once()
588
+ _, kwargs = tracer._send.call_args
589
+ assert kwargs["framework"] == "nvidia-nim"
590
+ assert kwargs["output"] == "hello from async nim"
591
+ assert kwargs["input_tokens"] == 12
592
+
593
+
594
+ def test_nim_streaming_calls_are_passed_through_untraced():
595
+ from agentx.integrations.nvidia_nim import patch_nim_client
596
+
597
+ sentinel_stream = object()
598
+
599
+ class FakeCompletions:
600
+ def create(self, **kwargs):
601
+ assert kwargs.get("stream") is True
602
+ return sentinel_stream
603
+
604
+ client = _fake_openai_client(FakeCompletions())
605
+ tracer = make_tracer()
606
+ patch_nim_client(client, tracer, name="nim-agent")
607
+
608
+ result = client.chat.completions.create(
609
+ model="meta/llama-3.1-8b-instruct", messages=[{"role": "user", "content": "hi"}], stream=True
610
+ )
611
+
612
+ assert result is sentinel_stream
613
+ tracer._send.assert_not_called()
614
+
615
+
616
+ def test_nim_sync_client_records_errors():
617
+ from agentx.integrations.nvidia_nim import patch_nim_client
618
+
619
+ class FakeCompletions:
620
+ def create(self, **kwargs):
621
+ raise ValueError("nim endpoint unavailable")
622
+
623
+ client = _fake_openai_client(FakeCompletions())
624
+ tracer = make_tracer()
625
+ patch_nim_client(client, tracer, name="nim-agent")
626
+
627
+ with pytest.raises(ValueError, match="nim endpoint unavailable"):
628
+ client.chat.completions.create(model="meta/llama-3.1-8b-instruct", messages=[{"role": "user", "content": "hi"}])
629
+
630
+ tracer._send.assert_called_once()
631
+ _, kwargs = tracer._send.call_args
632
+ assert kwargs["error"] == "nim endpoint unavailable"
633
+ assert kwargs["framework"] == "nvidia-nim"
634
+
635
+
636
+ def test_nim_patch_is_idempotent_and_first_patch_wins():
637
+ from agentx.integrations.nvidia_nim import patch_nim_client
638
+ from agentx.integrations.openai import patch_openai_client
639
+
640
+ class FakeCompletions:
641
+ def create(self, **kwargs):
642
+ return _FakeOpenAIChatCompletion("once")
643
+
644
+ client = _fake_openai_client(FakeCompletions())
645
+ tracer = make_tracer()
646
+ patch_nim_client(client, tracer, name="nim-agent")
647
+ # Double NIM patch and a later OpenAI patch are both no-ops (shared _agentx_patched guard):
648
+ # exactly one trace per call, and the first patch's framework label stays.
649
+ patch_nim_client(client, tracer, name="nim-agent")
650
+ patch_openai_client(client, tracer, name="gpt-agent")
651
+
652
+ client.chat.completions.create(model="meta/llama-3.1-8b-instruct", messages=[{"role": "user", "content": "hi"}])
653
+
654
+ tracer._send.assert_called_once()
655
+ _, kwargs = tracer._send.call_args
656
+ assert kwargs["framework"] == "nvidia-nim"
657
+
658
+
659
+ def test_nim_rejects_client_without_chat_completions():
660
+ from agentx.integrations.nvidia_nim import patch_nim_client
661
+
662
+ with pytest.raises(ValueError, match="chat.completions"):
663
+ patch_nim_client(object(), make_tracer())
664
+
665
+
666
+ def test_nim_request_tools_land_in_trace_metadata():
667
+ # The docs promise the request's tools=[...] definitions feed the unregistered-tool
668
+ # listing via metadata.tools - pin the shared capture path (openai.py machinery) here.
669
+ from agentx.integrations.nvidia_nim import patch_nim_client
670
+
671
+ class FakeCompletions:
672
+ def create(self, **kwargs):
673
+ return _FakeOpenAIChatCompletion("used a tool")
674
+
675
+ tool_def = {
676
+ "type": "function",
677
+ "function": {"name": "lookup_order", "parameters": {"type": "object", "properties": {}}},
678
+ }
679
+ client = _fake_openai_client(FakeCompletions())
680
+ tracer = make_tracer()
681
+ patch_nim_client(client, tracer, name="nim-agent", metadata={"env": "test"})
682
+
683
+ client.chat.completions.create(
684
+ model="meta/llama-3.1-8b-instruct",
685
+ messages=[{"role": "user", "content": "hi"}],
686
+ tools=[tool_def],
687
+ )
688
+
689
+ _, kwargs = tracer._send.call_args
690
+ assert kwargs["metadata"]["tools"] == [tool_def]
691
+ # The caller's own static metadata must survive the tools merge.
692
+ assert kwargs["metadata"]["env"] == "test"
693
+
694
+
516
695
  # ---------------------------------------------------------------------------
517
696
  # 7. langchain.py — nested-run state cleanup + TTL safety net
518
697
  # ---------------------------------------------------------------------------
File without changes
File without changes