Skip to content
8 changes: 4 additions & 4 deletions libs/elasticsearch/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "langchain-elasticsearch"
version = "0.4.0"
version = "1.0.0"
description = "An integration package connecting Elasticsearch and LangChain"
authors = []
readme = "README.md"
Expand All @@ -12,8 +12,8 @@ license = "MIT"

[tool.poetry.dependencies]
python = ">=3.10,<4.0"
langchain-core = "^0.3.0"
elasticsearch = {version = ">=8.16.0,<9.0.0", extras = ["vectorstore_mmr"]}
langchain-core = "^1.0.0"
elasticsearch = {version = ">=8.19.0,<9.0.0", extras = ["vectorstore_mmr"]}

[tool.poetry.group.test]
optional = true
Expand All @@ -25,8 +25,8 @@ pytest-mock = "^3.10.0"
syrupy = "^4.0.2"
pytest-watcher = "^0.3.4"
pytest-asyncio = "^0.21.1"
langchain = ">=0.3.10,<1.0.0"
aiohttp = "^3.8.3"
langchain-classic = "^1.0.0"
Copy link
Collaborator

@miguelgrinberg miguelgrinberg Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it strange that we have a mix of langchain dependencies here, some at 0.3 and some at 1.0. If we intend for this 1.0 release to also support langchain 0.3, then we should have tests in place that ensure everything works. Or if we decide that we only support langchain 1.0 and up, then the 0.3 references should be upgraded.

Also, doesn't langchain-classic need to be given as a general dependency? You have it in the testing section right now, but you are importing from this package in the code.

Finally, the documentation for the langchain-classic package says this:

Legacy chains, langchain-community re-exports, indexing API, deprecated functionality, and more. In most cases, you should be using the main langchain package."

Have you looked into upgrading our implementation so that we can use the proper interfaces in langchain instead of the legacy or deprecated ones?

Copy link
Collaborator Author

@ssh-esh ssh-esh Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will look into your feedback, thank you for it. For the latter part, Could you tell me where you got that quote about the langchain-classic package? The migration docs says the main Lang chain package is now concerned with agents https://docs.langchain.com/oss/python/migrate/langchain-v1

The langchain package namespace has been significantly reduced in v1 to focus on essential building blocks for agents. The streamlined package makes it easier to discover and use the core functionality.

They also say

langchain-classic
If you were using any of the following from the langchain package, you’ll need to install langchain-classic and update your imports:

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's on their readme file in the "What is this?" section: https://github.com/langchain-ai/langchain/tree/master/libs/langchain#-what-is-this


[tool.poetry.group.codespell]
optional = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import pytest
from elasticsearch.helpers import BulkIndexError
from langchain.embeddings.cache import _value_serializer
from langchain.globals import set_llm_cache
from langchain_classic.embeddings.cache import _value_serializer
from langchain_core.globals import set_llm_cache
from langchain_core.language_models import BaseChatModel

from langchain_elasticsearch import (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import AsyncIterator

import pytest
from langchain.memory import ConversationBufferMemory
from langchain_classic.memory import ConversationBufferMemory
from langchain_core.messages import AIMessage, HumanMessage, message_to_dict

from langchain_elasticsearch.chat_history import AsyncElasticsearchChatMessageHistory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async def test_user_agent_header(
), f"The string '{user_agent}' does not match the expected pattern."

await index_test_data(es_client, index_name, "text")
await retriever.aget_relevant_documents("foo")
await retriever.ainvoke("foo")

search_request = es_client.transport.requests[-1] # type: ignore[attr-defined]
user_agent = search_request["headers"]["User-Agent"]
Expand Down Expand Up @@ -105,7 +105,7 @@ def body_func(query: str) -> Dict:
)

await index_test_data(retriever.client, index_name, text_field)
result = await retriever.aget_relevant_documents("foo")
result = await retriever.ainvoke("foo")

assert {r.page_content for r in result} == {"foo", "foo bar", "foo baz"}
assert {r.metadata["_id"] for r in result} == {"3", "1", "5"}
Expand Down Expand Up @@ -133,7 +133,7 @@ def body_func(query: str) -> Dict:
)

await index_test_data(es_client, index_name, text_field)
result = await retriever.aget_relevant_documents("foo")
result = await retriever.ainvoke("foo")

