nvidia-nat-test 1.4.0a20251022__py3-none-any.whl → 1.4.0a20251024__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-test might be problematic. Click here for more details.

nat/test/plugin.py CHANGED
@@ -14,7 +14,9 @@
14
14
  # limitations under the License.
15
15
 
16
16
  import os
17
+ import random
17
18
  import subprocess
19
+ import time
18
20
  import types
19
21
  import typing
20
22
  from collections.abc import AsyncGenerator
@@ -25,6 +27,8 @@ import pytest
25
27
  import pytest_asyncio
26
28
 
27
29
  if typing.TYPE_CHECKING:
30
+ import langsmith.client
31
+
28
32
  from docker.client import DockerClient
29
33
 
30
34
 
@@ -220,7 +224,19 @@ def azure_openai_keys_fixture(fail_missing: bool):
220
224
  yield require_env_variables(
221
225
  varnames=["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"],
222
226
  reason="Azure integration tests require the `AZURE_OPENAI_API_KEY` and `AZURE_OPENAI_ENDPOINT` environment "
223
- "variable to be defined.",
227
+ "variables to be defined.",
228
+ fail_missing=fail_missing)
229
+
230
+
231
+ @pytest.fixture(name="langfuse_keys", scope='session')
232
+ def langfuse_keys_fixture(fail_missing: bool):
233
+ """
234
+ Use for integration tests that require Langfuse credentials.
235
+ """
236
+ yield require_env_variables(
237
+ varnames=["LANGFUSE_PUBLIC_KEY", "LANGFUSE_SECRET_KEY"],
238
+ reason="Langfuse integration tests require the `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` environment "
239
+ "variables to be defined.",
224
240
  fail_missing=fail_missing)
225
241
 
226
242
 
@@ -250,6 +266,40 @@ def require_weave_fixture(fail_missing: bool) -> types.ModuleType:
250
266
  pytest.skip(reason=reason)
251
267
 
252
268
 
269
+ @pytest.fixture(name="langsmith_api_key", scope='session')
270
+ def langsmith_api_key_fixture(fail_missing: bool):
271
+ """
272
+ Use for integration tests that require a LangSmith API key.
273
+ """
274
+ yield require_env_variables(
275
+ varnames=["LANGSMITH_API_KEY"],
276
+ reason="LangSmith integration tests require the `LANGSMITH_API_KEY` environment variable to be defined.",
277
+ fail_missing=fail_missing)
278
+
279
+
280
+ @pytest.fixture(name="langsmith_client")
281
+ def langsmith_client_fixture(langsmith_api_key: str, fail_missing: bool) -> "langsmith.client.Client":
282
+ try:
283
+ import langsmith.client
284
+ client = langsmith.client.Client()
285
+ return client
286
+ except ImportError:
287
+ reason = "LangSmith integration tests require the `langsmith` package to be installed."
288
+ if fail_missing:
289
+ raise RuntimeError(reason)
290
+ pytest.skip(reason=reason)
291
+
292
+
293
+ @pytest.fixture(name="langsmith_project_name")
294
+ def langsmith_project_name_fixture(langsmith_client: "langsmith.client.Client") -> Generator[str]:
295
+ # Createa a unique project name for each test run
296
+ project_name = f"nat-e2e-test-{time.time()}-{random.random()}"
297
+ langsmith_client.create_project(project_name)
298
+ yield project_name
299
+
300
+ langsmith_client.delete_project(project_name=project_name)
301
+
302
+
253
303
  @pytest.fixture(name="require_docker", scope='session')
254
304
  def require_docker_fixture(fail_missing: bool) -> "DockerClient":
255
305
  """
@@ -298,8 +348,8 @@ def env_without_nat_log_level_fixture() -> dict[str, str]:
298
348
  return env
299
349
 
300
350
 
301
- @pytest.fixture(name="require_etcd", scope="session")
302
- def require_etcd_fixture(fail_missing: bool = False) -> bool:
351
+ @pytest.fixture(name="etcd_url", scope="session")
352
+ def etcd_url_fixture(fail_missing: bool = False) -> str:
303
353
  """
304
354
  To run these tests, an etcd server must be running
