Skip to content

Commit 17ef1a3

Browse files
cbornetCopilot
andauthored
Add tests in CI (#1)
* Add tests in CI * Update .github/workflows/python_test.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 3633eda commit 17ef1a3

File tree

5 files changed

+182
-45
lines changed

5 files changed

+182
-45
lines changed

.github/workflows/lint.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Python lint
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
lint_langchain:
13+
name: Lint LangChain - Python ${{ matrix.python-version }}
14+
runs-on: ubuntu-latest
15+
defaults:
16+
run:
17+
working-directory: ./langchain
18+
strategy:
19+
matrix:
20+
python-version: ['3.9', '3.13']
21+
steps:
22+
- uses: actions/checkout@v4
23+
with:
24+
ref: ${{ github.ref }}
25+
- name: Install uv
26+
uses: astral-sh/setup-uv@v3
27+
with:
28+
enable-cache: true
29+
cache-dependency-glob: "langchain/uv.lock"
30+
- name: "Set up Python"
31+
uses: actions/setup-python@v5
32+
with:
33+
python-version-file: "langchain/pyproject.toml"
34+
- name: Restore uv cache
35+
uses: actions/cache@v4
36+
with:
37+
path: /tmp/.uv-cache
38+
key: uv-langchain-${{ hashFiles('langchain/uv.lock') }}
39+
restore-keys: |
40+
uv-langchain-${{ hashFiles('langchain/uv.lock') }}
41+
uv-${{ runner.os }}
42+
- name: Install the project
43+
run: uv sync --dev
44+
- name: Run ruff format check
45+
run: uv run ruff format --check
46+
- name: Run ruff check
47+
run: uv run ruff check
48+
# - name: Run mypy
49+
# run: uv run mypy .
50+
- name: Minimize uv cache
51+
run: uv cache prune --ci

.github/workflows/python_test.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Python tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
build-langchain:
13+
name: LangChain Unit Tests - Python ${{ matrix.python-version }}
14+
runs-on: ubuntu-latest
15+
defaults:
16+
run:
17+
working-directory: ./langchain
18+
strategy:
19+
matrix:
20+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
21+
steps:
22+
- uses: actions/checkout@v4
23+
with:
24+
ref: ${{ github.ref }}
25+
- name: Install uv
26+
uses: astral-sh/setup-uv@v3
27+
with:
28+
enable-cache: true
29+
cache-dependency-glob: "langchain/uv.lock"
30+
- name: "Set up Python"
31+
uses: actions/setup-python@v5
32+
with:
33+
python-version: ${{ matrix.python-version }}
34+
- name: Restore uv cache
35+
uses: actions/cache@v4
36+
with:
37+
path: /tmp/.uv-cache
38+
key: uv-langchain-${{ hashFiles('langchain/uv.lock') }}
39+
restore-keys: |
40+
uv-langchain-${{ hashFiles('langchain/uv.lock') }}
41+
- name: Install the project
42+
run: uv sync --dev
43+
- name: Run unit tests
44+
env:
45+
VECTORIZE_TOKEN: ${{ secrets.VECTORIZE_TOKEN }}
46+
VECTORIZE_ORG: ${{ secrets.VECTORIZE_ORG }}
47+
VECTORIZE_ENV: dev
48+
run: uv run pytest tests -vv
49+
- name: Minimize uv cache
50+
run: uv cache prune --ci

langchain/langchain_vectorize/retrievers.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from typing import TYPE_CHECKING, Any, Optional
5+
from typing import TYPE_CHECKING, Any, Literal, Optional
66

77
import vectorize_client
88
from langchain_core.documents import Document
@@ -40,6 +40,8 @@ class VectorizeRetriever(BaseRetriever):
4040

4141
api_token: str
4242
"""The Vectorize API token."""
43+
environment: Literal["prod", "dev", "local", "staging"] = "prod"
44+
"""The Vectorize API environment."""
4345
organization: Optional[str] = None # noqa: UP007
4446
"""The Vectorize organization ID."""
4547
pipeline_id: Optional[str] = None # noqa: UP007
@@ -55,7 +57,23 @@ class VectorizeRetriever(BaseRetriever):
5557

5658
@override
5759
def model_post_init(self, /, context: Any) -> None:
58-
api = ApiClient(Configuration(access_token=self.api_token))
60+
header_name = None
61+
header_value = None
62+
if self.environment == "prod":
63+
host = "https://api.vectorize.io/v1"
64+
elif self.environment == "dev":
65+
host = "https://api-dev.vectorize.io/v1"
66+
elif self.environment == "local":
67+
host = "http://localhost:3000/api"
68+
header_name = "x-lambda-api-key"
69+
header_value = self.api_token
70+
else:
71+
host = "https://api-staging.vectorize.io/v1"
72+
api = ApiClient(
73+
Configuration(host=host, access_token=self.api_token, debug=True),
74+
header_name,
75+
header_value,
76+
)
5977
self._pipelines = PipelinesApi(api)
6078

6179
@staticmethod

langchain/tests/test_retrievers.py

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,24 @@
11
import json
22
import logging
33
import os
4+
import time
45
from collections.abc import Iterator
5-
from dataclasses import dataclass
66
from pathlib import Path
7+
from typing import Literal
78

89
import pytest
10+
import urllib3
911
import vectorize_client as v
12+
from vectorize_client import ApiClient
1013

1114
from langchain_vectorize.retrievers import VectorizeRetriever
1215

1316

14-
@dataclass
15-
class TestContext:
16-
api_client: v.ApiClient
17-
api_token: str
18-
org_id: str
19-
20-
2117
@pytest.fixture(scope="session")
2218
def api_token() -> str:
2319
token = os.getenv("VECTORIZE_TOKEN")
2420
if not token:
25-
msg = "Please set VECTORIZE_TOKEN environment variable"
21+
msg = "Please set the VECTORIZE_TOKEN environment variable"
2622
raise ValueError(msg)
2723
return token
2824

@@ -31,21 +27,29 @@ def api_token() -> str:
3127
def org_id() -> str:
3228
org = os.getenv("VECTORIZE_ORG")
3329
if not org:
34-
msg = "Please set VECTORIZE_ORG environment variable"
30+
msg = "Please set the VECTORIZE_ORG environment variable"
3531
raise ValueError(msg)
3632
return org
3733

3834

3935
@pytest.fixture(scope="session")
40-
def api_client(api_token: str) -> Iterator[TestContext]:
36+
def environment() -> Literal["prod", "dev", "local", "staging"]:
4137
env = os.getenv("VECTORIZE_ENV", "prod")
38+
if env not in ["prod", "dev", "local", "staging"]:
39+
msg = "Invalid VECTORIZE_ENV environment variable."
40+
raise ValueError(msg)
41+
return env
42+
43+
44+
@pytest.fixture(scope="session")
45+
def api_client(api_token: str, environment: str) -> Iterator[ApiClient]:
4246
header_name = None
4347
header_value = None
44-
if env == "prod":
48+
if environment == "prod":
4549
host = "https://api.vectorize.io/v1"
46-
elif env == "dev":
50+
elif environment == "dev":
4751
host = "https://api-dev.vectorize.io/v1"
48-
elif env == "local":
52+
elif environment == "local":
4953
host = "http://localhost:3000/api"
5054
header_name = "x-lambda-api-key"
5155
header_value = api_token
@@ -87,8 +91,6 @@ def pipeline_id(api_client: v.ApiClient, org_id: str) -> Iterator[str]:
8791
),
8892
)
8993

