Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/scripts/check_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def _get_configs_for_single_dir(job: str, dir_: str) -> List[Dict[str, str]]:
if job == "codspeed":
py_versions = ["3.12"] # 3.13 is not yet supported
elif dir_ == "libs/core":
py_versions = ["3.10", "3.11", "3.12", "3.13"]
py_versions = ["3.10", "3.11", "3.12", "3.13", "3.14"]
# custom logic for specific directories

elif dir_ == "libs/langchain" and job == "extended-tests":
Expand Down
16 changes: 10 additions & 6 deletions libs/core/langchain_core/language_models/llms.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,17 @@ def _before_sleep(retry_state: RetryCallState) -> None:
if isinstance(run_manager, AsyncCallbackManagerForLLMRun):
coro = run_manager.on_retry(retry_state)
try:
loop = asyncio.get_event_loop()
if loop.is_running():
# TODO: Fix RUF006 - this task should have a reference
# and be awaited somewhere
loop.create_task(coro) # noqa: RUF006
else:
try:
loop = asyncio.get_event_loop()
except RuntimeError:
asyncio.run(coro)
else:
if loop.is_running():
# TODO: Fix RUF006 - this task should have a reference
# and be awaited somewhere
loop.create_task(coro) # noqa: RUF006
else:
asyncio.run(coro)
except Exception as e:
_log_error_once(f"Error in on_retry: {e}")
else:
Expand Down
6 changes: 2 additions & 4 deletions libs/core/langchain_core/runnables/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,8 +527,7 @@ def map(
self,
fn: Callable[..., T],
*iterables: Iterable[Any],
timeout: float | None = None,
chunksize: int = 1,
**kwargs: Any,
) -> Iterator[T]:
"""Map a function to multiple iterables.

Expand All @@ -549,8 +548,7 @@ def _wrapped_fn(*args: Any) -> T:
return super().map(
_wrapped_fn,
*iterables,
timeout=timeout,
chunksize=chunksize,
**kwargs,
)


Expand Down
6 changes: 3 additions & 3 deletions libs/core/langchain_core/runnables/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import ast
import asyncio
import inspect
import sys
import textwrap
from collections.abc import Callable, Mapping, Sequence
from contextvars import Context
Expand Down Expand Up @@ -118,14 +119,13 @@ def accepts_context(callable: Callable[..., Any]) -> bool: # noqa: A002
return False


@lru_cache(maxsize=1)
def asyncio_accepts_context() -> bool:
"""Cache the result of checking if asyncio.create_task accepts a `context` arg.
"""Check if asyncio.create_task accepts a `context` arg.

Returns:
True if `asyncio.create_task` accepts a context argument, `False` otherwise.
"""
return accepts_context(asyncio.create_task)
return sys.version_info >= (3, 11)