305
355
  """
@@ -307,21 +357,22 @@ def require_etcd_fixture(fail_missing: bool = False) -> bool:
307
357
 
308
358
  host = os.getenv("NAT_CI_ETCD_HOST", "localhost")
309
359
  port = os.getenv("NAT_CI_ETCD_PORT", "2379")
310
- health_url = f"http://{host}:{port}/health"
360
+ url = f"http://{host}:{port}"
361
+ health_url = f"{url}/health"
311
362
 
312
363
  try:
313
364
  response = requests.get(health_url, timeout=5)
314
365
  response.raise_for_status()
315
- return True
366
+ return url
316
367
  except: # noqa: E722
317
- failure_reason = f"Unable to connect to etcd server at {health_url}"
368
+ failure_reason = f"Unable to connect to etcd server at {url}"
318
369
  if fail_missing:
319
370
  raise RuntimeError(failure_reason)
320
371
  pytest.skip(reason=failure_reason)
321
372
 
322
373
 
323
374
  @pytest.fixture(name="milvus_uri", scope="session")
324
- def milvus_uri_fixture(require_etcd: bool, fail_missing: bool = False) -> str:
375
+ def milvus_uri_fixture(etcd_url: str, fail_missing: bool = False) -> str:
325
376
  """
326
377
  To run these tests, a Milvus server must be running
