mirror of
https://github.com/hwchase17/langchain.git
synced 2025-09-27 06:18:05 +00:00
Merge branch 'v0.3rc' into bagatur/fireworks_0.3
This commit is contained in:
114
.github/workflows/_dependencies.yml
vendored
114
.github/workflows/_dependencies.yml
vendored
@@ -1,114 +0,0 @@
|
|||||||
name: dependencies
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
working-directory:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: "From which folder this pipeline executes"
|
|
||||||
langchain-location:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: "Relative path to the langchain library folder"
|
|
||||||
python-version:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: "Python version to use"
|
|
||||||
|
|
||||||
env:
|
|
||||||
POETRY_VERSION: "1.7.1"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ${{ inputs.working-directory }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: dependency checks ${{ inputs.python-version }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Python ${{ inputs.python-version }} + Poetry ${{ env.POETRY_VERSION }}
|
|
||||||
uses: "./.github/actions/poetry_setup"
|
|
||||||
with:
|
|
||||||
python-version: ${{ inputs.python-version }}
|
|
||||||
poetry-version: ${{ env.POETRY_VERSION }}
|
|
||||||
working-directory: ${{ inputs.working-directory }}
|
|
||||||
cache-key: pydantic-cross-compat
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
shell: bash
|
|
||||||
run: poetry install
|
|
||||||
|
|
||||||
- name: Check imports with base dependencies
|
|
||||||
shell: bash
|
|
||||||
run: poetry run make check_imports
|
|
||||||
|
|
||||||
- name: Install test dependencies
|
|
||||||
shell: bash
|
|
||||||
run: poetry install --with test
|
|
||||||
|
|
||||||
- name: Install langchain editable
|
|
||||||
working-directory: ${{ inputs.working-directory }}
|
|
||||||
if: ${{ inputs.langchain-location }}
|
|
||||||
env:
|
|
||||||
LANGCHAIN_LOCATION: ${{ inputs.langchain-location }}
|
|
||||||
run: |
|
|
||||||
poetry run pip install -e "$LANGCHAIN_LOCATION"
|
|
||||||
|
|
||||||
- name: Install the opposite major version of pydantic
|
|
||||||
# If normal tests use pydantic v1, here we'll use v2, and vice versa.
|
|
||||||
shell: bash
|
|
||||||
# airbyte currently doesn't support pydantic v2
|
|
||||||
if: ${{ !startsWith(inputs.working-directory, 'libs/partners/airbyte') }}
|
|
||||||
run: |
|
|
||||||
# Determine the major part of pydantic version
|
|
||||||
REGULAR_VERSION=$(poetry run python -c "import pydantic; print(pydantic.__version__)" | cut -d. -f1)
|
|
||||||
|
|
||||||
if [[ "$REGULAR_VERSION" == "1" ]]; then
|
|
||||||
PYDANTIC_DEP=">=2.1,<3"
|
|
||||||
TEST_WITH_VERSION="2"
|
|
||||||
elif [[ "$REGULAR_VERSION" == "2" ]]; then
|
|
||||||
PYDANTIC_DEP="<2"
|
|
||||||
TEST_WITH_VERSION="1"
|
|
||||||
else
|
|
||||||
echo "Unexpected pydantic major version '$REGULAR_VERSION', cannot determine which version to use for cross-compatibility test."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install via `pip` instead of `poetry add` to avoid changing lockfile,
|
|
||||||
# which would prevent caching from working: the cache would get saved
|
|
||||||
# to a different key than where it gets loaded from.
|
|
||||||
poetry run pip install "pydantic${PYDANTIC_DEP}"
|
|
||||||
|
|
||||||
# Ensure that the correct pydantic is installed now.
|
|
||||||
echo "Checking pydantic version... Expecting ${TEST_WITH_VERSION}"
|
|
||||||
|
|
||||||
# Determine the major part of pydantic version
|
|
||||||
CURRENT_VERSION=$(poetry run python -c "import pydantic; print(pydantic.__version__)" | cut -d. -f1)
|
|
||||||
|
|
||||||
# Check that the major part of pydantic version is as expected, if not
|
|
||||||
# raise an error
|
|
||||||
if [[ "$CURRENT_VERSION" != "$TEST_WITH_VERSION" ]]; then
|
|
||||||
echo "Error: expected pydantic version ${CURRENT_VERSION} to have been installed, but found: ${TEST_WITH_VERSION}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Found pydantic version ${CURRENT_VERSION}, as expected"
|
|
||||||
- name: Run pydantic compatibility tests
|
|
||||||
# airbyte currently doesn't support pydantic v2
|
|
||||||
if: ${{ !startsWith(inputs.working-directory, 'libs/partners/airbyte') }}
|
|
||||||
shell: bash
|
|
||||||
run: make test
|
|
||||||
|
|
||||||
- name: Ensure the tests did not create any additional files
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
STATUS="$(git status)"
|
|
||||||
echo "$STATUS"
|
|
||||||
|
|
||||||
# grep will exit non-zero if the target message isn't found,
|
|
||||||
# and `set -e` above will cause the step to fail.
|
|
||||||
echo "$STATUS" | grep 'nothing to commit, working tree clean'
|
|
15
.github/workflows/check_diffs.yml
vendored
15
.github/workflows/check_diffs.yml
vendored
@@ -89,19 +89,6 @@ jobs:
|
|||||||
python-version: ${{ matrix.job-configs.python-version }}
|
python-version: ${{ matrix.job-configs.python-version }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
dependencies:
|
|
||||||
name: cd ${{ matrix.job-configs.working-directory }}
|
|
||||||
needs: [ build ]
|
|
||||||
if: ${{ needs.build.outputs.dependencies != '[]' }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
job-configs: ${{ fromJson(needs.build.outputs.dependencies) }}
|
|
||||||
uses: ./.github/workflows/_dependencies.yml
|
|
||||||
with:
|
|
||||||
working-directory: ${{ matrix.job-configs.working-directory }}
|
|
||||||
python-version: ${{ matrix.job-configs.python-version }}
|
|
||||||
secrets: inherit
|
|
||||||
|
|
||||||
extended-tests:
|
extended-tests:
|
||||||
name: "cd ${{ matrix.job-configs.working-directory }} / make extended_tests #${{ matrix.job-configs.python-version }}"
|
name: "cd ${{ matrix.job-configs.working-directory }} / make extended_tests #${{ matrix.job-configs.python-version }}"
|
||||||
needs: [ build ]
|
needs: [ build ]
|
||||||
@@ -149,7 +136,7 @@ jobs:
|
|||||||
echo "$STATUS" | grep 'nothing to commit, working tree clean'
|
echo "$STATUS" | grep 'nothing to commit, working tree clean'
|
||||||
ci_success:
|
ci_success:
|
||||||
name: "CI Success"
|
name: "CI Success"
|
||||||
needs: [build, lint, test, compile-integration-tests, dependencies, extended-tests, test-doc-imports]
|
needs: [build, lint, test, compile-integration-tests, extended-tests, test-doc-imports]
|
||||||
if: |
|
if: |
|
||||||
always()
|
always()
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@@ -273,6 +273,17 @@ def _create_subset_model_v2(
|
|||||||
fields[field_name] = (field.annotation, field_info)
|
fields[field_name] = (field.annotation, field_info)
|
||||||
rtn = create_model(name, **fields) # type: ignore
|
rtn = create_model(name, **fields) # type: ignore
|
||||||
|
|
||||||
|
# TODO(0.3): Determine if there is a more "pydantic" way to preserve annotations.
|
||||||
|
# This is done to preserve __annotations__ when working with pydantic 2.x
|
||||||
|
# and using the Annotated type with TypedDict.
|
||||||
|
# Comment out the following line, to trigger the relevant test case.
|
||||||
|
selected_annotations = [
|
||||||
|
(name, annotation)
|
||||||
|
for name, annotation in model.__annotations__.items()
|
||||||
|
if name in field_names
|
||||||
|
]
|
||||||
|
|
||||||
|
rtn.__annotations__ = dict(selected_annotations)
|
||||||
rtn.__doc__ = textwrap.dedent(fn_description or model.__doc__ or "")
|
rtn.__doc__ = textwrap.dedent(fn_description or model.__doc__ or "")
|
||||||
return rtn
|
return rtn
|
||||||
|
|
||||||
|
@@ -1756,13 +1756,17 @@ def test__get_all_basemodel_annotations_v2(use_v1_namespace: bool) -> None:
|
|||||||
A = TypeVar("A")
|
A = TypeVar("A")
|
||||||
|
|
||||||
if use_v1_namespace:
|
if use_v1_namespace:
|
||||||
|
from pydantic.v1 import BaseModel as BM1
|
||||||
|
|
||||||
class ModelA(BaseModel, Generic[A], extra="allow"):
|
class ModelA(BM1, Generic[A], extra="allow"):
|
||||||
a: A
|
a: A
|
||||||
else:
|
else:
|
||||||
|
from pydantic import BaseModel as BM2
|
||||||
|
from pydantic import ConfigDict
|
||||||
|
|
||||||
class ModelA(BaseModelProper, Generic[A], extra="allow"): # type: ignore[no-redef]
|
class ModelA(BM2, Generic[A], extra="allow"): # type: ignore[no-redef]
|
||||||
a: A
|
a: A
|
||||||
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||||
|
|
||||||
class ModelB(ModelA[str]):
|
class ModelB(ModelA[str]):
|
||||||
b: Annotated[ModelA[Dict[str, Any]], "foo"]
|
b: Annotated[ModelA[Dict[str, Any]], "foo"]
|
||||||
@@ -1871,6 +1875,26 @@ def test__get_all_basemodel_annotations_v1() -> None:
|
|||||||
assert actual == expected
|
assert actual == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_tool_annotations_preserved() -> None:
|
||||||
|
"""Test that annotations are preserved when creating a tool."""
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def my_tool(val: int, other_val: Annotated[dict, "my annotation"]) -> str:
|
||||||
|
"""Tool docstring."""
|
||||||
|
return "foo"
|
||||||
|
|
||||||
|
schema = my_tool.get_input_schema() # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
func = my_tool.func # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
expected_type_hints = {
|
||||||
|
name: hint
|
||||||
|
for name, hint in func.__annotations__.items()
|
||||||
|
if name in inspect.signature(func).parameters
|
||||||
|
}
|
||||||
|
assert schema.__annotations__ == expected_type_hints
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(PYDANTIC_MAJOR_VERSION != 2, reason="Testing pydantic v2.")
|
@pytest.mark.skipif(PYDANTIC_MAJOR_VERSION != 2, reason="Testing pydantic v2.")
|
||||||
def test_tool_args_schema_pydantic_v2_with_metadata() -> None:
|
def test_tool_args_schema_pydantic_v2_with_metadata() -> None:
|
||||||
from pydantic import BaseModel as BaseModelV2
|
from pydantic import BaseModel as BaseModelV2
|
||||||
|
Reference in New Issue
Block a user