assert {r.page_content for r in result} == {"foo", "foo bar", "foo baz"}
assert {r.metadata["_id"] for r in result} == {"3", "1", "5"}
Expand Down Expand Up @@ -171,7 +171,7 @@ def body_func(query: str) -> Dict:

await index_test_data(es_client, index_name_1, text_field_1)
await index_test_data(es_client, index_name_2, text_field_2)
result = await retriever.aget_relevant_documents("foo")
result = await retriever.ainvoke("foo")

# matches from both indices
assert sorted([(r.page_content, r.metadata["_index"]) for r in result]) == [
Expand Down Expand Up @@ -206,7 +206,7 @@ def id_as_content(hit: Mapping[str, Any]) -> Document:
)

await index_test_data(es_client, index_name, text_field)
result = await retriever.aget_relevant_documents("foo")
result = await retriever.ainvoke("foo")

assert [r.page_content for r in result] == ["3", "1", "5"]
assert [r.metadata for r in result] == [meta, meta, meta]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ async def test_search_with_relevance_threshold(
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": similarity_of_second_ranked},
)
output = await retriever.aget_relevant_documents(query=query_string)
output = await retriever.ainvoke(query_string)

assert output == [
top3[0][0],
Expand Down Expand Up @@ -145,7 +145,7 @@ async def test_search_by_vector_with_relevance_threshold(
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": similarity_of_second_ranked},
)
output = await retriever.aget_relevant_documents(query=query_string)
output = await retriever.ainvoke(query_string)

assert output == [
top3[0][0],
Expand Down Expand Up @@ -823,38 +823,51 @@ def assert_query(
# 2. check query result is okay
es_output = await docsearch.client.search(
index=index_name,
query={
"bool": {
"filter": [],
"must": [{"match": {"text": {"query": "foo"}}}],
retriever={
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"bool": {
"filter": [],
"must": [{"match": {"text": {"query": "foo"}}}],
}
}
}
},
{
"knn": {
"field": "vector",
"filter": [],
"k": 3,
"num_candidates": 50,
"query_vector": [
0.06,
0.07,
0.01,
0.08,
0.03,
0.07,
0.09,
0.03,
0.09,
0.09,
0.04,
0.03,
0.08,
0.07,
0.06,
0.08,
],
}
},
],
"rank_constant": 1,
"rank_window_size": 5,
}
},
knn={
"field": "vector",
"filter": [],
"k": 3,
"num_candidates": 50,
"query_vector": [
0.06,
0.07,
0.01,
0.08,
0.03,
0.07,
0.09,
0.03,
0.09,
0.09,
0.04,
0.03,
0.08,
0.07,
0.06,
0.08,
],
},
size=3,
rank={"rrf": {"rank_constant": 1, "rank_window_size": 5}},
)

assert [o.page_content for o in output] == [
Expand Down Expand Up @@ -1081,7 +1094,7 @@ async def test_elasticsearch_with_relevance_threshold(
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": similarity_of_second_ranked},
)
output = await retriever.aget_relevant_documents(query=query_string)
output = await retriever.ainvoke(query_string)

assert output == [
top3[0][0],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import pytest
from elasticsearch.helpers import BulkIndexError
from langchain.embeddings.cache import _value_serializer
from langchain.globals import set_llm_cache
from langchain_classic.embeddings.cache import _value_serializer
from langchain_core.globals import set_llm_cache
from langchain_core.language_models import BaseChatModel

from langchain_elasticsearch import (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Iterator

import pytest
from langchain.memory import ConversationBufferMemory
from langchain_classic.memory import ConversationBufferMemory
from langchain_core.messages import AIMessage, HumanMessage, message_to_dict

from langchain_elasticsearch.chat_history import ElasticsearchChatMessageHistory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def test_user_agent_header(self, es_client: Elasticsearch, index_name: str) -> N
), f"The string '{user_agent}' does not match the expected pattern."

index_test_data(es_client, index_name, "text")
retriever.get_relevant_documents("foo")
retriever.invoke("foo")

search_request = es_client.transport.requests[-1] # type: ignore[attr-defined]
user_agent = search_request["headers"]["User-Agent"]
Expand Down Expand Up @@ -101,7 +101,7 @@ def body_func(query: str) -> Dict:
)

index_test_data(retriever.client, index_name, text_field)
result = retriever.get_relevant_documents("foo")
result = retriever.invoke("foo")

assert {r.page_content for r in result} == {"foo", "foo bar", "foo baz"}
assert {r.metadata["_id"] for r in result} == {"3", "1", "5"}
Expand All @@ -127,7 +127,7 @@ def body_func(query: str) -> Dict:
)