327
378
  """
@@ -452,7 +503,7 @@ def fixture_redis_server(fail_missing: bool) -> Generator[dict[str, str | int]]:
452
503
  pytest.skip(f"Error connecting to Redis server: {e}, skipping redis tests")
453
504
 
454
505
 
455
- @pytest_asyncio.fixture(name="mysql_server", scope="module")
506
+ @pytest_asyncio.fixture(name="mysql_server", scope="session")
456
507
  async def fixture_mysql_server(fail_missing: bool) -> AsyncGenerator[dict[str, str | int]]:
457
508
  """Fixture to safely skip MySQL based tests if MySQL is not running"""
458
509
  host = os.environ.get('NAT_CI_MYSQL_HOST', '127.0.0.1')
@@ -475,7 +526,7 @@ async def fixture_mysql_server(fail_missing: bool) -> AsyncGenerator[dict[str, s
475
526
  pytest.skip(f"Error connecting to MySQL server: {e}, skipping MySQL tests")
476
527
 
477
528
 
478
- @pytest.fixture(name="minio_server", scope="module")
529
+ @pytest.fixture(name="minio_server", scope="session")
479
530
  def minio_server_fixture(fail_missing: bool) -> Generator[dict[str, str | int]]:
480
531
  """Fixture to safely skip MinIO based tests if MinIO is not running"""
481
532
  host = os.getenv("NAT_CI_MINIO_HOST", "localhost")
@@ -502,17 +553,76 @@ def minio_server_fixture(fail_missing: bool) -> Generator[dict[str, str | int]]:
502
553
  aws_access_key_id=aws_access_key_id,
503
554
  aws_secret_access_key=aws_secret_access_key,
504
555
  endpoint_url=endpoint_url)
505
- client.head_bucket(Bucket=bucket_name)
556
+ client.list_buckets()
506
557
  yield minio_info
507
558
  except ImportError:
508
559
  if fail_missing:
509
560
  raise
510
561
  pytest.skip("aioboto3 not installed, skipping MinIO tests")
511
562
  except Exception as e:
512
- import botocore.exceptions
513
- if isinstance(e, botocore.exceptions.ClientError) and e.response['Error']['Code'] == '404':
514
- yield minio_info # Bucket does not exist, but server is reachable
515
- elif fail_missing:
563
+ if fail_missing:
564
+ raise
565
+ else:
566
+ pytest.skip(f"Error connecting to MinIO server: {e}, skipping MinIO tests")
567
+
568
+
569
+ @pytest.fixture(name="langfuse_bucket", scope="session")
570
+ def langfuse_bucket_fixture(fail_missing: bool, minio_server: dict[str, str | int]) -> Generator[str]:
571
+
572
+ bucket_name = os.getenv("NAT_CI_LANGFUSE_BUCKET", "langfuse")
573
+ try:
574
+ import botocore.session
575
+ session = botocore.session.get_session()
576
+
577
+ client = session.create_client("s3",
578
+ aws_access_key_id=minio_server["aws_access_key_id"],
579
+ aws_secret_access_key=minio_server["aws_secret_access_key"],
580
+ endpoint_url=minio_server["endpoint_url"])
581
+
582
+ buckets = client.list_buckets()
583
+ bucket_names = [b['Name'] for b in buckets['Buckets']]
584
+ if bucket_name not in bucket_names:
585
+ client.create_bucket(Bucket=bucket_name)
586
+
587
+ yield bucket_name
588
+ except ImportError:
589
+ if fail_missing:
590
+ raise
591
+ pytest.skip("aioboto3 not installed, skipping MinIO tests")
592
+ except Exception as e:
593
+ if fail_missing:
516
594
  raise
517
595
  else:
518
596
  pytest.skip(f"Error connecting to MinIO server: {e}, skipping MinIO tests")
597
+
598
+
599
+ @pytest.fixture(name="langfuse_url", scope="session")
600
+ def langfuse_url_fixture(fail_missing: bool, langfuse_bucket: str) -> str:
601
+ """
602
+ To run these tests, a langfuse server must be running.
603
+ """
604
+ import requests
605
+
606
+ host = os.getenv("NAT_CI_LANGFUSE_HOST", "localhost")
607
+ port = int(os.getenv("NAT_CI_LANGFUSE_PORT", "3000"))
608
+ url = f"http://{host}:{port}"
609
+ health_endpoint = f"{url}/api/public/health"
610
+ try:
611
+ response = requests.get(health_endpoint, timeout=5)
612
+ response.raise_for_status()
613
+
614
+ return url
615
+ except Exception as e:
616
+ reason = f"Unable to connect to Langfuse server at {url}: {e}"
617
+ if fail_missing:
618
+ raise RuntimeError(reason)
619
+ pytest.skip(reason=reason)
620
+
621
+
622
+ @pytest.fixture(name="langfuse_trace_url", scope="session")
623
+ def langfuse_trace_url_fixture(langfuse_url: str) -> str:
624
+ """
625
+ The langfuse_url fixture provides the base url, however the general.telemetry.tracing["langfuse"].endpoint expects
626
+ the trace url which is what this fixture provides.
627
+ """
628
+ return f"{langfuse_url}/api/public/otel/v1/traces"
nat/test/utils.py CHANGED
@@ -68,27 +68,19 @@ def locate_example_config(example_config_class: type,
68
68
  return config_path
69
69
 
70
70
 
71
- async def run_workflow(
72
- *,
73
- config: "Config | None" = None,
74
- config_file: "StrPath | None" = None,
75
- question: str,
76
- expected_answer: str,
77
- assert_expected_answer: bool = True,
78
- ) -> str:
79
- from nat.builder.workflow_builder import WorkflowBuilder
80
- from nat.runtime.loader import load_config
81
- from nat.runtime.session import SessionManager
82
-
83
- if config is None:
84
- assert config_file is not None, "Either config_file or config must be provided"
85
- assert Path(config_file).exists(), f"Config file {config_file} does not exist"
86
- config = load_config(config_file)
87
-
88
- async with WorkflowBuilder.from_config(config=config) as workflow_builder:
89
- workflow = SessionManager(await workflow_builder.build())
90
- async with workflow.run(question) as runner:
91
- result = await runner.result(to_type=str)
71
+ async def run_workflow(*,
72
+ config: "Config | None" = None,
73
+ config_file: "StrPath | None" = None,
74
+ question: str,
75
+ expected_answer: str,
76
+ assert_expected_answer: bool = True) -> str:
77
+ """
78
+ Test specific wrapper for `nat.utils.run_workflow` to run a workflow with a question and validate the expected
79
+ answer. This variant always sets the result type to `str`.
80
+ """
81
+ from nat.utils import run_workflow as nat_run_workflow
82
+
83
+ result = await nat_run_workflow(config=config, config_file=config_file, prompt=question, to_type=str)
92
84
 
93
85
  if assert_expected_answer:
94
86
  assert expected_answer.lower() in result.lower(), f"Expected '{expected_answer}' in '{result}'"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nvidia-nat-test
3
- Version: 1.4.0a20251022
3
+ Version: 1.4.0a20251024
4
4
  Summary: Testing utilities for NeMo Agent toolkit
5
5
  Author: NVIDIA Corporation
6
6
  Maintainer: NVIDIA Corporation
@@ -16,7 +16,7 @@ Requires-Python: <3.14,>=3.11
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE-3rd-party.txt
18
18
  License-File: LICENSE.md
19
- Requires-Dist: nvidia-nat==v1.4.0a20251022
19
+ Requires-Dist: nvidia-nat==v1.4.0a20251024
20
20
  Requires-Dist: langchain-community~=0.3
21
21
  Requires-Dist: pytest~=8.3
22
22
  Dynamic: license-file
@@ -5,14 +5,14 @@ nat/test/functions.py,sha256=ZxXVzfaLBGOpR5qtmMrKU7q-M9-vVGGj3Xi5mrw4vHY,3557
5
5
  nat/test/llm.py,sha256=f6bz6arAQjhjuOKFrLfu_U1LbiyFzQmpM-q8b-WKSrU,9550
6
6
  nat/test/memory.py,sha256=xki_A2yiMhEZuQk60K7t04QRqf32nQqnfzD5Iv7fkvw,1456
7
7
  nat/test/object_store_tests.py,sha256=PyJioOtoSzILPq6LuD-sOZ_89PIcgXWZweoHBQpK2zQ,4281
8
- nat/test/plugin.py,sha256=dvRXq_GHdXs95kHeJhG2PL1H6u5jbPgnvsdsedJFyGg,18386
8
+ nat/test/plugin.py,sha256=VbKEduSIll8TlyKQtLX5LJ0LzCyXQDwCTxeirZKnrMY,22257
9
9
  nat/test/register.py,sha256=o1BEA5fyxyFyCxXhQ6ArmtuNpgRyTEfvw6HdBgECPLI,897
10
10
  nat/test/tool_test_runner.py,sha256=SxavwXHkvCQDl_PUiiiqgvGfexKJJTeBdI5i1qk6AzI,21712
11
- nat/test/utils.py,sha256=Lml187P9SUP3IB_HhBaU1XNhiljcpOFFZOAxgQR1vQo,5936
12
- nvidia_nat_test-1.4.0a20251022.dist-info/licenses/LICENSE-3rd-party.txt,sha256=fOk5jMmCX9YoKWyYzTtfgl-SUy477audFC5hNY4oP7Q,284609
13
- nvidia_nat_test-1.4.0a20251022.dist-info/licenses/LICENSE.md,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
14
- nvidia_nat_test-1.4.0a20251022.dist-info/METADATA,sha256=STNcheOAgfsnvxSOrSCEpNc0y68F-q16iENgf5HN5Q4,1925
15
- nvidia_nat_test-1.4.0a20251022.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- nvidia_nat_test-1.4.0a20251022.dist-info/entry_points.txt,sha256=7dOP9XB6iMDqvav3gYx9VWUwA8RrFzhbAa8nGeC8e4Y,99
17
- nvidia_nat_test-1.4.0a20251022.dist-info/top_level.txt,sha256=8-CJ2cP6-f0ZReXe5Hzqp-5pvzzHz-5Ds5H2bGqh1-U,4
18
- nvidia_nat_test-1.4.0a20251022.dist-info/RECORD,,
11
+ nat/test/utils.py,sha256=x0xi3Oo2CYKqQdJ2nKqr9HvzBN6snjwzKGbVTOneqRs,5731
12
+ nvidia_nat_test-1.4.0a20251024.dist-info/licenses/LICENSE-3rd-party.txt,sha256=fOk5jMmCX9YoKWyYzTtfgl-SUy477audFC5hNY4oP7Q,284609
13
+ nvidia_nat_test-1.4.0a20251024.dist-info/licenses/LICENSE.md,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
14
+ nvidia_nat_test-1.4.0a20251024.dist-info/METADATA,sha256=XbMMIEco3xAp5nx7PzeNaNnuyUyyhp7-mAqWvt3vFno,1925
15
+ nvidia_nat_test-1.4.0a20251024.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ nvidia_nat_test-1.4.0a20251024.dist-info/entry_points.txt,sha256=7dOP9XB6iMDqvav3gYx9VWUwA8RrFzhbAa8nGeC8e4Y,99
17
+ nvidia_nat_test-1.4.0a20251024.dist-info/top_level.txt,sha256=8-CJ2cP6-f0ZReXe5Hzqp-5pvzzHz-5Ds5H2bGqh1-U,4
18
+ nvidia_nat_test-1.4.0a20251024.dist-info/RECORD,,