def coro_with_context(
Expand Down
5 changes: 4 additions & 1 deletion libs/core/langchain_core/tracers/event_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,10 @@ def __init__(
exclude_tags=exclude_tags,
)

loop = asyncio.get_event_loop()
try:
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
memory_stream = _MemoryStream[StreamEvent](loop)
self.send_stream = memory_stream.get_send_stream()
self.receive_stream = memory_stream.get_receive_stream()
Expand Down
5 changes: 4 additions & 1 deletion libs/core/langchain_core/tracers/log_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,10 @@ def __init__(
self.exclude_types = exclude_types
self.exclude_tags = exclude_tags

loop = asyncio.get_event_loop()
try:
loop = asyncio.get_event_loop()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

asyncio.get_event_loop raises an exception in Python 3.14 if there's no running loop (was a warning in previous versions)

except RuntimeError:
loop = asyncio.new_event_loop()
memory_stream = _MemoryStream[RunLogPatch](loop)
self.lock = threading.Lock()
self.send_stream = memory_stream.get_send_stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Test PydanticOutputParser."""

import sys
from enum import Enum
from typing import Literal

Expand All @@ -22,15 +23,23 @@ class ForecastV2(pydantic.BaseModel):
forecast: str


class ForecastV1(V1BaseModel):
temperature: int
f_or_c: Literal["F", "C"]
forecast: str
if sys.version_info < (3, 14):

class ForecastV1(V1BaseModel):
temperature: int
f_or_c: Literal["F", "C"]
forecast: str

_FORECAST_MODELS_TYPES = type[ForecastV2] | type[ForecastV1]
_FORECAST_MODELS = [ForecastV2, ForecastV1]
else:
_FORECAST_MODELS_TYPES = type[ForecastV2]
_FORECAST_MODELS = [ForecastV2]


@pytest.mark.parametrize("pydantic_object", [ForecastV2, ForecastV1])
@pytest.mark.parametrize("pydantic_object", _FORECAST_MODELS)
def test_pydantic_parser_chaining(
pydantic_object: type[ForecastV2] | type[ForecastV1],
pydantic_object: _FORECAST_MODELS_TYPES,
) -> None:
prompt = PromptTemplate(
template="""{{
Expand All @@ -53,7 +62,7 @@ def test_pydantic_parser_chaining(
assert res.forecast == "Sunny"


@pytest.mark.parametrize("pydantic_object", [ForecastV2, ForecastV1])
@pytest.mark.parametrize("pydantic_object", _FORECAST_MODELS)
def test_pydantic_parser_validation(pydantic_object: TBaseModel) -> None:
bad_prompt = PromptTemplate(
template="""{{
Expand All @@ -75,7 +84,7 @@ def test_pydantic_parser_validation(pydantic_object: TBaseModel) -> None:


# JSON output parser tests
@pytest.mark.parametrize("pydantic_object", [ForecastV2, ForecastV1])
@pytest.mark.parametrize("pydantic_object", _FORECAST_MODELS)
def test_json_parser_chaining(
pydantic_object: TBaseModel,
) -> None:
Expand Down
24 changes: 0 additions & 24 deletions libs/core/tests/unit_tests/prompts/__snapshots__/test_chat.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
dict({
'$defs': dict({
'AIMessage': dict({
'additionalProperties': True,
'description': '''
Message from an AI.

Expand Down Expand Up @@ -110,7 +109,6 @@
'type': 'object',
}),
'AIMessageChunk': dict({
'additionalProperties': True,
'description': 'Message chunk from an AI (yielded when streaming).',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -231,7 +229,6 @@
'type': 'object',
}),
'ChatMessage': dict({
'additionalProperties': True,
'description': 'Message that can be assigned an arbitrary speaker (i.e. role).',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -306,7 +303,6 @@
'type': 'object',
}),
'ChatMessageChunk': dict({
'additionalProperties': True,
'description': 'Chat Message chunk.',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -381,7 +377,6 @@
'type': 'object',
}),
'FunctionMessage': dict({
'additionalProperties': True,
'description': '''
Message for passing the result of executing a tool back to a model.

Expand Down Expand Up @@ -453,7 +448,6 @@
'type': 'object',
}),
'FunctionMessageChunk': dict({
'additionalProperties': True,
'description': 'Function Message chunk.',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -516,7 +510,6 @@
'type': 'object',
}),
'HumanMessage': dict({
'additionalProperties': True,
'description': '''
Message from the user.

Expand Down Expand Up @@ -604,7 +597,6 @@
'type': 'object',
}),
'HumanMessageChunk': dict({
'additionalProperties': True,
'description': 'Human Message chunk.',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -822,7 +814,6 @@
'type': 'object',
}),
'SystemMessage': dict({
'additionalProperties': True,
'description': '''
Message for priming AI behavior.

Expand Down Expand Up @@ -910,7 +901,6 @@
'type': 'object',
}),
'SystemMessageChunk': dict({
'additionalProperties': True,
'description': 'System Message chunk.',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -1105,7 +1095,6 @@
'type': 'object',
}),
'ToolMessage': dict({
'additionalProperties': True,
'description': '''
Message for passing the result of executing a tool back to a model.

Expand Down Expand Up @@ -1224,7 +1213,6 @@
'type': 'object',
}),
'ToolMessageChunk': dict({
'additionalProperties': True,
'description': 'Tool Message chunk.',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -1422,7 +1410,6 @@
dict({
'$defs': dict({
'AIMessage': dict({
'additionalProperties': True,
'description': '''
Message from an AI.

Expand Down Expand Up @@ -1529,7 +1516,6 @@
'type': 'object',
}),
'AIMessageChunk': dict({
'additionalProperties': True,
'description': 'Message chunk from an AI (yielded when streaming).',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -1650,7 +1636,6 @@
'type': 'object',
}),
'ChatMessage': dict({
'additionalProperties': True,
'description': 'Message that can be assigned an arbitrary speaker (i.e. role).',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -1725,7 +1710,6 @@
'type': 'object',
}),
'ChatMessageChunk': dict({
'additionalProperties': True,
'description': 'Chat Message chunk.',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -1800,7 +1784,6 @@
'type': 'object',
}),
'FunctionMessage': dict({
'additionalProperties': True,
'description': '''
Message for passing the result of executing a tool back to a model.

Expand Down Expand Up @@ -1872,7 +1855,6 @@
'type': 'object',
}),
'FunctionMessageChunk': dict({
'additionalProperties': True,
'description': 'Function Message chunk.',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -1935,7 +1917,6 @@
'type': 'object',
}),
'HumanMessage': dict({
'additionalProperties': True,
'description': '''
Message from the user.

Expand Down Expand Up @@ -2023,7 +2004,6 @@
'type': 'object',
}),
'HumanMessageChunk': dict({
'additionalProperties': True,
'description': 'Human Message chunk.',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -2241,7 +2221,6 @@
'type': 'object',
}),
'SystemMessage': dict({
'additionalProperties': True,
'description': '''
Message for priming AI behavior.

Expand Down Expand Up @@ -2329,7 +2308,6 @@
'type': 'object',
}),
'SystemMessageChunk': dict({
'additionalProperties': True,
'description': 'System Message chunk.',
'properties': dict({
'additional_kwargs': dict({
Expand Down Expand Up @@ -2524,7 +2502,6 @@
'type': 'object',
}),
'ToolMessage': dict({
'additionalProperties': True,
'description': '''
Message for passing the result of executing a tool back to a model.

Expand Down Expand Up @@ -2643,7 +2620,6 @@
'type': 'object',
}),
'ToolMessageChunk': dict({
'additionalProperties': True,
'description': 'Tool Message chunk.',
'properties': dict({
'additional_kwargs': dict({
Expand Down
Loading