mirror of
https://github.com/hwchase17/langchain.git
synced 2025-08-07 12:06:43 +00:00
standard-tests: migrate to pytest-recording (#31425)
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
This commit is contained in:
parent
d7f90f233b
commit
3db1aa0ba6
Binary file not shown.
@ -1 +0,0 @@
|
||||
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
|
@ -1,7 +1,7 @@
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from langchain_tests.conftest import YamlGzipSerializer
|
||||
from langchain_tests.conftest import CustomPersister, CustomSerializer
|
||||
from langchain_tests.conftest import _base_vcr_config as _base_vcr_config
|
||||
from vcr import VCR # type: ignore[import-untyped]
|
||||
|
||||
@ -32,9 +32,6 @@ def vcr_config(_base_vcr_config: dict) -> dict: # noqa: F811
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vcr(vcr_config: dict) -> VCR:
|
||||
"""Override the default vcr fixture to include custom serializers"""
|
||||
my_vcr = VCR(**vcr_config)
|
||||
my_vcr.register_serializer("yaml.gz", YamlGzipSerializer)
|
||||
return my_vcr
|
||||
def pytest_recording_configure(config: dict, vcr: VCR) -> None:
|
||||
vcr.register_persister(CustomPersister())
|
||||
vcr.register_serializer("yaml.gz", CustomSerializer())
|
||||
|
@ -502,7 +502,7 @@ typing = [
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.3.62"
|
||||
version = "0.3.63"
|
||||
source = { editable = "../../core" }
|
||||
dependencies = [
|
||||
{ name = "jsonpatch" },
|
||||
@ -571,8 +571,8 @@ dependencies = [
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "pytest-benchmark" },
|
||||
{ name = "pytest-codspeed" },
|
||||
{ name = "pytest-recording" },
|
||||
{ name = "pytest-socket" },
|
||||
{ name = "pytest-vcr" },
|
||||
{ name = "syrupy" },
|
||||
{ name = "vcrpy" },
|
||||
]
|
||||
@ -587,8 +587,8 @@ requires-dist = [
|
||||
{ name = "pytest-asyncio", specifier = ">=0.20,<1" },
|
||||
{ name = "pytest-benchmark" },
|
||||
{ name = "pytest-codspeed" },
|
||||
{ name = "pytest-recording" },
|
||||
{ name = "pytest-socket", specifier = ">=0.6.0,<1" },
|
||||
{ name = "pytest-vcr" },
|
||||
{ name = "syrupy", specifier = ">=4,<5" },
|
||||
{ name = "vcrpy", specifier = ">=7.0" },
|
||||
]
|
||||
@ -1339,6 +1339,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-recording"
|
||||
version = "0.13.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
{ name = "vcrpy" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/32/9c/f4027c5f1693847b06d11caf4b4f6bb09f22c1581ada4663877ec166b8c6/pytest_recording-0.13.4.tar.gz", hash = "sha256:568d64b2a85992eec4ae0a419c855d5fd96782c5fb016784d86f18053792768c", size = 26576 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/42/c2/ce34735972cc42d912173e79f200fe66530225190c06655c5632a9d88f1e/pytest_recording-0.13.4-py3-none-any.whl", hash = "sha256:ad49a434b51b1c4f78e85b1e6b74fdcc2a0a581ca16e52c798c6ace971f7f439", size = 13723 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-retry"
|
||||
version = "1.7.0"
|
||||
@ -1375,19 +1388,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/03/27/14af9ef8321f5edc7527e47def2a21d8118c6f329a9342cc61387a0c0599/pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e", size = 14148 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-vcr"
|
||||
version = "1.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
{ name = "vcrpy" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1a/60/104c619483c1a42775d3f8b27293f1ecfc0728014874d065e68cb9702d49/pytest-vcr-1.0.2.tar.gz", hash = "sha256:23ee51b75abbcc43d926272773aae4f39f93aceb75ed56852d0bf618f92e1896", size = 3810 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/d3/ff520d11e6ee400602711d1ece8168dcfc5b6d8146fb7db4244a6ad6a9c3/pytest_vcr-1.0.2-py2.py3-none-any.whl", hash = "sha256:2f316e0539399bea0296e8b8401145c62b6f85e9066af7e57b6151481b0d6d9c", size = 4137 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-watcher"
|
||||
version = "0.4.3"
|
||||
|
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -1,7 +1,7 @@
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from langchain_tests.conftest import YamlGzipSerializer
|
||||
from langchain_tests.conftest import CustomPersister, CustomSerializer
|
||||
from langchain_tests.conftest import _base_vcr_config as _base_vcr_config
|
||||
from vcr import VCR # type: ignore[import-untyped]
|
||||
|
||||
@ -13,6 +13,7 @@ _EXTRA_HEADERS = [
|
||||
|
||||
|
||||
def remove_request_headers(request: Any) -> Any:
|
||||
"""Remove sensitive headers from the request."""
|
||||
for k in request.headers:
|
||||
request.headers[k] = "**REDACTED**"
|
||||
request.uri = "**REDACTED**"
|
||||
@ -20,6 +21,7 @@ def remove_request_headers(request: Any) -> Any:
|
||||
|
||||
|
||||
def remove_response_headers(response: dict) -> dict:
|
||||
"""Remove sensitive headers from the response."""
|
||||
for k in response["headers"]:
|
||||
response["headers"][k] = "**REDACTED**"
|
||||
return response
|
||||
@ -27,22 +29,16 @@ def remove_response_headers(response: dict) -> dict:
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def vcr_config(_base_vcr_config: dict) -> dict: # noqa: F811
|
||||
"""
|
||||
Extend the default configuration coming from langchain_tests.
|
||||
"""
|
||||
"""Extend the default configuration coming from langchain_tests."""
|
||||
config = _base_vcr_config.copy()
|
||||
config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS)
|
||||
config["before_record_request"] = remove_request_headers
|
||||
config["before_record_response"] = remove_response_headers
|
||||
config["serializer"] = "yaml.gz"
|
||||
config["path_transformer"] = VCR.ensure_suffix(".yaml.gz")
|
||||
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vcr(vcr_config: dict) -> VCR:
|
||||
"""Override the default vcr fixture to include custom serializers"""
|
||||
my_vcr = VCR(**vcr_config)
|
||||
my_vcr.register_serializer("yaml.gz", YamlGzipSerializer)
|
||||
return my_vcr
|
||||
def pytest_recording_configure(config: dict, vcr: VCR) -> None:
|
||||
vcr.register_persister(CustomPersister())
|
||||
vcr.register_serializer("yaml.gz", CustomSerializer())
|
||||
|
@ -479,7 +479,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.3.62"
|
||||
version = "0.3.63"
|
||||
source = { editable = "../../core" }
|
||||
dependencies = [
|
||||
{ name = "jsonpatch" },
|
||||
@ -637,8 +637,8 @@ dependencies = [
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "pytest-benchmark" },
|
||||
{ name = "pytest-codspeed" },
|
||||
{ name = "pytest-recording" },
|
||||
{ name = "pytest-socket" },
|
||||
{ name = "pytest-vcr" },
|
||||
{ name = "syrupy" },
|
||||
{ name = "vcrpy" },
|
||||
]
|
||||
@ -653,8 +653,8 @@ requires-dist = [
|
||||
{ name = "pytest-asyncio", specifier = ">=0.20,<1" },
|
||||
{ name = "pytest-benchmark" },
|
||||
{ name = "pytest-codspeed" },
|
||||
{ name = "pytest-recording" },
|
||||
{ name = "pytest-socket", specifier = ">=0.6.0,<1" },
|
||||
{ name = "pytest-vcr" },
|
||||
{ name = "syrupy", specifier = ">=4,<5" },
|
||||
{ name = "vcrpy", specifier = ">=7.0" },
|
||||
]
|
||||
@ -1514,6 +1514,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-recording"
|
||||
version = "0.13.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
{ name = "vcrpy" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/32/9c/f4027c5f1693847b06d11caf4b4f6bb09f22c1581ada4663877ec166b8c6/pytest_recording-0.13.4.tar.gz", hash = "sha256:568d64b2a85992eec4ae0a419c855d5fd96782c5fb016784d86f18053792768c", size = 26576 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/42/c2/ce34735972cc42d912173e79f200fe66530225190c06655c5632a9d88f1e/pytest_recording-0.13.4-py3-none-any.whl", hash = "sha256:ad49a434b51b1c4f78e85b1e6b74fdcc2a0a581ca16e52c798c6ace971f7f439", size = 13723 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-retry"
|
||||
version = "1.7.0"
|
||||
@ -1538,19 +1551,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/58/5d14cb5cb59409e491ebe816c47bf81423cd03098ea92281336320ae5681/pytest_socket-0.7.0-py3-none-any.whl", hash = "sha256:7e0f4642177d55d317bbd58fc68c6bd9048d6eadb2d46a89307fa9221336ce45", size = 6754 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-vcr"
|
||||
version = "1.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
{ name = "vcrpy" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1a/60/104c619483c1a42775d3f8b27293f1ecfc0728014874d065e68cb9702d49/pytest-vcr-1.0.2.tar.gz", hash = "sha256:23ee51b75abbcc43d926272773aae4f39f93aceb75ed56852d0bf618f92e1896", size = 3810 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/d3/ff520d11e6ee400602711d1ece8168dcfc5b6d8146fb7db4244a6ad6a9c3/pytest_vcr-1.0.2-py2.py3-none-any.whl", hash = "sha256:2f316e0539399bea0296e8b8401145c62b6f85e9066af7e57b6151481b0d6d9c", size = 4137 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-watcher"
|
||||
version = "0.4.3"
|
||||
|
@ -1,25 +1,85 @@
|
||||
import base64
|
||||
import gzip
|
||||
from os import PathLike
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
|
||||
import pytest
|
||||
from vcr import VCR # type: ignore[import-untyped]
|
||||
from vcr.serializers import yamlserializer # type: ignore[import-untyped]
|
||||
import yaml
|
||||
from vcr import VCR
|
||||
from vcr.persisters.filesystem import CassetteNotFoundError
|
||||
from vcr.request import Request
|
||||
|
||||
|
||||
class YamlGzipSerializer:
|
||||
@staticmethod
|
||||
def serialize(cassette_dict: dict) -> str:
|
||||
raw = yamlserializer.serialize(cassette_dict).encode("utf-8")
|
||||
compressed = gzip.compress(raw)
|
||||
return base64.b64encode(compressed).decode("ascii")
|
||||
class CustomSerializer:
|
||||
"""Custom serializer for VCR cassettes using YAML and gzip.
|
||||
|
||||
We're using a custom serializer to avoid the default yaml serializer
|
||||
used by VCR, which is not designed to be safe for untrusted input.
|
||||
|
||||
This step is an extra precaution necessary because the cassette files
|
||||
are in compressed YAML format, which makes it more difficult to inspect
|
||||
their contents during development or debugging.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def deserialize(data: str) -> dict:
|
||||
compressed = base64.b64decode(data.encode("ascii"))
|
||||
text = gzip.decompress(compressed).decode("utf-8")
|
||||
return yamlserializer.deserialize(text)
|
||||
def serialize(cassette_dict: dict) -> bytes:
|
||||
"""Convert cassette to YAML and compress it."""
|
||||
cassette_dict["requests"] = [
|
||||
request._to_dict() for request in cassette_dict["requests"]
|
||||
]
|
||||
yml = yaml.safe_dump(cassette_dict)
|
||||
return gzip.compress(yml.encode("utf-8"))
|
||||
|
||||
@staticmethod
|
||||
def deserialize(data: bytes) -> dict:
|
||||
"""Decompress data and convert it from YAML."""
|
||||
text = gzip.decompress(data).decode("utf-8")
|
||||
cassette = yaml.safe_load(text)
|
||||
cassette["requests"] = [
|
||||
Request._from_dict(request) for request in cassette["requests"]
|
||||
]
|
||||
return cassette
|
||||
|
||||
|
||||
class CustomPersister:
|
||||
"""A custom persister for VCR that uses the CustomSerializer."""
|
||||
|
||||
@classmethod
|
||||
def load_cassette(
|
||||
cls, cassette_path: Union[str, PathLike[str]], serializer: CustomSerializer
|
||||
) -> tuple[dict, dict]:
|
||||
"""Load a cassette from a file."""
|
||||
# If cassette path is already Path this is a no-op
|
||||
cassette_path = Path(cassette_path)
|
||||
if not cassette_path.is_file():
|
||||
raise CassetteNotFoundError(
|
||||
f"Cassette file {cassette_path} does not exist."
|
||||
)
|
||||
with cassette_path.open(mode="rb") as f:
|
||||
data = f.read()
|
||||
deser = serializer.deserialize(data)
|
||||
return deser["requests"], deser["responses"]
|
||||
|
||||
@staticmethod
|
||||
def save_cassette(
|
||||
cassette_path: Union[str, PathLike[str]],
|
||||
cassette_dict: dict,
|
||||
serializer: CustomSerializer,
|
||||
) -> None:
|
||||
"""Save a cassette to a file."""
|
||||
data = serializer.serialize(cassette_dict)
|
||||
# if cassette path is already Path this is no operation
|
||||
cassette_path = Path(cassette_path)
|
||||
cassette_folder = cassette_path.parent
|
||||
if not cassette_folder.exists():
|
||||
cassette_folder.mkdir(parents=True)
|
||||
with cassette_path.open("wb") as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
# A list of headers that should be filtered out of the cassettes.
|
||||
# These are typically associated with sensitive information and should
|
||||
# not be stored in cassettes.
|
||||
_BASE_FILTER_HEADERS = [
|
||||
("authorization", "PLACEHOLDER"),
|
||||
("x-api-key", "PLACEHOLDER"),
|
||||
@ -29,14 +89,15 @@ _BASE_FILTER_HEADERS = [
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def _base_vcr_config() -> dict:
|
||||
"""
|
||||
Configuration that every cassette will receive.
|
||||
"""Configuration that every cassette will receive.
|
||||
|
||||
(Anything permitted by vcr.VCR(**kwargs) can be put here.)
|
||||
"""
|
||||
return {
|
||||
"record_mode": "once",
|
||||
"filter_headers": _BASE_FILTER_HEADERS.copy(),
|
||||
"match_on": ["method", "scheme", "host", "port", "path", "query"],
|
||||
"match_on": ["method", "uri", "body"],
|
||||
"allow_playback_repeats": True,
|
||||
"decode_compressed_response": True,
|
||||
"cassette_library_dir": "tests/cassettes",
|
||||
"path_transformer": VCR.ensure_suffix(".yaml"),
|
||||
|
@ -6,7 +6,6 @@ from unittest.mock import MagicMock
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
import vcr # type: ignore[import-untyped]
|
||||
from langchain_core._api import warn_deprecated
|
||||
from langchain_core.callbacks import BaseCallbackHandler
|
||||
from langchain_core.language_models import BaseChatModel, GenericFakeChatModel
|
||||
@ -31,6 +30,7 @@ from pydantic.v1 import BaseModel as BaseModelV1
|
||||
from pydantic.v1 import Field as FieldV1
|
||||
from pytest_benchmark.fixture import BenchmarkFixture # type: ignore[import-untyped]
|
||||
from typing_extensions import Annotated, TypedDict
|
||||
from vcr.cassette import Cassette
|
||||
|
||||
from langchain_tests.unit_tests.chat_models import (
|
||||
ChatModelTests,
|
||||
@ -592,7 +592,7 @@ class ChatModelIntegrationTests(ChatModelTests):
|
||||
:caption: tests/conftest.py
|
||||
|
||||
import pytest
|
||||
from langchain_tests.conftest import YamlGzipSerializer
|
||||
from langchain_tests.conftest import CustomPersister, CustomSerializer
|
||||
from langchain_tests.conftest import _base_vcr_config as _base_vcr_config
|
||||
from vcr import VCR
|
||||
|
||||
@ -621,24 +621,26 @@ class ChatModelIntegrationTests(ChatModelTests):
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vcr(vcr_config: dict) -> VCR:
|
||||
\"\"\"Override the default vcr fixture to include custom serializers\"\"\"
|
||||
my_vcr = VCR(**vcr_config)
|
||||
my_vcr.register_serializer("yaml.gz", YamlGzipSerializer)
|
||||
return my_vcr
|
||||
def pytest_recording_configure(config: dict, vcr: VCR) -> None:
|
||||
vcr.register_persister(CustomPersister())
|
||||
vcr.register_serializer("yaml.gz", CustomSerializer())
|
||||
|
||||
|
||||
You can inspect the contents of the compressed cassettes (e.g., to
|
||||
ensure no sensitive information is recorded) using the serializer:
|
||||
ensure no sensitive information is recorded) using
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
gunzip -k /path/to/tests/cassettes/TestClass_test.yaml.gz
|
||||
|
||||
or by using the serializer:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_tests.conftest import YamlGzipSerializer
|
||||
from langchain_tests.conftest import CustomPersister, CustomSerializer
|
||||
|
||||
with open("/path/to/tests/cassettes/TestClass_test.yaml.gz", "r") as f:
|
||||
data = f.read()
|
||||
|
||||
YamlGzipSerializer.deserialize(data)
|
||||
cassette_path = "/path/to/tests/cassettes/TestClass_test.yaml.gz"
|
||||
requests, responses = CustomPersister().load_cassette(path, CustomSerializer())
|
||||
|
||||
3. Run tests to generate VCR cassettes.
|
||||
|
||||
@ -2826,8 +2828,9 @@ class ChatModelIntegrationTests(ChatModelTests):
|
||||
assert isinstance(response, AIMessage)
|
||||
|
||||
@pytest.mark.benchmark
|
||||
@pytest.mark.vcr
|
||||
def test_stream_time(
|
||||
self, model: BaseChatModel, benchmark: BenchmarkFixture, vcr: vcr.VCR
|
||||
self, model: BaseChatModel, benchmark: BenchmarkFixture, vcr: Cassette
|
||||
) -> None:
|
||||
"""Test that streaming does not introduce undue overhead.
|
||||
|
||||
@ -2857,12 +2860,13 @@ class ChatModelIntegrationTests(ChatModelTests):
|
||||
pytest.skip("VCR not set up.")
|
||||
|
||||
def _run() -> None:
|
||||
cassette_name = f"{self.__class__.__name__}_test_stream_time"
|
||||
with vcr.use_cassette(cassette_name, record_mode="once"):
|
||||
for _ in model.stream("Write a story about a cat."):
|
||||
pass
|
||||
for _ in model.stream("Write a story about a cat."):
|
||||
pass
|
||||
|
||||
benchmark(_run)
|
||||
if not vcr.responses:
|
||||
_run()
|
||||
else:
|
||||
benchmark(_run)
|
||||
|
||||
def invoke_with_audio_input(self, *, stream: bool = False) -> AIMessage:
|
||||
""":private:"""
|
||||
|
@ -693,7 +693,7 @@ class ChatModelUnitTests(ChatModelTests):
|
||||
:caption: tests/conftest.py
|
||||
|
||||
import pytest
|
||||
from langchain_tests.conftest import YamlGzipSerializer
|
||||
from langchain_tests.conftest import CustomPersister, CustomSerializer
|
||||
from langchain_tests.conftest import _base_vcr_config as _base_vcr_config
|
||||
from vcr import VCR
|
||||
|
||||
@ -722,24 +722,26 @@ class ChatModelUnitTests(ChatModelTests):
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vcr(vcr_config: dict) -> VCR:
|
||||
\"\"\"Override the default vcr fixture to include custom serializers\"\"\"
|
||||
my_vcr = VCR(**vcr_config)
|
||||
my_vcr.register_serializer("yaml.gz", YamlGzipSerializer)
|
||||
return my_vcr
|
||||
def pytest_recording_configure(config: dict, vcr: VCR) -> None:
|
||||
vcr.register_persister(CustomPersister())
|
||||
vcr.register_serializer("yaml.gz", CustomSerializer())
|
||||
|
||||
|
||||
You can inspect the contents of the compressed cassettes (e.g., to
|
||||
ensure no sensitive information is recorded) using the serializer:
|
||||
ensure no sensitive information is recorded) using
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
gunzip -k /path/to/tests/cassettes/TestClass_test.yaml.gz
|
||||
|
||||
or by using the serializer:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_tests.conftest import YamlGzipSerializer
|
||||
from langchain_tests.conftest import CustomPersister, CustomSerializer
|
||||
|
||||
with open("/path/to/tests/cassettes/TestClass_test.yaml.gz", "r") as f:
|
||||
data = f.read()
|
||||
|
||||
YamlGzipSerializer.deserialize(data)
|
||||
cassette_path = "/path/to/tests/cassettes/TestClass_test.yaml.gz"
|
||||
requests, responses = CustomPersister().load_cassette(path, CustomSerializer())
|
||||
|
||||
3. Run tests to generate VCR cassettes.
|
||||
|
||||
|
@ -15,7 +15,7 @@ dependencies = [
|
||||
"pytest-socket<1,>=0.6.0",
|
||||
"pytest-benchmark",
|
||||
"pytest-codspeed",
|
||||
"pytest-vcr",
|
||||
"pytest-recording",
|
||||
"vcrpy>=7.0",
|
||||
"numpy>=1.26.2; python_version<'3.13'",
|
||||
"numpy>=2.1.0; python_version>='3.13'",
|
||||
@ -42,6 +42,15 @@ langchain-core = { path = "../core", editable = true }
|
||||
[tool.mypy]
|
||||
disallow_untyped_defs = "True"
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "yaml"
|
||||
ignore_missing_imports = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "vcr.*"
|
||||
ignore_missing_imports = true
|
||||
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py39"
|
||||
|
||||
|
@ -304,7 +304,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.3.60"
|
||||
version = "0.3.63"
|
||||
source = { editable = "../core" }
|
||||
dependencies = [
|
||||
{ name = "jsonpatch" },
|
||||
@ -373,8 +373,8 @@ dependencies = [
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "pytest-benchmark" },
|
||||
{ name = "pytest-codspeed" },
|
||||
{ name = "pytest-recording" },
|
||||
{ name = "pytest-socket" },
|
||||
{ name = "pytest-vcr" },
|
||||
{ name = "syrupy" },
|
||||
{ name = "vcrpy" },
|
||||
]
|
||||
@ -404,8 +404,8 @@ requires-dist = [
|
||||
{ name = "pytest-asyncio", specifier = ">=0.20,<1" },
|
||||
{ name = "pytest-benchmark" },
|
||||
{ name = "pytest-codspeed" },
|
||||
{ name = "pytest-recording" },
|
||||
{ name = "pytest-socket", specifier = ">=0.6.0,<1" },
|
||||
{ name = "pytest-vcr" },
|
||||
{ name = "syrupy", specifier = ">=4,<5" },
|
||||
{ name = "vcrpy", specifier = ">=7.0" },
|
||||
]
|
||||
@ -1153,6 +1153,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/9b/952c70bd1fae9baa58077272e7f191f377c86d812263c21b361195e125e6/pytest_codspeed-3.2.0-py3-none-any.whl", hash = "sha256:54b5c2e986d6a28e7b0af11d610ea57bd5531cec8326abe486f1b55b09d91c39", size = 15007 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-recording"
|
||||
version = "0.13.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
{ name = "vcrpy" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/32/9c/f4027c5f1693847b06d11caf4b4f6bb09f22c1581ada4663877ec166b8c6/pytest_recording-0.13.4.tar.gz", hash = "sha256:568d64b2a85992eec4ae0a419c855d5fd96782c5fb016784d86f18053792768c", size = 26576 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/42/c2/ce34735972cc42d912173e79f200fe66530225190c06655c5632a9d88f1e/pytest_recording-0.13.4-py3-none-any.whl", hash = "sha256:ad49a434b51b1c4f78e85b1e6b74fdcc2a0a581ca16e52c798c6ace971f7f439", size = 13723 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-socket"
|
||||
version = "0.7.0"
|
||||
@ -1165,19 +1178,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/58/5d14cb5cb59409e491ebe816c47bf81423cd03098ea92281336320ae5681/pytest_socket-0.7.0-py3-none-any.whl", hash = "sha256:7e0f4642177d55d317bbd58fc68c6bd9048d6eadb2d46a89307fa9221336ce45", size = 6754 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-vcr"
|
||||
version = "1.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
{ name = "vcrpy" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1a/60/104c619483c1a42775d3f8b27293f1ecfc0728014874d065e68cb9702d49/pytest-vcr-1.0.2.tar.gz", hash = "sha256:23ee51b75abbcc43d926272773aae4f39f93aceb75ed56852d0bf618f92e1896", size = 3810 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/d3/ff520d11e6ee400602711d1ece8168dcfc5b6d8146fb7db4244a6ad6a9c3/pytest_vcr-1.0.2-py2.py3-none-any.whl", hash = "sha256:2f316e0539399bea0296e8b8401145c62b6f85e9066af7e57b6151481b0d6d9c", size = 4137 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
|
Loading…
Reference in New Issue
Block a user