90-
import urllib3
91-
9294
http = urllib3.PoolManager()
9395
this_dir = Path(__file__).parent
9496
file_path = this_dir / "research.pdf"
@@ -137,7 +139,9 @@ def pipeline_id(api_client: v.ApiClient, org_id: str) -> Iterator[str]:
137139
config={},
138140
),
139141
ai_platform=v.AIPlatformSchema(
140-
id=builtin_ai_platform, type=v.AIPlatformType.VECTORIZE, config=v.AIPlatformConfigSchema()
142+
id=builtin_ai_platform,
143+
type=v.AIPlatformType.VECTORIZE,
144+
config=v.AIPlatformConfigSchema(),
141145
),
142146
pipeline_name="Test pipeline",
143147
schedule=v.ScheduleSchema(type=v.ScheduleSchemaType.MANUAL),
@@ -154,20 +158,48 @@ def pipeline_id(api_client: v.ApiClient, org_id: str) -> Iterator[str]:
154158
logging.exception("Failed to delete pipeline %s", pipeline_id)
155159

156160

157-
def test_retrieve_init_args(api_token: str, org_id: str, pipeline_id: str) -> None:
161+
def test_retrieve_init_args(
162+
environment: Literal["prod", "dev", "local", "staging"],
163+
api_token: str,
164+
org_id: str,
165+
pipeline_id: str,
166+
) -> None:
158167
retriever = VectorizeRetriever(
159-
api_token=api_token, organization=org_id, pipeline_id=pipeline_id, num_results=2
160-
)
161-
docs = retriever.invoke(input="What are you?")
162-
assert len(docs) == 2
163-
164-
165-
def test_retrieve_invoke_args(api_token: str, org_id: str, pipeline_id: str) -> None:
166-
retriever = VectorizeRetriever(api_token=api_token)
167-
docs = retriever.invoke(
168-
input="What are you?",
168+
environment=environment,
169+
api_token=api_token,
169170
organization=org_id,
170171
pipeline_id=pipeline_id,
171172
num_results=2,
172173
)
173-
assert len(docs) == 2
174+
start = time.time()
175+
while True:
176+
docs = retriever.invoke(input="What are you?")
177+
if len(docs) == 2:
178+
break
179+
if time.time() - start > 180:
180+
msg = "Docs not retrieved in time"
181+
raise RuntimeError(msg)
182+
time.sleep(1)
183+
184+
185+
def test_retrieve_invoke_args(
186+
environment: Literal["prod", "dev", "local", "staging"],
187+
api_token: str,
188+
org_id: str,
189+
pipeline_id: str,
190+
) -> None:
191+
retriever = VectorizeRetriever(environment=environment, api_token=api_token)
192+
start = time.time()
193+
while True:
194+
docs = retriever.invoke(
195+
input="What are you?",
196+
organization=org_id,
197+
pipeline_id=pipeline_id,
198+
num_results=2,
199+
)
200+
if len(docs) == 2:
201+
break
202+
if time.time() - start > 180:
203+
msg = "Docs not retrieved in time"
204+
raise RuntimeError(msg)
205+
time.sleep(1)

langchain/uv.lock

Lines changed: 0 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)