index_test_data(es_client, index_name, text_field)
result = retriever.get_relevant_documents("foo")
result = retriever.invoke("foo")

assert {r.page_content for r in result} == {"foo", "foo bar", "foo baz"}
assert {r.metadata["_id"] for r in result} == {"3", "1", "5"}
Expand Down Expand Up @@ -165,7 +165,7 @@ def body_func(query: str) -> Dict:

index_test_data(es_client, index_name_1, text_field_1)
index_test_data(es_client, index_name_2, text_field_2)
result = retriever.get_relevant_documents("foo")
result = retriever.invoke("foo")

# matches from both indices
assert sorted([(r.page_content, r.metadata["_index"]) for r in result]) == [
Expand Down Expand Up @@ -198,7 +198,7 @@ def id_as_content(hit: Mapping[str, Any]) -> Document:
)

index_test_data(es_client, index_name, text_field)
result = retriever.get_relevant_documents("foo")
result = retriever.invoke("foo")

assert [r.page_content for r in result] == ["3", "1", "5"]
assert [r.metadata for r in result] == [meta, meta, meta]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def test_search_with_relevance_threshold(
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": similarity_of_second_ranked},
)
output = retriever.get_relevant_documents(query=query_string)
output = retriever.invoke(query_string)

assert output == [
top3[0][0],
Expand Down Expand Up @@ -145,7 +145,7 @@ def test_search_by_vector_with_relevance_threshold(
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": similarity_of_second_ranked},
)
output = retriever.get_relevant_documents(query=query_string)
output = retriever.invoke(query_string)

assert output == [
top3[0][0],
Expand Down Expand Up @@ -807,38 +807,51 @@ def assert_query(
# 2. check query result is okay
es_output = docsearch.client.search(
index=index_name,
query={
"bool": {
"filter": [],
"must": [{"match": {"text": {"query": "foo"}}}],
retriever={
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"bool": {
"filter": [],
"must": [{"match": {"text": {"query": "foo"}}}],
}
}
}
},
{
"knn": {
"field": "vector",
"filter": [],
"k": 3,
"num_candidates": 50,
"query_vector": [
0.06,
0.07,
0.01,
0.08,
0.03,
0.07,
0.09,
0.03,
0.09,
0.09,
0.04,
0.03,
0.08,
0.07,
0.06,
0.08,
],
}
},
],
"rank_constant": 1,
"rank_window_size": 5,
}
},
knn={
"field": "vector",
"filter": [],
"k": 3,
"num_candidates": 50,
"query_vector": [
0.06,
0.07,
0.01,
0.08,
0.03,
0.07,
0.09,
0.03,
0.09,
0.09,
0.04,
0.03,
0.08,
0.07,
0.06,
0.08,
],
},
size=3,
rank={"rrf": {"rank_constant": 1, "rank_window_size": 5}},
)

assert [o.page_content for o in output] == [
Expand Down Expand Up @@ -1061,7 +1074,7 @@ def test_elasticsearch_with_relevance_threshold(
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": similarity_of_second_ranked},
)
output = retriever.get_relevant_documents(query=query_string)
output = retriever.invoke(query_string)

assert output == [
top3[0][0],
Expand Down
2 changes: 1 addition & 1 deletion libs/elasticsearch/tests/unit_tests/_async/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from _pytest.fixtures import FixtureRequest
from elastic_transport import ApiResponseMeta, HttpHeaders, NodeConfig
from elasticsearch import NotFoundError
from langchain.embeddings.cache import _value_serializer
from langchain_classic.embeddings.cache import _value_serializer
from langchain_core.load import dumps
from langchain_core.outputs import Generation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,12 @@ def test_doc_field_to_metadata(self) -> None:
assert actual == expected


@pytest.mark.filterwarnings(
"ignore::langchain_core._api.deprecation.LangChainPendingDeprecationWarning"
)
class TestConvertStrategy:
"""Test conversion of deprecated retrieval strategies to new ones."""

def test_dense_approx(self) -> None:
actual = _convert_retrieval_strategy(
ApproxRetrievalStrategy(query_model_id="my model", hybrid=True, rrf=False),
Expand Down
Loading