mirror of
https://github.com/hwchase17/langchain.git
synced 2025-06-23 15:19:33 +00:00
cli: standard tests in cli, test that they run, skip vectorstore tests (#28521)
This commit is contained in:
parent
c5acedddc2
commit
43c35d19d4
6
.github/scripts/check_diff.py
vendored
6
.github/scripts/check_diff.py
vendored
@ -272,6 +272,7 @@ if __name__ == "__main__":
|
|||||||
# TODO: update to include all packages that rely on standard-tests (all partner packages)
|
# TODO: update to include all packages that rely on standard-tests (all partner packages)
|
||||||
# note: won't run on external repo partners
|
# note: won't run on external repo partners
|
||||||
dirs_to_run["lint"].add("libs/standard-tests")
|
dirs_to_run["lint"].add("libs/standard-tests")
|
||||||
|
dirs_to_run["test"].add("libs/standard-tests")
|
||||||
dirs_to_run["test"].add("libs/partners/mistralai")
|
dirs_to_run["test"].add("libs/partners/mistralai")
|
||||||
dirs_to_run["test"].add("libs/partners/openai")
|
dirs_to_run["test"].add("libs/partners/openai")
|
||||||
dirs_to_run["test"].add("libs/partners/anthropic")
|
dirs_to_run["test"].add("libs/partners/anthropic")
|
||||||
@ -279,8 +280,9 @@ if __name__ == "__main__":
|
|||||||
dirs_to_run["test"].add("libs/partners/groq")
|
dirs_to_run["test"].add("libs/partners/groq")
|
||||||
|
|
||||||
elif file.startswith("libs/cli"):
|
elif file.startswith("libs/cli"):
|
||||||
# todo: add cli makefile
|
dirs_to_run["lint"].add("libs/cli")
|
||||||
pass
|
dirs_to_run["test"].add("libs/cli")
|
||||||
|
|
||||||
elif file.startswith("libs/partners"):
|
elif file.startswith("libs/partners"):
|
||||||
partner_dir = file.split("/")[2]
|
partner_dir = file.split("/")[2]
|
||||||
if os.path.isdir(f"libs/partners/{partner_dir}") and [
|
if os.path.isdir(f"libs/partners/{partner_dir}") and [
|
||||||
|
2
libs/cli/.gitignore
vendored
2
libs/cli/.gitignore
vendored
@ -158,3 +158,5 @@ cython_debug/
|
|||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
|
.integration_test
|
||||||
|
@ -1,8 +1,47 @@
|
|||||||
lint lint_diff:
|
|
||||||
poetry run poe lint
|
|
||||||
|
|
||||||
test:
|
######################
|
||||||
poetry run poe test
|
# LINTING AND FORMATTING
|
||||||
|
######################
|
||||||
|
|
||||||
format:
|
# Define a variable for Python and notebook files.
|
||||||
poetry run poe format
|
PYTHON_FILES=.
|
||||||
|
MYPY_CACHE=.mypy_cache
|
||||||
|
lint format: PYTHON_FILES=.
|
||||||
|
lint_diff format_diff: PYTHON_FILES=$(shell git diff --relative=libs/cli --name-only --diff-filter=d master | grep -E '\.py$$|\.ipynb$$')
|
||||||
|
lint_package: PYTHON_FILES=langchain_cli
|
||||||
|
lint_tests: PYTHON_FILES=tests
|
||||||
|
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||||
|
|
||||||
|
lint lint_diff lint_package lint_tests:
|
||||||
|
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check $(PYTHON_FILES)
|
||||||
|
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
||||||
|
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||||
|
|
||||||
|
format format_diff:
|
||||||
|
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES)
|
||||||
|
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I --fix $(PYTHON_FILES)
|
||||||
|
|
||||||
|
test tests: _test _e2e_test
|
||||||
|
|
||||||
|
PYTHON = .venv/bin/python
|
||||||
|
|
||||||
|
_test:
|
||||||
|
poetry run pytest tests
|
||||||
|
|
||||||
|
# custom integration testing for cli integration flow
|
||||||
|
# currently ignores vectorstores test because lacks implementation
|
||||||
|
_e2e_test:
|
||||||
|
rm -rf .integration_test
|
||||||
|
mkdir .integration_test
|
||||||
|
cd .integration_test && \
|
||||||
|
python3 -m venv .venv && \
|
||||||
|
$(PYTHON) -m pip install --upgrade poetry && \
|
||||||
|
$(PYTHON) -m pip install -e .. && \
|
||||||
|
$(PYTHON) -m langchain_cli.cli integration new --name parrot-link --name-class ParrotLink && \
|
||||||
|
cd langchain-parrot-link && \
|
||||||
|
poetry install --with lint,typing,test && \
|
||||||
|
poetry run pip install -e ../../../standard-tests && \
|
||||||
|
make format lint tests && \
|
||||||
|
poetry install --with test_integration && \
|
||||||
|
rm tests/integration_tests/test_vectorstores.py && \
|
||||||
|
make integration_test
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# type: ignore
|
||||||
"""
|
"""
|
||||||
Development Scripts for template packages
|
Development Scripts for template packages
|
||||||
"""
|
"""
|
||||||
|
@ -33,13 +33,13 @@ lint_tests: PYTHON_FILES=tests
|
|||||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||||
|
|
||||||
lint lint_diff lint_package lint_tests:
|
lint lint_diff lint_package lint_tests:
|
||||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff $(PYTHON_FILES)
|
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check $(PYTHON_FILES)
|
||||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
||||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||||
|
|
||||||
format format_diff:
|
format format_diff:
|
||||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES)
|
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES)
|
||||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff --select I --fix $(PYTHON_FILES)
|
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I --fix $(PYTHON_FILES)
|
||||||
|
|
||||||
spell_check:
|
spell_check:
|
||||||
poetry run codespell --toml pyproject.toml
|
poetry run codespell --toml pyproject.toml
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
from importlib import metadata
|
from importlib import metadata
|
||||||
|
|
||||||
from __module_name__.chat_models import Chat__ModuleName__
|
from __module_name__.chat_models import Chat__ModuleName__
|
||||||
|
from __module_name__.document_loaders import __ModuleName__Loader
|
||||||
from __module_name__.embeddings import __ModuleName__Embeddings
|
from __module_name__.embeddings import __ModuleName__Embeddings
|
||||||
from __module_name__.llms import __ModuleName__LLM
|
from __module_name__.retrievers import __ModuleName__Retriever
|
||||||
|
from __module_name__.toolkits import __ModuleName__Toolkit
|
||||||
|
from __module_name__.tools import __ModuleName__Tool
|
||||||
from __module_name__.vectorstores import __ModuleName__VectorStore
|
from __module_name__.vectorstores import __ModuleName__VectorStore
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -14,8 +17,11 @@ del metadata # optional, avoids polluting the results of dir(__package__)
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Chat__ModuleName__",
|
"Chat__ModuleName__",
|
||||||
"__ModuleName__LLM",
|
|
||||||
"__ModuleName__VectorStore",
|
"__ModuleName__VectorStore",
|
||||||
"__ModuleName__Embeddings",
|
"__ModuleName__Embeddings",
|
||||||
|
"__ModuleName__Loader",
|
||||||
|
"__ModuleName__Retriever",
|
||||||
|
"__ModuleName__Toolkit",
|
||||||
|
"__ModuleName__Tool",
|
||||||
"__version__",
|
"__version__",
|
||||||
]
|
]
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
"""__ModuleName__ chat models."""
|
"""__ModuleName__ chat models."""
|
||||||
|
|
||||||
from typing import Any, List, Optional
|
from typing import Any, Dict, Iterator, List, Optional
|
||||||
|
|
||||||
from langchain_core.callbacks import (
|
from langchain_core.callbacks import (
|
||||||
CallbackManagerForLLMRun,
|
CallbackManagerForLLMRun,
|
||||||
)
|
)
|
||||||
from langchain_core.language_models.chat_models import BaseChatModel
|
from langchain_core.language_models import BaseChatModel
|
||||||
from langchain_core.messages import BaseMessage
|
from langchain_core.messages import (
|
||||||
from langchain_core.outputs import ChatResult
|
AIMessage,
|
||||||
|
AIMessageChunk,
|
||||||
|
BaseMessage,
|
||||||
|
)
|
||||||
|
from langchain_core.messages.ai import UsageMetadata
|
||||||
|
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
|
||||||
class Chat__ModuleName__(BaseChatModel):
|
class Chat__ModuleName__(BaseChatModel):
|
||||||
@ -15,6 +21,8 @@ class Chat__ModuleName__(BaseChatModel):
|
|||||||
# https://github.com/langchain-ai/langchain/blob/7ff05357bac6eaedf5058a2af88f23a1817d40fe/libs/partners/openai/langchain_openai/chat_models/base.py#L1120
|
# https://github.com/langchain-ai/langchain/blob/7ff05357bac6eaedf5058a2af88f23a1817d40fe/libs/partners/openai/langchain_openai/chat_models/base.py#L1120
|
||||||
"""__ModuleName__ chat model integration.
|
"""__ModuleName__ chat model integration.
|
||||||
|
|
||||||
|
The default implementation echoes the first `parrot_buffer_length` characters of the input.
|
||||||
|
|
||||||
# TODO: Replace with relevant packages, env vars.
|
# TODO: Replace with relevant packages, env vars.
|
||||||
Setup:
|
Setup:
|
||||||
Install ``__package_name__`` and set environment variable ``__MODULE_NAME___API_KEY``.
|
Install ``__package_name__`` and set environment variable ``__MODULE_NAME___API_KEY``.
|
||||||
@ -258,7 +266,36 @@ class Chat__ModuleName__(BaseChatModel):
|
|||||||
|
|
||||||
""" # noqa: E501
|
""" # noqa: E501
|
||||||
|
|
||||||
# TODO: This method must be implemented to generate chat responses.
|
model_name: str = Field(alias="model")
|
||||||
|
"""The name of the model"""
|
||||||
|
parrot_buffer_length: int
|
||||||
|
"""The number of characters from the last message of the prompt to be echoed."""
|
||||||
|
temperature: Optional[float] = None
|
||||||
|
max_tokens: Optional[int] = None
|
||||||
|
timeout: Optional[int] = None
|
||||||
|
stop: Optional[List[str]] = None
|
||||||
|
max_retries: int = 2
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _llm_type(self) -> str:
|
||||||
|
"""Return type of chat model."""
|
||||||
|
return "chat-__package_name_short__"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _identifying_params(self) -> Dict[str, Any]:
|
||||||
|
"""Return a dictionary of identifying parameters.
|
||||||
|
|
||||||
|
This information is used by the LangChain callback system, which
|
||||||
|
is used for tracing purposes make it possible to monitor LLMs.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
# The model name allows users to specify custom token counting
|
||||||
|
# rules in LLM monitoring applications (e.g., in LangSmith users
|
||||||
|
# can provide per token pricing for their model and monitor
|
||||||
|
# costs for the given LLM.)
|
||||||
|
"model_name": self.model_name,
|
||||||
|
}
|
||||||
|
|
||||||
def _generate(
|
def _generate(
|
||||||
self,
|
self,
|
||||||
messages: List[BaseMessage],
|
messages: List[BaseMessage],
|
||||||
@ -266,16 +303,101 @@ class Chat__ModuleName__(BaseChatModel):
|
|||||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> ChatResult:
|
) -> ChatResult:
|
||||||
raise NotImplementedError()
|
"""Override the _generate method to implement the chat model logic.
|
||||||
|
|
||||||
# TODO: Implement if Chat__ModuleName__ supports streaming. Otherwise delete method.
|
This can be a call to an API, a call to a local model, or any other
|
||||||
# def _stream(
|
implementation that generates a response to the input prompt.
|
||||||
# self,
|
|
||||||
# messages: List[BaseMessage],
|
Args:
|
||||||
# stop: Optional[List[str]] = None,
|
messages: the prompt composed of a list of messages.
|
||||||
# run_manager: Optional[CallbackManagerForLLMRun] = None,
|
stop: a list of strings on which the model should stop generating.
|
||||||
# **kwargs: Any,
|
If generation stops due to a stop token, the stop token itself
|
||||||
# ) -> Iterator[ChatGenerationChunk]:
|
SHOULD BE INCLUDED as part of the output. This is not enforced
|
||||||
|
across models right now, but it's a good practice to follow since
|
||||||
|
it makes it much easier to parse the output of the model
|
||||||
|
downstream and understand why generation stopped.
|
||||||
|
run_manager: A run manager with callbacks for the LLM.
|
||||||
|
"""
|
||||||
|
# Replace this with actual logic to generate a response from a list
|
||||||
|
# of messages.
|
||||||
|
last_message = messages[-1]
|
||||||
|
tokens = last_message.content[: self.parrot_buffer_length]
|
||||||
|
ct_input_tokens = sum(len(message.content) for message in messages)
|
||||||
|
ct_output_tokens = len(tokens)
|
||||||
|
message = AIMessage(
|
||||||
|
content=tokens,
|
||||||
|
additional_kwargs={}, # Used to add additional payload to the message
|
||||||
|
response_metadata={ # Use for response metadata
|
||||||
|
"time_in_seconds": 3,
|
||||||
|
},
|
||||||
|
usage_metadata={
|
||||||
|
"input_tokens": ct_input_tokens,
|
||||||
|
"output_tokens": ct_output_tokens,
|
||||||
|
"total_tokens": ct_input_tokens + ct_output_tokens,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
##
|
||||||
|
|
||||||
|
generation = ChatGeneration(message=message)
|
||||||
|
return ChatResult(generations=[generation])
|
||||||
|
|
||||||
|
def _stream(
|
||||||
|
self,
|
||||||
|
messages: List[BaseMessage],
|
||||||
|
stop: Optional[List[str]] = None,
|
||||||
|
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Iterator[ChatGenerationChunk]:
|
||||||
|
"""Stream the output of the model.
|
||||||
|
|
||||||
|
This method should be implemented if the model can generate output
|
||||||
|
in a streaming fashion. If the model does not support streaming,
|
||||||
|
do not implement it. In that case streaming requests will be automatically
|
||||||
|
handled by the _generate method.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
messages: the prompt composed of a list of messages.
|
||||||
|
stop: a list of strings on which the model should stop generating.
|
||||||
|
If generation stops due to a stop token, the stop token itself
|
||||||
|
SHOULD BE INCLUDED as part of the output. This is not enforced
|
||||||
|
across models right now, but it's a good practice to follow since
|
||||||
|
it makes it much easier to parse the output of the model
|
||||||
|
downstream and understand why generation stopped.
|
||||||
|
run_manager: A run manager with callbacks for the LLM.
|
||||||
|
"""
|
||||||
|
last_message = messages[-1]
|
||||||
|
tokens = str(last_message.content[: self.parrot_buffer_length])
|
||||||
|
ct_input_tokens = sum(len(message.content) for message in messages)
|
||||||
|
|
||||||
|
for token in tokens:
|
||||||
|
usage_metadata = UsageMetadata(
|
||||||
|
{
|
||||||
|
"input_tokens": ct_input_tokens,
|
||||||
|
"output_tokens": 1,
|
||||||
|
"total_tokens": ct_input_tokens + 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
ct_input_tokens = 0
|
||||||
|
chunk = ChatGenerationChunk(
|
||||||
|
message=AIMessageChunk(content=token, usage_metadata=usage_metadata)
|
||||||
|
)
|
||||||
|
|
||||||
|
if run_manager:
|
||||||
|
# This is optional in newer versions of LangChain
|
||||||
|
# The on_llm_new_token will be called automatically
|
||||||
|
run_manager.on_llm_new_token(token, chunk=chunk)
|
||||||
|
|
||||||
|
yield chunk
|
||||||
|
|
||||||
|
# Let's add some other information (e.g., response metadata)
|
||||||
|
chunk = ChatGenerationChunk(
|
||||||
|
message=AIMessageChunk(content="", response_metadata={"time_in_sec": 3})
|
||||||
|
)
|
||||||
|
if run_manager:
|
||||||
|
# This is optional in newer versions of LangChain
|
||||||
|
# The on_llm_new_token will be called automatically
|
||||||
|
run_manager.on_llm_new_token(token, chunk=chunk)
|
||||||
|
yield chunk
|
||||||
|
|
||||||
# TODO: Implement if Chat__ModuleName__ supports async streaming. Otherwise delete.
|
# TODO: Implement if Chat__ModuleName__ supports async streaming. Otherwise delete.
|
||||||
# async def _astream(
|
# async def _astream(
|
||||||
@ -294,8 +416,3 @@ class Chat__ModuleName__(BaseChatModel):
|
|||||||
# run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
|
# run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
|
||||||
# **kwargs: Any,
|
# **kwargs: Any,
|
||||||
# ) -> ChatResult:
|
# ) -> ChatResult:
|
||||||
|
|
||||||
@property
|
|
||||||
def _llm_type(self) -> str:
|
|
||||||
"""Return type of chat model."""
|
|
||||||
return "chat-__package_name_short__"
|
|
||||||
|
@ -8,7 +8,8 @@ class __ModuleName__Embeddings(Embeddings):
|
|||||||
|
|
||||||
# TODO: Replace with relevant packages, env vars.
|
# TODO: Replace with relevant packages, env vars.
|
||||||
Setup:
|
Setup:
|
||||||
Install ``__package_name__`` and set environment variable ``__MODULE_NAME___API_KEY``.
|
Install ``__package_name__`` and set environment variable
|
||||||
|
``__MODULE_NAME___API_KEY``.
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@ -70,21 +71,26 @@ class __ModuleName__Embeddings(Embeddings):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, model: str):
|
||||||
|
self.model = model
|
||||||
|
|
||||||
def embed_documents(self, texts: List[str]) -> List[List[float]]:
|
def embed_documents(self, texts: List[str]) -> List[List[float]]:
|
||||||
"""Embed search docs."""
|
"""Embed search docs."""
|
||||||
raise NotImplementedError
|
return [[0.5, 0.6, 0.7] for _ in texts]
|
||||||
|
|
||||||
def embed_query(self, text: str) -> List[float]:
|
def embed_query(self, text: str) -> List[float]:
|
||||||
"""Embed query text."""
|
"""Embed query text."""
|
||||||
raise NotImplementedError
|
return self.embed_documents([text])[0]
|
||||||
|
|
||||||
# only keep aembed_documents and aembed_query if they're implemented!
|
# optional: add custom async implementations here
|
||||||
# delete them otherwise to use the base class' default
|
# you can also delete these, and the base class will
|
||||||
# implementation, which calls the sync version in an executor
|
# use the default implementation, which calls the sync
|
||||||
async def aembed_documents(self, texts: List[str]) -> List[List[float]]:
|
# version in an async executor:
|
||||||
"""Asynchronous Embed search docs."""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
async def aembed_query(self, text: str) -> List[float]:
|
# async def aembed_documents(self, texts: List[str]) -> List[List[float]]:
|
||||||
"""Asynchronous Embed query text."""
|
# """Asynchronous Embed search docs."""
|
||||||
raise NotImplementedError
|
# ...
|
||||||
|
|
||||||
|
# async def aembed_query(self, text: str) -> List[float]:
|
||||||
|
# """Asynchronous Embed query text."""
|
||||||
|
# ...
|
||||||
|
@ -1,155 +0,0 @@
|
|||||||
"""__ModuleName__ large language models."""
|
|
||||||
|
|
||||||
from typing import (
|
|
||||||
Any,
|
|
||||||
List,
|
|
||||||
Optional,
|
|
||||||
)
|
|
||||||
|
|
||||||
from langchain_core.callbacks import (
|
|
||||||
CallbackManagerForLLMRun,
|
|
||||||
)
|
|
||||||
from langchain_core.language_models import BaseLLM
|
|
||||||
from langchain_core.outputs import LLMResult
|
|
||||||
|
|
||||||
|
|
||||||
class __ModuleName__LLM(BaseLLM):
|
|
||||||
"""__ModuleName__ completion model integration.
|
|
||||||
|
|
||||||
# TODO: Replace with relevant packages, env vars.
|
|
||||||
Setup:
|
|
||||||
Install ``__package_name__`` and set environment variable ``__MODULE_NAME___API_KEY``.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
pip install -U __package_name__
|
|
||||||
export __MODULE_NAME___API_KEY="your-api-key"
|
|
||||||
|
|
||||||
# TODO: Populate with relevant params.
|
|
||||||
Key init args — completion params:
|
|
||||||
model: str
|
|
||||||
Name of __ModuleName__ model to use.
|
|
||||||
temperature: float
|
|
||||||
Sampling temperature.
|
|
||||||
max_tokens: Optional[int]
|
|
||||||
Max number of tokens to generate.
|
|
||||||
|
|
||||||
# TODO: Populate with relevant params.
|
|
||||||
Key init args — client params:
|
|
||||||
timeout: Optional[float]
|
|
||||||
Timeout for requests.
|
|
||||||
max_retries: int
|
|
||||||
Max number of retries.
|
|
||||||
api_key: Optional[str]
|
|
||||||
__ModuleName__ API key. If not passed in will be read from env var __MODULE_NAME___API_KEY.
|
|
||||||
|
|
||||||
See full list of supported init args and their descriptions in the params section.
|
|
||||||
|
|
||||||
# TODO: Replace with relevant init params.
|
|
||||||
Instantiate:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from __module_name__ import __ModuleName__LLM
|
|
||||||
|
|
||||||
llm = __ModuleName__LLM(
|
|
||||||
model="...",
|
|
||||||
temperature=0,
|
|
||||||
max_tokens=None,
|
|
||||||
timeout=None,
|
|
||||||
max_retries=2,
|
|
||||||
# api_key="...",
|
|
||||||
# other params...
|
|
||||||
)
|
|
||||||
|
|
||||||
Invoke:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
input_text = "The meaning of life is "
|
|
||||||
llm.invoke(input_text)
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
# TODO: Example output.
|
|
||||||
|
|
||||||
# TODO: Delete if token-level streaming isn't supported.
|
|
||||||
Stream:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
for chunk in llm.stream(input_text):
|
|
||||||
print(chunk)
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
# TODO: Example output.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
''.join(llm.stream(input_text))
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
# TODO: Example output.
|
|
||||||
|
|
||||||
# TODO: Delete if native async isn't supported.
|
|
||||||
Async:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
await llm.ainvoke(input_text)
|
|
||||||
|
|
||||||
# stream:
|
|
||||||
# async for chunk in (await llm.astream(input_text))
|
|
||||||
|
|
||||||
# batch:
|
|
||||||
# await llm.abatch([input_text])
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
# TODO: Example output.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# TODO: This method must be implemented to generate text completions.
|
|
||||||
def _generate(
|
|
||||||
self,
|
|
||||||
prompts: List[str],
|
|
||||||
stop: Optional[List[str]] = None,
|
|
||||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> LLMResult:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
# TODO: Implement if __ModuleName__LLM supports async generation. Otherwise
|
|
||||||
# delete method.
|
|
||||||
# async def _agenerate(
|
|
||||||
# self,
|
|
||||||
# prompts: List[str],
|
|
||||||
# stop: Optional[List[str]] = None,
|
|
||||||
# run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
|
|
||||||
# **kwargs: Any,
|
|
||||||
# ) -> LLMResult:
|
|
||||||
# raise NotImplementedError
|
|
||||||
|
|
||||||
# TODO: Implement if __ModuleName__LLM supports streaming. Otherwise delete method.
|
|
||||||
# def _stream(
|
|
||||||
# self,
|
|
||||||
# prompt: str,
|
|
||||||
# stop: Optional[List[str]] = None,
|
|
||||||
# run_manager: Optional[CallbackManagerForLLMRun] = None,
|
|
||||||
# **kwargs: Any,
|
|
||||||
# ) -> Iterator[GenerationChunk]:
|
|
||||||
# raise NotImplementedError
|
|
||||||
|
|
||||||
# TODO: Implement if __ModuleName__LLM supports async streaming. Otherwise delete
|
|
||||||
# method.
|
|
||||||
# async def _astream(
|
|
||||||
# self,
|
|
||||||
# prompt: str,
|
|
||||||
# stop: Optional[List[str]] = None,
|
|
||||||
# run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
|
|
||||||
# **kwargs: Any,
|
|
||||||
# ) -> AsyncIterator[GenerationChunk]:
|
|
||||||
# raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _llm_type(self) -> str:
|
|
||||||
"""Return type of LLM."""
|
|
||||||
return "__package_name_short__-llm"
|
|
@ -1,7 +1,8 @@
|
|||||||
"""__ModuleName__ retrievers."""
|
"""__ModuleName__ retrievers."""
|
||||||
|
|
||||||
from typing import List
|
from typing import Any, List
|
||||||
|
|
||||||
|
from langchain_core.callbacks import CallbackManagerForRetrieverRun
|
||||||
from langchain_core.documents import Document
|
from langchain_core.documents import Document
|
||||||
from langchain_core.retrievers import BaseRetriever
|
from langchain_core.retrievers import BaseRetriever
|
||||||
|
|
||||||
@ -13,7 +14,8 @@ class __ModuleName__Retriever(BaseRetriever):
|
|||||||
|
|
||||||
# TODO: Replace with relevant packages, env vars, etc.
|
# TODO: Replace with relevant packages, env vars, etc.
|
||||||
Setup:
|
Setup:
|
||||||
Install ``__package_name__`` and set environment variable ``__MODULE_NAME___API_KEY``.
|
Install ``__package_name__`` and set environment variable
|
||||||
|
``__MODULE_NAME___API_KEY``.
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@ -82,8 +84,24 @@ class __ModuleName__Retriever(BaseRetriever):
|
|||||||
|
|
||||||
# TODO: Example output.
|
# TODO: Example output.
|
||||||
|
|
||||||
""" # noqa: E501
|
"""
|
||||||
|
|
||||||
|
k: int = 3
|
||||||
|
|
||||||
# TODO: This method must be implemented to retrieve documents.
|
# TODO: This method must be implemented to retrieve documents.
|
||||||
def _get_relevant_documents(self, query: str) -> List[Document]:
|
def _get_relevant_documents(
|
||||||
raise NotImplementedError()
|
self, query: str, *, run_manager: CallbackManagerForRetrieverRun, **kwargs: Any
|
||||||
|
) -> List[Document]:
|
||||||
|
k = kwargs.get("k", self.k)
|
||||||
|
return [
|
||||||
|
Document(page_content=f"Result {i} for query: {query}") for i in range(k)
|
||||||
|
]
|
||||||
|
|
||||||
|
# optional: add custom async implementations here
|
||||||
|
# async def _aget_relevant_documents(
|
||||||
|
# self,
|
||||||
|
# query: str,
|
||||||
|
# *,
|
||||||
|
# run_manager: AsyncCallbackManagerForRetrieverRun,
|
||||||
|
# **kwargs: Any,
|
||||||
|
# ) -> List[Document]: ...
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool, BaseToolKit
|
from langchain_core.tools import BaseTool, BaseToolkit
|
||||||
|
|
||||||
|
|
||||||
class __ModuleName__Toolkit(BaseToolKit):
|
class __ModuleName__Toolkit(BaseToolkit):
|
||||||
# TODO: Replace all TODOs in docstring. See example docstring:
|
# TODO: Replace all TODOs in docstring. See example docstring:
|
||||||
# https://github.com/langchain-ai/langchain/blob/c123cb2b304f52ab65db4714eeec46af69a861ec/libs/community/langchain_community/agent_toolkits/sql/toolkit.py#L19
|
# https://github.com/langchain-ai/langchain/blob/c123cb2b304f52ab65db4714eeec46af69a861ec/libs/community/langchain_community/agent_toolkits/sql/toolkit.py#L19
|
||||||
"""__ModuleName__ toolkit.
|
"""__ModuleName__ toolkit.
|
||||||
|
@ -6,10 +6,10 @@ from langchain_core.callbacks import (
|
|||||||
CallbackManagerForToolRun,
|
CallbackManagerForToolRun,
|
||||||
)
|
)
|
||||||
from langchain_core.tools import BaseTool
|
from langchain_core.tools import BaseTool
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
class __ModuleName__Input(BaseModel):
|
class __ModuleName__ToolInput(BaseModel):
|
||||||
"""Input schema for __ModuleName__ tool.
|
"""Input schema for __ModuleName__ tool.
|
||||||
|
|
||||||
This docstring is **not** part of what is sent to the model when performing tool
|
This docstring is **not** part of what is sent to the model when performing tool
|
||||||
@ -18,12 +18,11 @@ class __ModuleName__Input(BaseModel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: Add input args and descriptions.
|
# TODO: Add input args and descriptions.
|
||||||
# a: int = Field(..., description="first number")
|
a: int = Field(..., description="first number to add")
|
||||||
# b: int = Field(0, description="second number")
|
b: int = Field(..., description="second number to add")
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class __ModuleName__Tool(BaseTool):
|
class __ModuleName__Tool(BaseTool): # type: ignore[override]
|
||||||
"""__ModuleName__ tool.
|
"""__ModuleName__ tool.
|
||||||
|
|
||||||
Setup:
|
Setup:
|
||||||
@ -69,24 +68,26 @@ class __ModuleName__Tool(BaseTool):
|
|||||||
"""The name that is passed to the model when performing tool calling."""
|
"""The name that is passed to the model when performing tool calling."""
|
||||||
description: str = "TODO: Tool description."
|
description: str = "TODO: Tool description."
|
||||||
"""The description that is passed to the model when performing tool calling."""
|
"""The description that is passed to the model when performing tool calling."""
|
||||||
args_schema: Type[BaseModel] = __ModuleName__Input
|
args_schema: Type[BaseModel] = __ModuleName__ToolInput
|
||||||
"""The schema that is passed to the model when performing tool calling."""
|
"""The schema that is passed to the model when performing tool calling."""
|
||||||
|
|
||||||
# TODO: Add any other init params for the tool.
|
# TODO: Add any other init params for the tool.
|
||||||
# param1: Optional[str]
|
# param1: Optional[str]
|
||||||
# """param1 determines foobar"""
|
# """param1 determines foobar"""
|
||||||
|
|
||||||
# TODO: Replaced *args with real tool arguments.
|
# TODO: Replaced (a, b) with real tool arguments.
|
||||||
def _run(
|
def _run(
|
||||||
self, *args, run_manager: Optional[CallbackManagerForToolRun] = None
|
self, a: int, b: int, *, run_manager: Optional[CallbackManagerForToolRun] = None
|
||||||
) -> str:
|
) -> str:
|
||||||
raise NotImplementedError
|
return str(a + b + 80)
|
||||||
|
|
||||||
# TODO: Implement if tool has native async functionality, otherwise delete.
|
# TODO: Implement if tool has native async functionality, otherwise delete.
|
||||||
|
|
||||||
# async def _arun(
|
# async def _arun(
|
||||||
# self,
|
# self,
|
||||||
# *args,
|
# a: int,
|
||||||
|
# b: int,
|
||||||
|
# *,
|
||||||
# run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
|
# run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
|
||||||
# ) -> str:
|
# ) -> str:
|
||||||
# ...
|
# ...
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
|
||||||
from functools import partial
|
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
Any,
|
Any,
|
||||||
@ -160,6 +158,8 @@ class __ModuleName__VectorStore(VectorStore):
|
|||||||
|
|
||||||
""" # noqa: E501
|
""" # noqa: E501
|
||||||
|
|
||||||
|
_database: dict[str, tuple[Document, list[float]]] = {}
|
||||||
|
|
||||||
def add_texts(
|
def add_texts(
|
||||||
self,
|
self,
|
||||||
texts: Iterable[str],
|
texts: Iterable[str],
|
||||||
@ -168,65 +168,70 @@ class __ModuleName__VectorStore(VectorStore):
|
|||||||
) -> List[str]:
|
) -> List[str]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def aadd_texts(
|
# optional: add custom async implementations
|
||||||
self,
|
# async def aadd_texts(
|
||||||
texts: Iterable[str],
|
# self,
|
||||||
metadatas: Optional[List[dict]] = None,
|
# texts: Iterable[str],
|
||||||
**kwargs: Any,
|
# metadatas: Optional[List[dict]] = None,
|
||||||
) -> List[str]:
|
# **kwargs: Any,
|
||||||
return await asyncio.get_running_loop().run_in_executor(
|
# ) -> List[str]:
|
||||||
None, partial(self.add_texts, **kwargs), texts, metadatas
|
# return await asyncio.get_running_loop().run_in_executor(
|
||||||
)
|
# None, partial(self.add_texts, **kwargs), texts, metadatas
|
||||||
|
# )
|
||||||
|
|
||||||
def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> Optional[bool]:
|
def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> Optional[bool]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def adelete(
|
# optional: add custom async implementations
|
||||||
self, ids: Optional[List[str]] = None, **kwargs: Any
|
# async def adelete(
|
||||||
) -> Optional[bool]:
|
# self, ids: Optional[List[str]] = None, **kwargs: Any
|
||||||
raise NotImplementedError
|
# ) -> Optional[bool]:
|
||||||
|
# raise NotImplementedError
|
||||||
|
|
||||||
def similarity_search(
|
def similarity_search(
|
||||||
self, query: str, k: int = 4, **kwargs: Any
|
self, query: str, k: int = 4, **kwargs: Any
|
||||||
) -> List[Document]:
|
) -> List[Document]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def asimilarity_search(
|
# optional: add custom async implementations
|
||||||
self, query: str, k: int = 4, **kwargs: Any
|
# async def asimilarity_search(
|
||||||
) -> List[Document]:
|
# self, query: str, k: int = 4, **kwargs: Any
|
||||||
# This is a temporary workaround to make the similarity search
|
# ) -> List[Document]:
|
||||||
# asynchronous. The proper solution is to make the similarity search
|
# # This is a temporary workaround to make the similarity search
|
||||||
# asynchronous in the vector store implementations.
|
# # asynchronous. The proper solution is to make the similarity search
|
||||||
func = partial(self.similarity_search, query, k=k, **kwargs)
|
# # asynchronous in the vector store implementations.
|
||||||
return await asyncio.get_event_loop().run_in_executor(None, func)
|
# func = partial(self.similarity_search, query, k=k, **kwargs)
|
||||||
|
# return await asyncio.get_event_loop().run_in_executor(None, func)
|
||||||
|
|
||||||
def similarity_search_with_score(
|
def similarity_search_with_score(
|
||||||
self, *args: Any, **kwargs: Any
|
self, *args: Any, **kwargs: Any
|
||||||
) -> List[Tuple[Document, float]]:
|
) -> List[Tuple[Document, float]]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def asimilarity_search_with_score(
|
# optional: add custom async implementations
|
||||||
self, *args: Any, **kwargs: Any
|
# async def asimilarity_search_with_score(
|
||||||
) -> List[Tuple[Document, float]]:
|
# self, *args: Any, **kwargs: Any
|
||||||
# This is a temporary workaround to make the similarity search
|
# ) -> List[Tuple[Document, float]]:
|
||||||
# asynchronous. The proper solution is to make the similarity search
|
# # This is a temporary workaround to make the similarity search
|
||||||
# asynchronous in the vector store implementations.
|
# # asynchronous. The proper solution is to make the similarity search
|
||||||
func = partial(self.similarity_search_with_score, *args, **kwargs)
|
# # asynchronous in the vector store implementations.
|
||||||
return await asyncio.get_event_loop().run_in_executor(None, func)
|
# func = partial(self.similarity_search_with_score, *args, **kwargs)
|
||||||
|
# return await asyncio.get_event_loop().run_in_executor(None, func)
|
||||||
|
|
||||||
def similarity_search_by_vector(
|
def similarity_search_by_vector(
|
||||||
self, embedding: List[float], k: int = 4, **kwargs: Any
|
self, embedding: List[float], k: int = 4, **kwargs: Any
|
||||||
) -> List[Document]:
|
) -> List[Document]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def asimilarity_search_by_vector(
|
# optional: add custom async implementations
|
||||||
self, embedding: List[float], k: int = 4, **kwargs: Any
|
# async def asimilarity_search_by_vector(
|
||||||
) -> List[Document]:
|
# self, embedding: List[float], k: int = 4, **kwargs: Any
|
||||||
# This is a temporary workaround to make the similarity search
|
# ) -> List[Document]:
|
||||||
# asynchronous. The proper solution is to make the similarity search
|
# # This is a temporary workaround to make the similarity search
|
||||||
# asynchronous in the vector store implementations.
|
# # asynchronous. The proper solution is to make the similarity search
|
||||||
func = partial(self.similarity_search_by_vector, embedding, k=k, **kwargs)
|
# # asynchronous in the vector store implementations.
|
||||||
return await asyncio.get_event_loop().run_in_executor(None, func)
|
# func = partial(self.similarity_search_by_vector, embedding, k=k, **kwargs)
|
||||||
|
# return await asyncio.get_event_loop().run_in_executor(None, func)
|
||||||
|
|
||||||
def max_marginal_relevance_search(
|
def max_marginal_relevance_search(
|
||||||
self,
|
self,
|
||||||
@ -238,26 +243,27 @@ class __ModuleName__VectorStore(VectorStore):
|
|||||||
) -> List[Document]:
|
) -> List[Document]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def amax_marginal_relevance_search(
|
# optional: add custom async implementations
|
||||||
self,
|
# async def amax_marginal_relevance_search(
|
||||||
query: str,
|
# self,
|
||||||
k: int = 4,
|
# query: str,
|
||||||
fetch_k: int = 20,
|
# k: int = 4,
|
||||||
lambda_mult: float = 0.5,
|
# fetch_k: int = 20,
|
||||||
**kwargs: Any,
|
# lambda_mult: float = 0.5,
|
||||||
) -> List[Document]:
|
# **kwargs: Any,
|
||||||
# This is a temporary workaround to make the similarity search
|
# ) -> List[Document]:
|
||||||
# asynchronous. The proper solution is to make the similarity search
|
# # This is a temporary workaround to make the similarity search
|
||||||
# asynchronous in the vector store implementations.
|
# # asynchronous. The proper solution is to make the similarity search
|
||||||
func = partial(
|
# # asynchronous in the vector store implementations.
|
||||||
self.max_marginal_relevance_search,
|
# func = partial(
|
||||||
query,
|
# self.max_marginal_relevance_search,
|
||||||
k=k,
|
# query,
|
||||||
fetch_k=fetch_k,
|
# k=k,
|
||||||
lambda_mult=lambda_mult,
|
# fetch_k=fetch_k,
|
||||||
**kwargs,
|
# lambda_mult=lambda_mult,
|
||||||
)
|
# **kwargs,
|
||||||
return await asyncio.get_event_loop().run_in_executor(None, func)
|
# )
|
||||||
|
# return await asyncio.get_event_loop().run_in_executor(None, func)
|
||||||
|
|
||||||
def max_marginal_relevance_search_by_vector(
|
def max_marginal_relevance_search_by_vector(
|
||||||
self,
|
self,
|
||||||
@ -269,15 +275,16 @@ class __ModuleName__VectorStore(VectorStore):
|
|||||||
) -> List[Document]:
|
) -> List[Document]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def amax_marginal_relevance_search_by_vector(
|
# optional: add custom async implementations
|
||||||
self,
|
# async def amax_marginal_relevance_search_by_vector(
|
||||||
embedding: List[float],
|
# self,
|
||||||
k: int = 4,
|
# embedding: List[float],
|
||||||
fetch_k: int = 20,
|
# k: int = 4,
|
||||||
lambda_mult: float = 0.5,
|
# fetch_k: int = 20,
|
||||||
**kwargs: Any,
|
# lambda_mult: float = 0.5,
|
||||||
) -> List[Document]:
|
# **kwargs: Any,
|
||||||
raise NotImplementedError
|
# ) -> List[Document]:
|
||||||
|
# raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_texts(
|
def from_texts(
|
||||||
@ -289,17 +296,18 @@ class __ModuleName__VectorStore(VectorStore):
|
|||||||
) -> VST:
|
) -> VST:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
# optional: add custom async implementations
|
||||||
async def afrom_texts(
|
# @classmethod
|
||||||
cls: Type[VST],
|
# async def afrom_texts(
|
||||||
texts: List[str],
|
# cls: Type[VST],
|
||||||
embedding: Embeddings,
|
# texts: List[str],
|
||||||
metadatas: Optional[List[dict]] = None,
|
# embedding: Embeddings,
|
||||||
**kwargs: Any,
|
# metadatas: Optional[List[dict]] = None,
|
||||||
) -> VST:
|
# **kwargs: Any,
|
||||||
return await asyncio.get_running_loop().run_in_executor(
|
# ) -> VST:
|
||||||
None, partial(cls.from_texts, **kwargs), texts, embedding, metadatas
|
# return await asyncio.get_running_loop().run_in_executor(
|
||||||
)
|
# None, partial(cls.from_texts, **kwargs), texts, embedding, metadatas
|
||||||
|
# )
|
||||||
|
|
||||||
def _select_relevance_score_fn(self) -> Callable[[float], float]:
|
def _select_relevance_score_fn(self) -> Callable[[float], float]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = [ "poetry-core>=1.0.0",]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
@ -23,14 +23,16 @@ python = ">=3.9,<4.0"
|
|||||||
langchain-core = "^0.3.15"
|
langchain-core = "^0.3.15"
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
select = [ "E", "F", "I", "T201",]
|
select = ["E", "F", "I", "T201"]
|
||||||
|
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
omit = [ "tests/*",]
|
omit = ["tests/*"]
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
addopts = "--strict-markers --strict-config --durations=5"
|
addopts = "--strict-markers --strict-config --durations=5"
|
||||||
markers = [ "compile: mark placeholder test used to compile integration tests without running them",]
|
markers = [
|
||||||
|
"compile: mark placeholder test used to compile integration tests without running them",
|
||||||
|
]
|
||||||
asyncio_mode = "auto"
|
asyncio_mode = "auto"
|
||||||
|
|
||||||
[tool.poetry.group.test]
|
[tool.poetry.group.test]
|
||||||
@ -48,11 +50,14 @@ optional = true
|
|||||||
[tool.poetry.group.dev]
|
[tool.poetry.group.dev]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
|
||||||
[tool.poetry.group.test.dependencies]
|
[tool.poetry.group.test.dependencies]
|
||||||
pytest = "^7.4.3"
|
pytest = "^7.4.3"
|
||||||
pytest-asyncio = "^0.23.2"
|
pytest-asyncio = "^0.23.2"
|
||||||
pytest-socket = "^0.7.0"
|
pytest-socket = "^0.7.0"
|
||||||
pytest-watcher = "^0.3.4"
|
pytest-watcher = "^0.3.4"
|
||||||
|
langchain-tests = "^0.3.4"
|
||||||
|
|
||||||
[tool.poetry.group.codespell.dependencies]
|
[tool.poetry.group.codespell.dependencies]
|
||||||
codespell = "^2.2.6"
|
codespell = "^2.2.6"
|
||||||
@ -64,15 +69,3 @@ ruff = "^0.5"
|
|||||||
|
|
||||||
[tool.poetry.group.typing.dependencies]
|
[tool.poetry.group.typing.dependencies]
|
||||||
mypy = "^1.10"
|
mypy = "^1.10"
|
||||||
|
|
||||||
[tool.poetry.group.test.dependencies.langchain-core]
|
|
||||||
path = "../../core"
|
|
||||||
develop = true
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies.langchain-core]
|
|
||||||
path = "../../core"
|
|
||||||
develop = true
|
|
||||||
|
|
||||||
[tool.poetry.group.typing.dependencies.langchain-core]
|
|
||||||
path = "../../core"
|
|
||||||
develop = true
|
|
||||||
|
@ -1,64 +1,21 @@
|
|||||||
"""Test Chat__ModuleName__ chat model."""
|
"""Test Chat__ModuleName__ chat model."""
|
||||||
|
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from __module_name__.chat_models import Chat__ModuleName__
|
from __module_name__.chat_models import Chat__ModuleName__
|
||||||
|
from langchain_tests.integration_tests import ChatModelIntegrationTests
|
||||||
|
|
||||||
|
|
||||||
def test_stream() -> None:
|
class TestChatParrotLinkIntegration(ChatModelIntegrationTests):
|
||||||
"""Test streaming tokens from OpenAI."""
|
@property
|
||||||
llm = Chat__ModuleName__()
|
def chat_model_class(self) -> Type[Chat__ModuleName__]:
|
||||||
|
return Chat__ModuleName__
|
||||||
|
|
||||||
for token in llm.stream("I'm Pickle Rick"):
|
@property
|
||||||
assert isinstance(token.content, str)
|
def chat_model_params(self) -> dict:
|
||||||
|
# These should be parameters used to initialize your integration for testing
|
||||||
|
return {
|
||||||
async def test_astream() -> None:
|
"model": "bird-brain-001",
|
||||||
"""Test streaming tokens from OpenAI."""
|
"temperature": 0,
|
||||||
llm = Chat__ModuleName__()
|
"parrot_buffer_length": 50,
|
||||||
|
}
|
||||||
async for token in llm.astream("I'm Pickle Rick"):
|
|
||||||
assert isinstance(token.content, str)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_abatch() -> None:
|
|
||||||
"""Test streaming tokens from Chat__ModuleName__."""
|
|
||||||
llm = Chat__ModuleName__()
|
|
||||||
|
|
||||||
result = await llm.abatch(["I'm Pickle Rick", "I'm not Pickle Rick"])
|
|
||||||
for token in result:
|
|
||||||
assert isinstance(token.content, str)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_abatch_tags() -> None:
|
|
||||||
"""Test batch tokens from Chat__ModuleName__."""
|
|
||||||
llm = Chat__ModuleName__()
|
|
||||||
|
|
||||||
result = await llm.abatch(
|
|
||||||
["I'm Pickle Rick", "I'm not Pickle Rick"], config={"tags": ["foo"]}
|
|
||||||
)
|
|
||||||
for token in result:
|
|
||||||
assert isinstance(token.content, str)
|
|
||||||
|
|
||||||
|
|
||||||
def test_batch() -> None:
|
|
||||||
"""Test batch tokens from Chat__ModuleName__."""
|
|
||||||
llm = Chat__ModuleName__()
|
|
||||||
|
|
||||||
result = llm.batch(["I'm Pickle Rick", "I'm not Pickle Rick"])
|
|
||||||
for token in result:
|
|
||||||
assert isinstance(token.content, str)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_ainvoke() -> None:
|
|
||||||
"""Test invoke tokens from Chat__ModuleName__."""
|
|
||||||
llm = Chat__ModuleName__()
|
|
||||||
|
|
||||||
result = await llm.ainvoke("I'm Pickle Rick", config={"tags": ["foo"]})
|
|
||||||
assert isinstance(result.content, str)
|
|
||||||
|
|
||||||
|
|
||||||
def test_invoke() -> None:
|
|
||||||
"""Test invoke tokens from Chat__ModuleName__."""
|
|
||||||
llm = Chat__ModuleName__()
|
|
||||||
|
|
||||||
result = llm.invoke("I'm Pickle Rick", config=dict(tags=["foo"]))
|
|
||||||
assert isinstance(result.content, str)
|
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
"""Test __ModuleName__ embeddings."""
|
"""Test __ModuleName__ embeddings."""
|
||||||
|
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from __module_name__.embeddings import __ModuleName__Embeddings
|
from __module_name__.embeddings import __ModuleName__Embeddings
|
||||||
|
from langchain_tests.integration_tests import EmbeddingsIntegrationTests
|
||||||
|
|
||||||
|
|
||||||
def test___module_name___embedding_documents() -> None:
|
class TestParrotLinkEmbeddingsIntegration(EmbeddingsIntegrationTests):
|
||||||
"""Test cohere embeddings."""
|
@property
|
||||||
documents = ["foo bar"]
|
def embeddings_class(self) -> Type[__ModuleName__Embeddings]:
|
||||||
embedding = __ModuleName__Embeddings()
|
return __ModuleName__Embeddings
|
||||||
output = embedding.embed_documents(documents)
|
|
||||||
assert len(output) == 1
|
|
||||||
assert len(output[0]) > 0
|
|
||||||
|
|
||||||
|
@property
|
||||||
def test___module_name___embedding_query() -> None:
|
def embedding_model_params(self) -> dict:
|
||||||
"""Test cohere embeddings."""
|
return {"model": "nest-embed-001"}
|
||||||
document = "foo bar"
|
|
||||||
embedding = __ModuleName__Embeddings()
|
|
||||||
output = embedding.embed_query(document)
|
|
||||||
assert len(output) > 0
|
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
"""Test __ModuleName__LLM llm."""
|
|
||||||
|
|
||||||
from __module_name__.llms import __ModuleName__LLM
|
|
||||||
|
|
||||||
|
|
||||||
def test_stream() -> None:
|
|
||||||
"""Test streaming tokens from OpenAI."""
|
|
||||||
llm = __ModuleName__LLM()
|
|
||||||
|
|
||||||
for token in llm.stream("I'm Pickle Rick"):
|
|
||||||
assert isinstance(token, str)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_astream() -> None:
|
|
||||||
"""Test streaming tokens from OpenAI."""
|
|
||||||
llm = __ModuleName__LLM()
|
|
||||||
|
|
||||||
async for token in llm.astream("I'm Pickle Rick"):
|
|
||||||
assert isinstance(token, str)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_abatch() -> None:
|
|
||||||
"""Test streaming tokens from __ModuleName__LLM."""
|
|
||||||
llm = __ModuleName__LLM()
|
|
||||||
|
|
||||||
result = await llm.abatch(["I'm Pickle Rick", "I'm not Pickle Rick"])
|
|
||||||
for token in result:
|
|
||||||
assert isinstance(token, str)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_abatch_tags() -> None:
|
|
||||||
"""Test batch tokens from __ModuleName__LLM."""
|
|
||||||
llm = __ModuleName__LLM()
|
|
||||||
|
|
||||||
result = await llm.abatch(
|
|
||||||
["I'm Pickle Rick", "I'm not Pickle Rick"], config={"tags": ["foo"]}
|
|
||||||
)
|
|
||||||
for token in result:
|
|
||||||
assert isinstance(token, str)
|
|
||||||
|
|
||||||
|
|
||||||
def test_batch() -> None:
|
|
||||||
"""Test batch tokens from __ModuleName__LLM."""
|
|
||||||
llm = __ModuleName__LLM()
|
|
||||||
|
|
||||||
result = llm.batch(["I'm Pickle Rick", "I'm not Pickle Rick"])
|
|
||||||
for token in result:
|
|
||||||
assert isinstance(token, str)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_ainvoke() -> None:
|
|
||||||
"""Test invoke tokens from __ModuleName__LLM."""
|
|
||||||
llm = __ModuleName__LLM()
|
|
||||||
|
|
||||||
result = await llm.ainvoke("I'm Pickle Rick", config={"tags": ["foo"]})
|
|
||||||
assert isinstance(result, str)
|
|
||||||
|
|
||||||
|
|
||||||
def test_invoke() -> None:
|
|
||||||
"""Test invoke tokens from __ModuleName__LLM."""
|
|
||||||
llm = __ModuleName__LLM()
|
|
||||||
|
|
||||||
result = llm.invoke("I'm Pickle Rick", config=dict(tags=["foo"]))
|
|
||||||
assert isinstance(result, str)
|
|
@ -0,0 +1,24 @@
|
|||||||
|
from typing import Type
|
||||||
|
|
||||||
|
from __module_name__.retrievers import __ModuleName__Retriever
|
||||||
|
from langchain_tests.integration_tests import (
|
||||||
|
RetrieversIntegrationTests,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Test__ModuleName__Retriever(RetrieversIntegrationTests):
|
||||||
|
@property
|
||||||
|
def retriever_constructor(self) -> Type[__ModuleName__Retriever]:
|
||||||
|
"""Get an empty vectorstore for unit tests."""
|
||||||
|
return __ModuleName__Retriever
|
||||||
|
|
||||||
|
@property
|
||||||
|
def retriever_constructor_params(self) -> dict:
|
||||||
|
return {"k": 2}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def retriever_query_example(self) -> str:
|
||||||
|
"""
|
||||||
|
Returns a dictionary representing the "args" of an example retriever call.
|
||||||
|
"""
|
||||||
|
return "example query"
|
@ -0,0 +1,27 @@
|
|||||||
|
from typing import Type
|
||||||
|
|
||||||
|
from __module_name__.tools import __ModuleName__Tool
|
||||||
|
from langchain_tests.integration_tests import ToolsIntegrationTests
|
||||||
|
|
||||||
|
|
||||||
|
class TestParrotMultiplyToolIntegration(ToolsIntegrationTests):
|
||||||
|
@property
|
||||||
|
def tool_constructor(self) -> Type[__ModuleName__Tool]:
|
||||||
|
return __ModuleName__Tool
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tool_constructor_params(self) -> dict:
|
||||||
|
# if your tool constructor instead required initialization arguments like
|
||||||
|
# `def __init__(self, some_arg: int):`, you would return those here
|
||||||
|
# as a dictionary, e.g.: `return {'some_arg': 42}`
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tool_invoke_params_example(self) -> dict:
|
||||||
|
"""
|
||||||
|
Returns a dictionary representing the "args" of an example tool call.
|
||||||
|
|
||||||
|
This should NOT be a ToolCall dict - i.e. it should not
|
||||||
|
have {"name", "id", "args"} keys.
|
||||||
|
"""
|
||||||
|
return {"a": 2, "b": 3}
|
@ -0,0 +1,37 @@
|
|||||||
|
from typing import AsyncGenerator, Generator
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from __module_name__.vectorstores import __ModuleName__VectorStore
|
||||||
|
from langchain_core.vectorstores import VectorStore
|
||||||
|
from langchain_tests.integration_tests import (
|
||||||
|
AsyncReadWriteTestSuite,
|
||||||
|
ReadWriteTestSuite,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Test__ModuleName__VectorStoreSync(ReadWriteTestSuite):
|
||||||
|
@pytest.fixture()
|
||||||
|
def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore
|
||||||
|
"""Get an empty vectorstore for unit tests."""
|
||||||
|
store = __ModuleName__VectorStore()
|
||||||
|
# note: store should be EMPTY at this point
|
||||||
|
# if you need to delete data, you may do so here
|
||||||
|
try:
|
||||||
|
yield store
|
||||||
|
finally:
|
||||||
|
# cleanup operations, or deleting data
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Test__ModuleName__VectorStoreAsync(AsyncReadWriteTestSuite):
|
||||||
|
@pytest.fixture()
|
||||||
|
async def vectorstore(self) -> AsyncGenerator[VectorStore, None]: # type: ignore
|
||||||
|
"""Get an empty vectorstore for unit tests."""
|
||||||
|
store = __ModuleName__VectorStore()
|
||||||
|
# note: store should be EMPTY at this point
|
||||||
|
# if you need to delete data, you may do so here
|
||||||
|
try:
|
||||||
|
yield store
|
||||||
|
finally:
|
||||||
|
# cleanup operations, or deleting data
|
||||||
|
pass
|
@ -1,8 +1,21 @@
|
|||||||
"""Test chat model integration."""
|
"""Test chat model integration."""
|
||||||
|
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from __module_name__.chat_models import Chat__ModuleName__
|
from __module_name__.chat_models import Chat__ModuleName__
|
||||||
|
from langchain_tests.unit_tests import ChatModelUnitTests
|
||||||
|
|
||||||
|
|
||||||
def test_initialization() -> None:
|
class TestChat__ModuleName__Unit(ChatModelUnitTests):
|
||||||
"""Test chat model initialization."""
|
@property
|
||||||
Chat__ModuleName__()
|
def chat_model_class(self) -> Type[Chat__ModuleName__]:
|
||||||
|
return Chat__ModuleName__
|
||||||
|
|
||||||
|
@property
|
||||||
|
def chat_model_params(self) -> dict:
|
||||||
|
# These should be parameters used to initialize your integration for testing
|
||||||
|
return {
|
||||||
|
"model": "bird-brain-001",
|
||||||
|
"temperature": 0,
|
||||||
|
"parrot_buffer_length": 50,
|
||||||
|
}
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
"""Test embedding model integration."""
|
"""Test embedding model integration."""
|
||||||
|
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from __module_name__.embeddings import __ModuleName__Embeddings
|
from __module_name__.embeddings import __ModuleName__Embeddings
|
||||||
|
from langchain_tests.unit_tests import EmbeddingsUnitTests
|
||||||
|
|
||||||
|
|
||||||
def test_initialization() -> None:
|
class TestParrotLinkEmbeddingsUnit(EmbeddingsUnitTests):
|
||||||
"""Test embedding model initialization."""
|
@property
|
||||||
__ModuleName__Embeddings()
|
def embeddings_class(self) -> Type[__ModuleName__Embeddings]:
|
||||||
|
return __ModuleName__Embeddings
|
||||||
|
|
||||||
|
@property
|
||||||
|
def embedding_model_params(self) -> dict:
|
||||||
|
return {"model": "nest-embed-001"}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
from __module_name__ import __all__
|
|
||||||
|
|
||||||
EXPECTED_ALL = [
|
|
||||||
"__ModuleName__LLM",
|
|
||||||
"Chat__ModuleName__",
|
|
||||||
"__ModuleName__VectorStore",
|
|
||||||
"__ModuleName__Embeddings",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def test_all_imports() -> None:
|
|
||||||
assert sorted(EXPECTED_ALL) == sorted(__all__)
|
|
@ -1,8 +0,0 @@
|
|||||||
"""Test __ModuleName__ Chat API wrapper."""
|
|
||||||
|
|
||||||
from __module_name__ import __ModuleName__LLM
|
|
||||||
|
|
||||||
|
|
||||||
def test_initialization() -> None:
|
|
||||||
"""Test integration initialization."""
|
|
||||||
__ModuleName__LLM()
|
|
@ -0,0 +1,27 @@
|
|||||||
|
from typing import Type
|
||||||
|
|
||||||
|
from __module_name__.tools import __ModuleName__Tool
|
||||||
|
from langchain_tests.unit_tests import ToolsUnitTests
|
||||||
|
|
||||||
|
|
||||||
|
class TestParrotMultiplyToolUnit(ToolsUnitTests):
|
||||||
|
@property
|
||||||
|
def tool_constructor(self) -> Type[__ModuleName__Tool]:
|
||||||
|
return __ModuleName__Tool
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tool_constructor_params(self) -> dict:
|
||||||
|
# if your tool constructor instead required initialization arguments like
|
||||||
|
# `def __init__(self, some_arg: int):`, you would return those here
|
||||||
|
# as a dictionary, e.g.: `return {'some_arg': 42}`
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tool_invoke_params_example(self) -> dict:
|
||||||
|
"""
|
||||||
|
Returns a dictionary representing the "args" of an example tool call.
|
||||||
|
|
||||||
|
This should NOT be a ToolCall dict - i.e. it should not
|
||||||
|
have {"name", "id", "args"} keys.
|
||||||
|
"""
|
||||||
|
return {"a": 2, "b": 3}
|
@ -1,6 +0,0 @@
|
|||||||
from __module_name__.vectorstores import __ModuleName__VectorStore
|
|
||||||
|
|
||||||
|
|
||||||
def test_initialization() -> None:
|
|
||||||
"""Test integration vectorstore initialization."""
|
|
||||||
__ModuleName__VectorStore()
|
|
@ -6,7 +6,7 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Dict, Optional, cast
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
from typing_extensions import Annotated, TypedDict
|
from typing_extensions import Annotated, TypedDict
|
||||||
@ -15,19 +15,17 @@ from langchain_cli.utils.find_replace import replace_file, replace_glob
|
|||||||
|
|
||||||
integration_cli = typer.Typer(no_args_is_help=True, add_completion=False)
|
integration_cli = typer.Typer(no_args_is_help=True, add_completion=False)
|
||||||
|
|
||||||
Replacements = TypedDict(
|
|
||||||
"Replacements",
|
class Replacements(TypedDict):
|
||||||
{
|
__package_name__: str
|
||||||
"__package_name__": str,
|
__module_name__: str
|
||||||
"__module_name__": str,
|
__ModuleName__: str
|
||||||
"__ModuleName__": str,
|
__MODULE_NAME__: str
|
||||||
"__MODULE_NAME__": str,
|
__package_name_short__: str
|
||||||
"__package_name_short__": str,
|
__package_name_short_snake__: str
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _process_name(name: str, *, community: bool = False):
|
def _process_name(name: str, *, community: bool = False) -> Replacements:
|
||||||
preprocessed = name.replace("_", "-").lower()
|
preprocessed = name.replace("_", "-").lower()
|
||||||
|
|
||||||
if preprocessed.startswith("langchain-"):
|
if preprocessed.startswith("langchain-"):
|
||||||
@ -42,7 +40,7 @@ def _process_name(name: str, *, community: bool = False):
|
|||||||
raise ValueError("Name should not end with `-`.")
|
raise ValueError("Name should not end with `-`.")
|
||||||
if preprocessed.find("--") != -1:
|
if preprocessed.find("--") != -1:
|
||||||
raise ValueError("Name should not contain consecutive hyphens.")
|
raise ValueError("Name should not contain consecutive hyphens.")
|
||||||
replacements = {
|
replacements: Replacements = {
|
||||||
"__package_name__": f"langchain-{preprocessed}",
|
"__package_name__": f"langchain-{preprocessed}",
|
||||||
"__module_name__": "langchain_" + preprocessed.replace("-", "_"),
|
"__module_name__": "langchain_" + preprocessed.replace("-", "_"),
|
||||||
"__ModuleName__": preprocessed.title().replace("-", ""),
|
"__ModuleName__": preprocessed.title().replace("-", ""),
|
||||||
@ -52,7 +50,7 @@ def _process_name(name: str, *, community: bool = False):
|
|||||||
}
|
}
|
||||||
if community:
|
if community:
|
||||||
replacements["__module_name__"] = preprocessed.replace("-", "_")
|
replacements["__module_name__"] = preprocessed.replace("-", "_")
|
||||||
return Replacements(replacements)
|
return replacements
|
||||||
|
|
||||||
|
|
||||||
@integration_cli.command()
|
@integration_cli.command()
|
||||||
@ -74,16 +72,7 @@ def new(
|
|||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Creates a new integration package.
|
Creates a new integration package.
|
||||||
|
|
||||||
Should be run from libs/partners
|
|
||||||
"""
|
"""
|
||||||
# confirm that we are in the right directory
|
|
||||||
if not Path.cwd().name == "partners" or not Path.cwd().parent.name == "libs":
|
|
||||||
typer.echo(
|
|
||||||
"This command should be run from the `libs/partners` directory in the "
|
|
||||||
"langchain-ai/langchain monorepo. Continuing is NOT recommended."
|
|
||||||
)
|
|
||||||
typer.confirm("Are you sure you want to continue?", abort=True)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
replacements = _process_name(name)
|
replacements = _process_name(name)
|
||||||
@ -104,7 +93,7 @@ def new(
|
|||||||
"Name of integration in PascalCase", default=replacements["__ModuleName__"]
|
"Name of integration in PascalCase", default=replacements["__ModuleName__"]
|
||||||
)
|
)
|
||||||
|
|
||||||
destination_dir = Path.cwd() / replacements["__package_name_short__"]
|
destination_dir = Path.cwd() / replacements["__package_name__"]
|
||||||
if destination_dir.exists():
|
if destination_dir.exists():
|
||||||
typer.echo(f"Folder {destination_dir} exists.")
|
typer.echo(f"Folder {destination_dir} exists.")
|
||||||
raise typer.Exit(code=1)
|
raise typer.Exit(code=1)
|
||||||
@ -118,7 +107,7 @@ def new(
|
|||||||
shutil.move(destination_dir / "integration_template", package_dir)
|
shutil.move(destination_dir / "integration_template", package_dir)
|
||||||
|
|
||||||
# replacements in files
|
# replacements in files
|
||||||
replace_glob(destination_dir, "**/*", replacements)
|
replace_glob(destination_dir, "**/*", cast(Dict[str, str], replacements))
|
||||||
|
|
||||||
# poetry install
|
# poetry install
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
@ -226,4 +215,4 @@ def create_doc(
|
|||||||
shutil.copy(docs_template, destination_path)
|
shutil.copy(docs_template, destination_path)
|
||||||
|
|
||||||
# replacements in file
|
# replacements in file
|
||||||
replace_file(destination_path, replacements)
|
replace_file(destination_path, cast(Dict[str, str], replacements))
|
||||||
|
@ -17,7 +17,7 @@ PARTNER_PKGS = PKGS_ROOT / "partners"
|
|||||||
class ImportExtractor(ast.NodeVisitor):
|
class ImportExtractor(ast.NodeVisitor):
|
||||||
def __init__(self, *, from_package: Optional[str] = None) -> None:
|
def __init__(self, *, from_package: Optional[str] = None) -> None:
|
||||||
"""Extract all imports from the given code, optionally filtering by package."""
|
"""Extract all imports from the given code, optionally filtering by package."""
|
||||||
self.imports = []
|
self.imports: list = []
|
||||||
self.package = from_package
|
self.package = from_package
|
||||||
|
|
||||||
def visit_ImportFrom(self, node):
|
def visit_ImportFrom(self, node):
|
||||||
@ -68,7 +68,7 @@ def find_subclasses_in_module(module, classes_: List[Type]) -> List[str]:
|
|||||||
return subclasses
|
return subclasses
|
||||||
|
|
||||||
|
|
||||||
def _get_all_classnames_from_file(file: str, pkg: str) -> List[Tuple[str, str]]:
|
def _get_all_classnames_from_file(file: Path, pkg: str) -> List[Tuple[str, str]]:
|
||||||
"""Extract all class names from a file."""
|
"""Extract all class names from a file."""
|
||||||
with open(file, encoding="utf-8") as f:
|
with open(file, encoding="utf-8") as f:
|
||||||
code = f.read()
|
code = f.read()
|
||||||
@ -145,7 +145,7 @@ def find_imports_from_package(
|
|||||||
return extractor.imports
|
return extractor.imports
|
||||||
|
|
||||||
|
|
||||||
def _get_current_module(path: str, pkg_root: str) -> str:
|
def _get_current_module(path: Path, pkg_root: str) -> str:
|
||||||
"""Convert a path to a module name."""
|
"""Convert a path to a module name."""
|
||||||
path_as_pathlib = pathlib.Path(os.path.abspath(path))
|
path_as_pathlib = pathlib.Path(os.path.abspath(path))
|
||||||
relative_path = path_as_pathlib.relative_to(pkg_root).with_suffix("")
|
relative_path = path_as_pathlib.relative_to(pkg_root).with_suffix("")
|
||||||
|
@ -4,7 +4,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import rich
|
import rich
|
||||||
import typer
|
import typer
|
||||||
from gritql import run
|
from gritql import run # type: ignore
|
||||||
from typer import Option
|
from typer import Option
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ def find_and_replace(source: str, replacements: Dict[str, str]) -> str:
|
|||||||
return rtn
|
return rtn
|
||||||
|
|
||||||
|
|
||||||
def replace_file(source: Path, replacements: Dict[str, str]) -> None:
|
def replace_file(source: Path, replacements: dict[str, str]) -> None:
|
||||||
try:
|
try:
|
||||||
content = source.read_text()
|
content = source.read_text()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
@ -24,7 +24,7 @@ def replace_file(source: Path, replacements: Dict[str, str]) -> None:
|
|||||||
source.write_text(new_content)
|
source.write_text(new_content)
|
||||||
|
|
||||||
|
|
||||||
def replace_glob(parent: Path, glob: str, replacements: Dict[str, str]) -> None:
|
def replace_glob(parent: Path, glob: str, replacements: dict[str, str]) -> None:
|
||||||
for file in parent.glob(glob):
|
for file in parent.glob(glob):
|
||||||
if not file.is_file():
|
if not file.is_file():
|
||||||
continue
|
continue
|
||||||
|
1121
libs/cli/poetry.lock
generated
1121
libs/cli/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -25,16 +25,18 @@ langchain = "langchain_cli.cli:app"
|
|||||||
langchain-cli = "langchain_cli.cli:app"
|
langchain-cli = "langchain_cli.cli:app"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
poethepoet = "^0.24.1"
|
|
||||||
pytest = "^7.4.2"
|
pytest = "^7.4.2"
|
||||||
pytest-watch = "^4.2.0"
|
pytest-watch = "^4.2.0"
|
||||||
|
|
||||||
[tool.poetry.group.lint.dependencies]
|
[tool.poetry.group.lint.dependencies]
|
||||||
ruff = "^0.5"
|
ruff = "^0.5"
|
||||||
|
mypy = "^1.13.0"
|
||||||
|
|
||||||
[tool.poetry.group.test.dependencies]
|
[tool.poetry.group.test.dependencies]
|
||||||
|
langchain = {path = "../langchain", develop = true}
|
||||||
|
|
||||||
[tool.poetry.group.typing.dependencies]
|
[tool.poetry.group.typing.dependencies]
|
||||||
|
langchain = {path = "../langchain", develop = true}
|
||||||
|
|
||||||
[tool.poetry.group.test_integration.dependencies]
|
[tool.poetry.group.test_integration.dependencies]
|
||||||
|
|
||||||
@ -50,22 +52,11 @@ select = [
|
|||||||
"T201", # print
|
"T201", # print
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.poe.tasks]
|
[tool.mypy]
|
||||||
test = "poetry run pytest tests"
|
exclude = [
|
||||||
watch = "poetry run ptw"
|
"langchain_cli/integration_template",
|
||||||
version = "poetry version --short"
|
"langchain_cli/package_template",
|
||||||
bump = ["_bump_1", "_bump_2"]
|
]
|
||||||
lint = ["_lint", "_check_formatting"]
|
|
||||||
format = ["_format", "_lint_fix"]
|
|
||||||
|
|
||||||
_bump_2.shell = """sed -i "" "/^__version__ =/c\\ \n__version__ = \\"$version\\"\n" langchain_cli/cli.py"""
|
|
||||||
_bump_2.uses = { version = "version" }
|
|
||||||
|
|
||||||
_bump_1 = "poetry version patch"
|
|
||||||
_check_formatting = "poetry run ruff format . --diff"
|
|
||||||
_lint = "poetry run ruff check ."
|
|
||||||
_format = "poetry run ruff format ."
|
|
||||||
_lint_fix = "poetry run ruff check . --fix"
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# type: ignore
|
||||||
"""Script to generate migrations for the migration script."""
|
"""Script to generate migrations for the migration script."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
7
libs/cli/tests/integration_tests/test_compile.py
Normal file
7
libs/cli/tests/integration_tests/test_compile.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.compile
|
||||||
|
def test_placeholder() -> None:
|
||||||
|
"""Used for compiling integration tests without running any real tests."""
|
||||||
|
pass
|
@ -41,6 +41,7 @@ def find_issue(current: Folder, expected: Folder) -> str:
|
|||||||
return "Unknown"
|
return "Unknown"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(reason="grit may not be installed in env")
|
||||||
def test_command_line(tmp_path: Path) -> None:
|
def test_command_line(tmp_path: Path) -> None:
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ modules = [
|
|||||||
"vectorstores",
|
"vectorstores",
|
||||||
"embeddings",
|
"embeddings",
|
||||||
"tools",
|
"tools",
|
||||||
|
"retrievers",
|
||||||
]
|
]
|
||||||
|
|
||||||
for module in modules:
|
for module in modules:
|
||||||
|
Loading…
Reference in New Issue
Block a user