mirror of
https://github.com/hwchase17/langchain.git
synced 2025-06-28 01:19:31 +00:00
core: Bump ruff version to 0.9 (#29201)
Also run some preview autofix and formatting --------- Co-authored-by: Erick Friis <erick@langchain.dev>
This commit is contained in:
parent
6f95db81b7
commit
e4a78dfc2a
@ -214,7 +214,7 @@ def beta(
|
|||||||
old_doc = inspect.cleandoc(old_doc or "").strip("\n") or ""
|
old_doc = inspect.cleandoc(old_doc or "").strip("\n") or ""
|
||||||
components = [message, addendum]
|
components = [message, addendum]
|
||||||
details = " ".join([component.strip() for component in components if component])
|
details = " ".join([component.strip() for component in components if component])
|
||||||
new_doc = f".. beta::\n" f" {details}\n\n" f"{old_doc}\n"
|
new_doc = f".. beta::\n {details}\n\n{old_doc}\n"
|
||||||
|
|
||||||
if inspect.iscoroutinefunction(obj):
|
if inspect.iscoroutinefunction(obj):
|
||||||
finalized = finalize(awarning_emitting_wrapper, new_doc)
|
finalized = finalize(awarning_emitting_wrapper, new_doc)
|
||||||
|
@ -238,6 +238,7 @@ def deprecated(
|
|||||||
exclude=obj.exclude,
|
exclude=obj.exclude,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
elif isinstance(obj, FieldInfoV2):
|
elif isinstance(obj, FieldInfoV2):
|
||||||
wrapped = None
|
wrapped = None
|
||||||
if not _obj_type:
|
if not _obj_type:
|
||||||
|
@ -385,8 +385,7 @@ async def _ahandle_event_for_handler(
|
|||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Error in {handler.__class__.__name__}.{event_name} callback:"
|
f"Error in {handler.__class__.__name__}.{event_name} callback: {repr(e)}"
|
||||||
f" {repr(e)}"
|
|
||||||
)
|
)
|
||||||
if handler.raise_error:
|
if handler.raise_error:
|
||||||
raise e
|
raise e
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import operator
|
||||||
import uuid
|
import uuid
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
from typing import Any, Optional, cast
|
from typing import Any, Optional, cast
|
||||||
@ -80,5 +81,5 @@ class InMemoryDocumentIndex(DocumentIndex):
|
|||||||
count = document.page_content.count(query)
|
count = document.page_content.count(query)
|
||||||
counts_by_doc.append((document, count))
|
counts_by_doc.append((document, count))
|
||||||
|
|
||||||
counts_by_doc.sort(key=lambda x: x[1], reverse=True)
|
counts_by_doc.sort(key=operator.itemgetter(1), reverse=True)
|
||||||
return [doc.model_copy() for doc, count in counts_by_doc[: self.top_k]]
|
return [doc.model_copy() for doc, count in counts_by_doc[: self.top_k]]
|
||||||
|
@ -390,7 +390,7 @@ class BaseLanguageModel(
|
|||||||
"Counting tokens in tool schemas is not yet supported. Ignoring tools.",
|
"Counting tokens in tool schemas is not yet supported. Ignoring tools.",
|
||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
return sum([self.get_num_tokens(get_buffer_string([m])) for m in messages])
|
return sum(self.get_num_tokens(get_buffer_string([m])) for m in messages)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _all_required_field_names(cls) -> set:
|
def _all_required_field_names(cls) -> set:
|
||||||
|
@ -347,8 +347,7 @@ class BaseLLM(BaseLanguageModel[str], ABC):
|
|||||||
"""Get standard params for tracing."""
|
"""Get standard params for tracing."""
|
||||||
# get default provider from class name
|
# get default provider from class name
|
||||||
default_provider = self.__class__.__name__
|
default_provider = self.__class__.__name__
|
||||||
if default_provider.endswith("LLM"):
|
default_provider = default_provider.removesuffix("LLM")
|
||||||
default_provider = default_provider[:-3]
|
|
||||||
default_provider = default_provider.lower()
|
default_provider = default_provider.lower()
|
||||||
|
|
||||||
ls_params = LangSmithParams(ls_provider=default_provider, ls_model_type="llm")
|
ls_params = LangSmithParams(ls_provider=default_provider, ls_model_type="llm")
|
||||||
|
@ -1008,7 +1008,10 @@ def convert_to_openai_messages(
|
|||||||
)
|
)
|
||||||
raise ValueError(err)
|
raise ValueError(err)
|
||||||
content.append(
|
content.append(
|
||||||
{"type": "image_url", "image_url": block["image_url"]}
|
{
|
||||||
|
"type": "image_url",
|
||||||
|
"image_url": block["image_url"],
|
||||||
|
}
|
||||||
)
|
)
|
||||||
# Anthropic and Bedrock converse format
|
# Anthropic and Bedrock converse format
|
||||||
elif (block.get("type") == "image") or "image" in block:
|
elif (block.get("type") == "image") or "image" in block:
|
||||||
@ -1127,7 +1130,10 @@ def convert_to_openai_messages(
|
|||||||
)
|
)
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
content.append(
|
content.append(
|
||||||
{"type": "text", "text": json.dumps(block["json"])}
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": json.dumps(block["json"]),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
elif (
|
elif (
|
||||||
block.get("type") == "guard_content"
|
block.get("type") == "guard_content"
|
||||||
|
@ -227,7 +227,7 @@ class MarkdownListOutputParser(ListOutputParser):
|
|||||||
|
|
||||||
def get_format_instructions(self) -> str:
|
def get_format_instructions(self) -> str:
|
||||||
"""Return the format instructions for the Markdown list output."""
|
"""Return the format instructions for the Markdown list output."""
|
||||||
return "Your response should be a markdown list, " "eg: `- foo\n- bar\n- baz`"
|
return "Your response should be a markdown list, eg: `- foo\n- bar\n- baz`"
|
||||||
|
|
||||||
def parse(self, text: str) -> list[str]:
|
def parse(self, text: str) -> list[str]:
|
||||||
"""Parse the output of an LLM call.
|
"""Parse the output of an LLM call.
|
||||||
|
@ -1398,9 +1398,7 @@ def _create_template_from_message_type(
|
|||||||
elif len(template) == 2 and isinstance(template[1], bool):
|
elif len(template) == 2 and isinstance(template[1], bool):
|
||||||
var_name_wrapped, is_optional = template
|
var_name_wrapped, is_optional = template
|
||||||
if not isinstance(var_name_wrapped, str):
|
if not isinstance(var_name_wrapped, str):
|
||||||
msg = (
|
msg = f"Expected variable name to be a string. Got: {var_name_wrapped}"
|
||||||
"Expected variable name to be a string." f" Got: {var_name_wrapped}"
|
|
||||||
)
|
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
if var_name_wrapped[0] != "{" or var_name_wrapped[-1] != "}":
|
if var_name_wrapped[0] != "{" or var_name_wrapped[-1] != "}":
|
||||||
msg = (
|
msg = (
|
||||||
|
@ -2,7 +2,7 @@ from importlib import metadata
|
|||||||
|
|
||||||
from langchain_core._api.deprecation import warn_deprecated
|
from langchain_core._api.deprecation import warn_deprecated
|
||||||
|
|
||||||
## Create namespaces for pydantic v1 and v2.
|
# Create namespaces for pydantic v1 and v2.
|
||||||
# This code must stay at the top of the file before other modules may
|
# This code must stay at the top of the file before other modules may
|
||||||
# attempt to import pydantic since it adds pydantic_v1 and pydantic_v2 to sys.modules.
|
# attempt to import pydantic since it adds pydantic_v1 and pydantic_v2 to sys.modules.
|
||||||
#
|
#
|
||||||
|
@ -248,14 +248,14 @@ class InMemoryRateLimiter(BaseRateLimiter):
|
|||||||
if not blocking:
|
if not blocking:
|
||||||
return self._consume()
|
return self._consume()
|
||||||
|
|
||||||
while not self._consume():
|
while not self._consume(): # noqa: ASYNC110
|
||||||
# This code ignores the ASYNC110 warning which is a false positive in this
|
# This code ignores the ASYNC110 warning which is a false positive in this
|
||||||
# case.
|
# case.
|
||||||
# There is no external actor that can mark that the Event is done
|
# There is no external actor that can mark that the Event is done
|
||||||
# since the tokens are managed by the rate limiter itself.
|
# since the tokens are managed by the rate limiter itself.
|
||||||
# It needs to wake up to re-fill the tokens.
|
# It needs to wake up to re-fill the tokens.
|
||||||
# https://docs.astral.sh/ruff/rules/async-busy-wait/
|
# https://docs.astral.sh/ruff/rules/async-busy-wait/
|
||||||
await asyncio.sleep(self.check_every_n_seconds) # ruff: noqa: ASYNC110
|
await asyncio.sleep(self.check_every_n_seconds)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -2862,7 +2862,7 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
|||||||
# calculate context dependencies
|
# calculate context dependencies
|
||||||
specs_by_pos = groupby(
|
specs_by_pos = groupby(
|
||||||
[tup for tup in all_specs if tup[0].id.startswith(CONTEXT_CONFIG_PREFIX)],
|
[tup for tup in all_specs if tup[0].id.startswith(CONTEXT_CONFIG_PREFIX)],
|
||||||
lambda x: x[1],
|
itemgetter(1),
|
||||||
)
|
)
|
||||||
next_deps: set[str] = set()
|
next_deps: set[str] = set()
|
||||||
deps_by_pos: dict[int, set[str]] = {}
|
deps_by_pos: dict[int, set[str]] = {}
|
||||||
@ -3006,7 +3006,7 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
|||||||
for i, step in enumerate(self.steps):
|
for i, step in enumerate(self.steps):
|
||||||
# mark each step as a child run
|
# mark each step as a child run
|
||||||
config = patch_config(
|
config = patch_config(
|
||||||
config, callbacks=run_manager.get_child(f"seq:step:{i+1}")
|
config, callbacks=run_manager.get_child(f"seq:step:{i + 1}")
|
||||||
)
|
)
|
||||||
context = copy_context()
|
context = copy_context()
|
||||||
context.run(_set_config_context, config)
|
context.run(_set_config_context, config)
|
||||||
@ -3046,7 +3046,7 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
|||||||
for i, step in enumerate(self.steps):
|
for i, step in enumerate(self.steps):
|
||||||
# mark each step as a child run
|
# mark each step as a child run
|
||||||
config = patch_config(
|
config = patch_config(
|
||||||
config, callbacks=run_manager.get_child(f"seq:step:{i+1}")
|
config, callbacks=run_manager.get_child(f"seq:step:{i + 1}")
|
||||||
)
|
)
|
||||||
context = copy_context()
|
context = copy_context()
|
||||||
context.run(_set_config_context, config)
|
context.run(_set_config_context, config)
|
||||||
@ -3131,7 +3131,8 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
|||||||
[
|
[
|
||||||
# each step a child run of the corresponding root run
|
# each step a child run of the corresponding root run
|
||||||
patch_config(
|
patch_config(
|
||||||
config, callbacks=rm.get_child(f"seq:step:{stepidx+1}")
|
config,
|
||||||
|
callbacks=rm.get_child(f"seq:step:{stepidx + 1}"),
|
||||||
)
|
)
|
||||||
for i, (rm, config) in enumerate(zip(run_managers, configs))
|
for i, (rm, config) in enumerate(zip(run_managers, configs))
|
||||||
if i not in failed_inputs_map
|
if i not in failed_inputs_map
|
||||||
@ -3163,7 +3164,7 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
|||||||
[
|
[
|
||||||
# each step a child run of the corresponding root run
|
# each step a child run of the corresponding root run
|
||||||
patch_config(
|
patch_config(
|
||||||
config, callbacks=rm.get_child(f"seq:step:{i+1}")
|
config, callbacks=rm.get_child(f"seq:step:{i + 1}")
|
||||||
)
|
)
|
||||||
for rm, config in zip(run_managers, configs)
|
for rm, config in zip(run_managers, configs)
|
||||||
],
|
],
|
||||||
@ -3260,7 +3261,8 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
|||||||
[
|
[
|
||||||
# each step a child run of the corresponding root run
|
# each step a child run of the corresponding root run
|
||||||
patch_config(
|
patch_config(
|
||||||
config, callbacks=rm.get_child(f"seq:step:{stepidx+1}")
|
config,
|
||||||
|
callbacks=rm.get_child(f"seq:step:{stepidx + 1}"),
|
||||||
)
|
)
|
||||||
for i, (rm, config) in enumerate(zip(run_managers, configs))
|
for i, (rm, config) in enumerate(zip(run_managers, configs))
|
||||||
if i not in failed_inputs_map
|
if i not in failed_inputs_map
|
||||||
@ -3292,7 +3294,7 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
|||||||
[
|
[
|
||||||
# each step a child run of the corresponding root run
|
# each step a child run of the corresponding root run
|
||||||
patch_config(
|
patch_config(
|
||||||
config, callbacks=rm.get_child(f"seq:step:{i+1}")
|
config, callbacks=rm.get_child(f"seq:step:{i + 1}")
|
||||||
)
|
)
|
||||||
for rm, config in zip(run_managers, configs)
|
for rm, config in zip(run_managers, configs)
|
||||||
],
|
],
|
||||||
@ -3339,7 +3341,7 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
|||||||
final_pipeline = cast(Iterator[Output], input)
|
final_pipeline = cast(Iterator[Output], input)
|
||||||
for idx, step in enumerate(steps):
|
for idx, step in enumerate(steps):
|
||||||
config = patch_config(
|
config = patch_config(
|
||||||
config, callbacks=run_manager.get_child(f"seq:step:{idx+1}")
|
config, callbacks=run_manager.get_child(f"seq:step:{idx + 1}")
|
||||||
)
|
)
|
||||||
if idx == 0:
|
if idx == 0:
|
||||||
final_pipeline = step.transform(final_pipeline, config, **kwargs)
|
final_pipeline = step.transform(final_pipeline, config, **kwargs)
|
||||||
@ -3368,7 +3370,7 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
|||||||
for idx, step in enumerate(steps):
|
for idx, step in enumerate(steps):
|
||||||
config = patch_config(
|
config = patch_config(
|
||||||
config,
|
config,
|
||||||
callbacks=run_manager.get_child(f"seq:step:{idx+1}"),
|
callbacks=run_manager.get_child(f"seq:step:{idx + 1}"),
|
||||||
)
|
)
|
||||||
if idx == 0:
|
if idx == 0:
|
||||||
final_pipeline = step.atransform(final_pipeline, config, **kwargs)
|
final_pipeline = step.atransform(final_pipeline, config, **kwargs)
|
||||||
@ -4400,7 +4402,7 @@ class RunnableLambda(Runnable[Input, Output]):
|
|||||||
if dict_keys := get_function_first_arg_dict_keys(func):
|
if dict_keys := get_function_first_arg_dict_keys(func):
|
||||||
return create_model_v2(
|
return create_model_v2(
|
||||||
self.get_name("Input"),
|
self.get_name("Input"),
|
||||||
field_definitions={key: (Any, ...) for key in dict_keys},
|
field_definitions=dict.fromkeys(dict_keys, (Any, ...)),
|
||||||
)
|
)
|
||||||
|
|
||||||
return super().get_input_schema(config)
|
return super().get_input_schema(config)
|
||||||
@ -4524,7 +4526,7 @@ class RunnableLambda(Runnable[Input, Output]):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""A string representation of this Runnable."""
|
"""A string representation of this Runnable."""
|
||||||
if hasattr(self, "func") and isinstance(self.func, itemgetter):
|
if hasattr(self, "func") and isinstance(self.func, itemgetter):
|
||||||
return f"RunnableLambda({str(self.func)[len('operator.'):]})"
|
return f"RunnableLambda({str(self.func)[len('operator.') :]})"
|
||||||
elif hasattr(self, "func"):
|
elif hasattr(self, "func"):
|
||||||
return f"RunnableLambda({get_lambda_source(self.func) or '...'})"
|
return f"RunnableLambda({get_lambda_source(self.func) or '...'})"
|
||||||
elif hasattr(self, "afunc"):
|
elif hasattr(self, "afunc"):
|
||||||
@ -4785,8 +4787,7 @@ class RunnableLambda(Runnable[Input, Output]):
|
|||||||
recursion_limit = config["recursion_limit"]
|
recursion_limit = config["recursion_limit"]
|
||||||
if recursion_limit <= 0:
|
if recursion_limit <= 0:
|
||||||
msg = (
|
msg = (
|
||||||
f"Recursion limit reached when invoking "
|
f"Recursion limit reached when invoking {self} with input {final}."
|
||||||
f"{self} with input {final}."
|
|
||||||
)
|
)
|
||||||
raise RecursionError(msg)
|
raise RecursionError(msg)
|
||||||
for chunk in output.stream(
|
for chunk in output.stream(
|
||||||
@ -4909,8 +4910,7 @@ class RunnableLambda(Runnable[Input, Output]):
|
|||||||
recursion_limit = config["recursion_limit"]
|
recursion_limit = config["recursion_limit"]
|
||||||
if recursion_limit <= 0:
|
if recursion_limit <= 0:
|
||||||
msg = (
|
msg = (
|
||||||
f"Recursion limit reached when invoking "
|
f"Recursion limit reached when invoking {self} with input {final}."
|
||||||
f"{self} with input {final}."
|
|
||||||
)
|
)
|
||||||
raise RecursionError(msg)
|
raise RecursionError(msg)
|
||||||
async for chunk in output.astream(
|
async for chunk in output.astream(
|
||||||
|
@ -110,8 +110,8 @@ COPIABLE_KEYS = [
|
|||||||
DEFAULT_RECURSION_LIMIT = 25
|
DEFAULT_RECURSION_LIMIT = 25
|
||||||
|
|
||||||
|
|
||||||
var_child_runnable_config = ContextVar(
|
var_child_runnable_config: ContextVar[RunnableConfig | None] = ContextVar(
|
||||||
"child_runnable_config", default=RunnableConfig()
|
"child_runnable_config", default=None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -242,17 +242,20 @@ def draw_ascii(vertices: Mapping[str, str], edges: Sequence[LangEdge]) -> str:
|
|||||||
"""
|
"""
|
||||||
# NOTE: coordinates might me negative, so we need to shift
|
# NOTE: coordinates might me negative, so we need to shift
|
||||||
# everything to the positive plane before we actually draw it.
|
# everything to the positive plane before we actually draw it.
|
||||||
xlist = []
|
xlist: list[float] = []
|
||||||
ylist = []
|
ylist: list[float] = []
|
||||||
|
|
||||||
sug = _build_sugiyama_layout(vertices, edges)
|
sug = _build_sugiyama_layout(vertices, edges)
|
||||||
|
|
||||||
for vertex in sug.g.sV:
|
for vertex in sug.g.sV:
|
||||||
# NOTE: moving boxes w/2 to the left
|
# NOTE: moving boxes w/2 to the left
|
||||||
xlist.append(vertex.view.xy[0] - vertex.view.w / 2.0)
|
xlist.extend(
|
||||||
xlist.append(vertex.view.xy[0] + vertex.view.w / 2.0)
|
(
|
||||||
ylist.append(vertex.view.xy[1])
|
vertex.view.xy[0] - vertex.view.w / 2.0,
|
||||||
ylist.append(vertex.view.xy[1] + vertex.view.h)
|
vertex.view.xy[0] + vertex.view.w / 2.0,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ylist.extend((vertex.view.xy[1], vertex.view.xy[1] + vertex.view.h))
|
||||||
|
|
||||||
for edge in sug.g.sE:
|
for edge in sug.g.sE:
|
||||||
for x, y in edge.view._pts:
|
for x, y in edge.view._pts:
|
||||||
|
@ -590,9 +590,7 @@ class RunnableWithMessageHistory(RunnableBindingBase):
|
|||||||
|
|
||||||
if missing_keys and parameter_names:
|
if missing_keys and parameter_names:
|
||||||
example_input = {self.input_messages_key: "foo"}
|
example_input = {self.input_messages_key: "foo"}
|
||||||
example_configurable = {
|
example_configurable = dict.fromkeys(missing_keys, "[your-value-here]")
|
||||||
missing_key: "[your-value-here]" for missing_key in missing_keys
|
|
||||||
}
|
|
||||||
example_config = {"configurable": example_configurable}
|
example_config = {"configurable": example_configurable}
|
||||||
msg = (
|
msg = (
|
||||||
f"Missing keys {sorted(missing_keys)} in config['configurable'] "
|
f"Missing keys {sorted(missing_keys)} in config['configurable'] "
|
||||||
|
@ -248,7 +248,7 @@ class RunnableRetry(RunnableBindingBase[Input, Output]):
|
|||||||
result = cast(list[Output], [e] * len(inputs))
|
result = cast(list[Output], [e] * len(inputs))
|
||||||
|
|
||||||
outputs: list[Union[Output, Exception]] = []
|
outputs: list[Union[Output, Exception]] = []
|
||||||
for idx, _ in enumerate(inputs):
|
for idx in range(len(inputs)):
|
||||||
if idx in results_map:
|
if idx in results_map:
|
||||||
outputs.append(results_map[idx])
|
outputs.append(results_map[idx])
|
||||||
else:
|
else:
|
||||||
@ -314,7 +314,7 @@ class RunnableRetry(RunnableBindingBase[Input, Output]):
|
|||||||
result = cast(list[Output], [e] * len(inputs))
|
result = cast(list[Output], [e] * len(inputs))
|
||||||
|
|
||||||
outputs: list[Union[Output, Exception]] = []
|
outputs: list[Union[Output, Exception]] = []
|
||||||
for idx, _ in enumerate(inputs):
|
for idx in range(len(inputs)):
|
||||||
if idx in results_map:
|
if idx in results_map:
|
||||||
outputs.append(results_map[idx])
|
outputs.append(results_map[idx])
|
||||||
else:
|
else:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import AsyncIterator, Iterator, Mapping
|
from collections.abc import AsyncIterator, Iterator, Mapping
|
||||||
|
from itertools import starmap
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Callable,
|
Callable,
|
||||||
@ -189,10 +190,7 @@ class RouterRunnable(RunnableSerializable[RouterInput, Output]):
|
|||||||
configs = get_config_list(config, len(inputs))
|
configs = get_config_list(config, len(inputs))
|
||||||
return await gather_with_concurrency(
|
return await gather_with_concurrency(
|
||||||
configs[0].get("max_concurrency"),
|
configs[0].get("max_concurrency"),
|
||||||
*(
|
*starmap(ainvoke, zip(runnables, actual_inputs, configs)),
|
||||||
ainvoke(runnable, input, config)
|
|
||||||
for runnable, input, config in zip(runnables, actual_inputs, configs)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def stream(
|
def stream(
|
||||||
|
@ -160,7 +160,7 @@ class FunctionCallbackHandler(BaseTracer):
|
|||||||
def _on_tool_start(self, run: Run) -> None:
|
def _on_tool_start(self, run: Run) -> None:
|
||||||
crumbs = self.get_breadcrumbs(run)
|
crumbs = self.get_breadcrumbs(run)
|
||||||
self.function_callback(
|
self.function_callback(
|
||||||
f'{get_colored_text("[tool/start]", color="green")} '
|
f"{get_colored_text('[tool/start]', color='green')} "
|
||||||
+ get_bolded_text(f"[{crumbs}] Entering Tool run with input:\n")
|
+ get_bolded_text(f"[{crumbs}] Entering Tool run with input:\n")
|
||||||
+ f'"{run.inputs["input"].strip()}"'
|
+ f'"{run.inputs["input"].strip()}"'
|
||||||
)
|
)
|
||||||
@ -169,7 +169,7 @@ class FunctionCallbackHandler(BaseTracer):
|
|||||||
crumbs = self.get_breadcrumbs(run)
|
crumbs = self.get_breadcrumbs(run)
|
||||||
if run.outputs:
|
if run.outputs:
|
||||||
self.function_callback(
|
self.function_callback(
|
||||||
f'{get_colored_text("[tool/end]", color="blue")} '
|
f"{get_colored_text('[tool/end]', color='blue')} "
|
||||||
+ get_bolded_text(
|
+ get_bolded_text(
|
||||||
f"[{crumbs}] [{elapsed(run)}] Exiting Tool run with output:\n"
|
f"[{crumbs}] [{elapsed(run)}] Exiting Tool run with output:\n"
|
||||||
)
|
)
|
||||||
|
@ -44,7 +44,7 @@ class StrictFormatter(Formatter):
|
|||||||
Raises:
|
Raises:
|
||||||
ValueError: If any input variables are not used in the format string.
|
ValueError: If any input variables are not used in the format string.
|
||||||
"""
|
"""
|
||||||
dummy_inputs = {input_variable: "foo" for input_variable in input_variables}
|
dummy_inputs = dict.fromkeys(input_variables, "foo")
|
||||||
super().format(format_string, **dummy_inputs)
|
super().format(format_string, **dummy_inputs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,8 +122,7 @@ def parse_tag(template: str, l_del: str, r_del: str) -> tuple[tuple[str, str], s
|
|||||||
ChevronError: If the tag is unclosed.
|
ChevronError: If the tag is unclosed.
|
||||||
ChevronError: If the set delimiter tag is unclosed.
|
ChevronError: If the set delimiter tag is unclosed.
|
||||||
"""
|
"""
|
||||||
global _CURRENT_LINE
|
global _CURRENT_LINE, _LAST_TAG_LINE
|
||||||
global _LAST_TAG_LINE
|
|
||||||
|
|
||||||
tag_types = {
|
tag_types = {
|
||||||
"!": "comment",
|
"!": "comment",
|
||||||
@ -140,7 +139,7 @@ def parse_tag(template: str, l_del: str, r_del: str) -> tuple[tuple[str, str], s
|
|||||||
try:
|
try:
|
||||||
tag, template = template.split(r_del, 1)
|
tag, template = template.split(r_del, 1)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
msg = "unclosed tag " f"at line {_CURRENT_LINE}"
|
msg = f"unclosed tag at line {_CURRENT_LINE}"
|
||||||
raise ChevronError(msg) from e
|
raise ChevronError(msg) from e
|
||||||
|
|
||||||
# Find the type meaning of the first character
|
# Find the type meaning of the first character
|
||||||
@ -161,7 +160,7 @@ def parse_tag(template: str, l_del: str, r_del: str) -> tuple[tuple[str, str], s
|
|||||||
|
|
||||||
# Otherwise we should complain
|
# Otherwise we should complain
|
||||||
else:
|
else:
|
||||||
msg = "unclosed set delimiter tag\n" f"at line {_CURRENT_LINE}"
|
msg = f"unclosed set delimiter tag\nat line {_CURRENT_LINE}"
|
||||||
raise ChevronError(msg)
|
raise ChevronError(msg)
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
|
@ -389,6 +389,7 @@ if PYDANTIC_MAJOR_VERSION == 2:
|
|||||||
else:
|
else:
|
||||||
msg = f"Expected a Pydantic model. Got {type(model)}"
|
msg = f"Expected a Pydantic model. Got {type(model)}"
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
|
|
||||||
elif PYDANTIC_MAJOR_VERSION == 1:
|
elif PYDANTIC_MAJOR_VERSION == 1:
|
||||||
from pydantic import BaseModel as BaseModelV1_
|
from pydantic import BaseModel as BaseModelV1_
|
||||||
|
|
||||||
@ -397,6 +398,7 @@ elif PYDANTIC_MAJOR_VERSION == 1:
|
|||||||
) -> dict[str, FieldInfoV1]:
|
) -> dict[str, FieldInfoV1]:
|
||||||
"""Get the field names of a Pydantic model."""
|
"""Get the field names of a Pydantic model."""
|
||||||
return model.__fields__ # type: ignore
|
return model.__fields__ # type: ignore
|
||||||
|
|
||||||
else:
|
else:
|
||||||
msg = f"Unsupported Pydantic version: {PYDANTIC_MAJOR_VERSION}"
|
msg = f"Unsupported Pydantic version: {PYDANTIC_MAJOR_VERSION}"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
@ -189,7 +189,7 @@ class InMemoryVectorStore(VectorStore):
|
|||||||
|
|
||||||
for doc, vector in zip(documents, vectors):
|
for doc, vector in zip(documents, vectors):
|
||||||
doc_id = next(id_iterator)
|
doc_id = next(id_iterator)
|
||||||
doc_id_ = doc_id if doc_id else str(uuid.uuid4())
|
doc_id_ = doc_id or str(uuid.uuid4())
|
||||||
ids_.append(doc_id_)
|
ids_.append(doc_id_)
|
||||||
self.store[doc_id_] = {
|
self.store[doc_id_] = {
|
||||||
"id": doc_id_,
|
"id": doc_id_,
|
||||||
@ -221,7 +221,7 @@ class InMemoryVectorStore(VectorStore):
|
|||||||
|
|
||||||
for doc, vector in zip(documents, vectors):
|
for doc, vector in zip(documents, vectors):
|
||||||
doc_id = next(id_iterator)
|
doc_id = next(id_iterator)
|
||||||
doc_id_ = doc_id if doc_id else str(uuid.uuid4())
|
doc_id_ = doc_id or str(uuid.uuid4())
|
||||||
ids_.append(doc_id_)
|
ids_.append(doc_id_)
|
||||||
self.store[doc_id_] = {
|
self.store[doc_id_] = {
|
||||||
"id": doc_id_,
|
"id": doc_id_,
|
||||||
@ -258,8 +258,7 @@ class InMemoryVectorStore(VectorStore):
|
|||||||
@deprecated(
|
@deprecated(
|
||||||
alternative="VectorStore.add_documents",
|
alternative="VectorStore.add_documents",
|
||||||
message=(
|
message=(
|
||||||
"This was a beta API that was added in 0.2.11. "
|
"This was a beta API that was added in 0.2.11. It'll be removed in 0.3.0."
|
||||||
"It'll be removed in 0.3.0."
|
|
||||||
),
|
),
|
||||||
since="0.2.29",
|
since="0.2.29",
|
||||||
removal="1.0",
|
removal="1.0",
|
||||||
@ -268,7 +267,7 @@ class InMemoryVectorStore(VectorStore):
|
|||||||
vectors = self.embedding.embed_documents([item.page_content for item in items])
|
vectors = self.embedding.embed_documents([item.page_content for item in items])
|
||||||
ids = []
|
ids = []
|
||||||
for item, vector in zip(items, vectors):
|
for item, vector in zip(items, vectors):
|
||||||
doc_id = item.id if item.id else str(uuid.uuid4())
|
doc_id = item.id or str(uuid.uuid4())
|
||||||
ids.append(doc_id)
|
ids.append(doc_id)
|
||||||
self.store[doc_id] = {
|
self.store[doc_id] = {
|
||||||
"id": doc_id,
|
"id": doc_id,
|
||||||
@ -284,8 +283,7 @@ class InMemoryVectorStore(VectorStore):
|
|||||||
@deprecated(
|
@deprecated(
|
||||||
alternative="VectorStore.aadd_documents",
|
alternative="VectorStore.aadd_documents",
|
||||||
message=(
|
message=(
|
||||||
"This was a beta API that was added in 0.2.11. "
|
"This was a beta API that was added in 0.2.11. It'll be removed in 0.3.0."
|
||||||
"It'll be removed in 0.3.0."
|
|
||||||
),
|
),
|
||||||
since="0.2.29",
|
since="0.2.29",
|
||||||
removal="1.0",
|
removal="1.0",
|
||||||
@ -298,7 +296,7 @@ class InMemoryVectorStore(VectorStore):
|
|||||||
)
|
)
|
||||||
ids = []
|
ids = []
|
||||||
for item, vector in zip(items, vectors):
|
for item, vector in zip(items, vectors):
|
||||||
doc_id = item.id if item.id else str(uuid.uuid4())
|
doc_id = item.id or str(uuid.uuid4())
|
||||||
ids.append(doc_id)
|
ids.append(doc_id)
|
||||||
self.store[doc_id] = {
|
self.store[doc_id] = {
|
||||||
"id": doc_id,
|
"id": doc_id,
|
||||||
|
42
libs/core/poetry.lock
generated
42
libs/core/poetry.lock
generated
@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "annotated-types"
|
name = "annotated-types"
|
||||||
@ -2654,29 +2654,29 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.5.7"
|
version = "0.9.2"
|
||||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a"},
|
{file = "ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347"},
|
||||||
{file = "ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be"},
|
{file = "ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00"},
|
||||||
{file = "ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e"},
|
{file = "ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4"},
|
||||||
{file = "ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8"},
|
{file = "ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d"},
|
||||||
{file = "ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea"},
|
{file = "ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c"},
|
||||||
{file = "ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc"},
|
{file = "ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f"},
|
||||||
{file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692"},
|
{file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684"},
|
||||||
{file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf"},
|
{file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d"},
|
||||||
{file = "ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb"},
|
{file = "ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df"},
|
||||||
{file = "ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e"},
|
{file = "ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247"},
|
||||||
{file = "ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499"},
|
{file = "ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e"},
|
||||||
{file = "ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e"},
|
{file = "ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe"},
|
||||||
{file = "ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5"},
|
{file = "ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb"},
|
||||||
{file = "ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e"},
|
{file = "ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a"},
|
||||||
{file = "ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a"},
|
{file = "ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145"},
|
||||||
{file = "ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3"},
|
{file = "ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5"},
|
||||||
{file = "ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4"},
|
{file = "ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6"},
|
||||||
{file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"},
|
{file = "ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3134,4 +3134,4 @@ type = ["pytest-mypy"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.9,<4.0"
|
python-versions = ">=3.9,<4.0"
|
||||||
content-hash = "9d0d2645af208f4f334c2e35f80f39bdc438546d3c66a7aec7e8a67f99c04d28"
|
content-hash = "2a262498da93ae3991e5eade787affab38f1ddef5212cb745eafdd0a40f0e986"
|
||||||
|
@ -82,7 +82,8 @@ classmethod-decorators = [ "classmethod", "langchain_core.utils.pydantic.pre_ini
|
|||||||
"scripts/**" = [ "S",]
|
"scripts/**" = [ "S",]
|
||||||
|
|
||||||
[tool.poetry.group.lint.dependencies]
|
[tool.poetry.group.lint.dependencies]
|
||||||
ruff = "^0.5"
|
ruff = "^0.9.2"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.typing.dependencies]
|
[tool.poetry.group.typing.dependencies]
|
||||||
@ -92,12 +93,14 @@ types-requests = "^2.28.11.5"
|
|||||||
types-jinja2 = "^2.11.9"
|
types-jinja2 = "^2.11.9"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
jupyter = "^1.0.0"
|
jupyter = "^1.0.0"
|
||||||
setuptools = "^67.6.1"
|
setuptools = "^67.6.1"
|
||||||
grandalf = "^0.8"
|
grandalf = "^0.8"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.test.dependencies]
|
[tool.poetry.group.test.dependencies]
|
||||||
pytest = "^8"
|
pytest = "^8"
|
||||||
freezegun = "^1.2.2"
|
freezegun = "^1.2.2"
|
||||||
@ -118,14 +121,17 @@ version = ">=1.26.0,<3"
|
|||||||
python = ">=3.12"
|
python = ">=3.12"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.test_integration.dependencies]
|
[tool.poetry.group.test_integration.dependencies]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.typing.dependencies.langchain-text-splitters]
|
[tool.poetry.group.typing.dependencies.langchain-text-splitters]
|
||||||
path = "../text-splitters"
|
path = "../text-splitters"
|
||||||
develop = true
|
develop = true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.test.dependencies.langchain-tests]
|
[tool.poetry.group.test.dependencies.langchain-tests]
|
||||||
path = "../standard-tests"
|
path = "../standard-tests"
|
||||||
develop = true
|
develop = true
|
||||||
|
@ -29,7 +29,10 @@ def test_add_message_implementation_only() -> None:
|
|||||||
assert store[1] == HumanMessage(content="World")
|
assert store[1] == HumanMessage(content="World")
|
||||||
|
|
||||||
chat_history.add_messages(
|
chat_history.add_messages(
|
||||||
[HumanMessage(content="Hello"), HumanMessage(content="World")]
|
[
|
||||||
|
HumanMessage(content="Hello"),
|
||||||
|
HumanMessage(content="World"),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
assert len(store) == 4
|
assert len(store) == 4
|
||||||
assert store[2] == HumanMessage(content="Hello")
|
assert store[2] == HumanMessage(content="Hello")
|
||||||
@ -61,7 +64,10 @@ def test_bulk_message_implementation_only() -> None:
|
|||||||
assert store[1] == HumanMessage(content="World")
|
assert store[1] == HumanMessage(content="World")
|
||||||
|
|
||||||
chat_history.add_messages(
|
chat_history.add_messages(
|
||||||
[HumanMessage(content="Hello"), HumanMessage(content="World")]
|
[
|
||||||
|
HumanMessage(content="Hello"),
|
||||||
|
HumanMessage(content="World"),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
assert len(store) == 4
|
assert len(store) == 4
|
||||||
assert store[2] == HumanMessage(content="Hello")
|
assert store[2] == HumanMessage(content="Hello")
|
||||||
@ -85,7 +91,10 @@ async def test_async_interface() -> None:
|
|||||||
|
|
||||||
chat_history = BulkAddHistory()
|
chat_history = BulkAddHistory()
|
||||||
await chat_history.aadd_messages(
|
await chat_history.aadd_messages(
|
||||||
[HumanMessage(content="Hello"), HumanMessage(content="World")]
|
[
|
||||||
|
HumanMessage(content="Hello"),
|
||||||
|
HumanMessage(content="World"),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
assert await chat_history.aget_messages() == [
|
assert await chat_history.aget_messages() == [
|
||||||
HumanMessage(content="Hello"),
|
HumanMessage(content="Hello"),
|
||||||
|
@ -11,5 +11,8 @@ def test_deterministic_fake_embeddings() -> None:
|
|||||||
assert fake.embed_query(text) != fake.embed_query("Goodbye world!")
|
assert fake.embed_query(text) != fake.embed_query("Goodbye world!")
|
||||||
assert fake.embed_documents([text, text]) == fake.embed_documents([text, text])
|
assert fake.embed_documents([text, text]) == fake.embed_documents([text, text])
|
||||||
assert fake.embed_documents([text, text]) != fake.embed_documents(
|
assert fake.embed_documents([text, text]) != fake.embed_documents(
|
||||||
[text, "Goodbye world!"]
|
[
|
||||||
|
text,
|
||||||
|
"Goodbye world!",
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
@ -227,7 +227,7 @@ def test_global_cache_batch() -> None:
|
|||||||
assert results[0].content == results[1].content
|
assert results[0].content == results[1].content
|
||||||
assert {results[0].content, results[1].content}.issubset({"hello", "goodbye"})
|
assert {results[0].content, results[1].content}.issubset({"hello", "goodbye"})
|
||||||
|
|
||||||
## RACE CONDITION -- note behavior is different from async
|
# RACE CONDITION -- note behavior is different from async
|
||||||
# Now, reset cache and test the race condition
|
# Now, reset cache and test the race condition
|
||||||
# For now we just hard-code the result, if this changes
|
# For now we just hard-code the result, if this changes
|
||||||
# we can investigate further
|
# we can investigate further
|
||||||
|
@ -663,7 +663,7 @@ def create_image_data() -> str:
|
|||||||
|
|
||||||
def create_base64_image(format: str = "jpeg") -> str:
|
def create_base64_image(format: str = "jpeg") -> str:
|
||||||
data = create_image_data()
|
data = create_image_data()
|
||||||
return f"data:image/{format};base64,{data}" # noqa: E501
|
return f"data:image/{format};base64,{data}"
|
||||||
|
|
||||||
|
|
||||||
def test_convert_to_openai_messages_single_message() -> None:
|
def test_convert_to_openai_messages_single_message() -> None:
|
||||||
|
@ -613,6 +613,6 @@ def test_unicode_handling() -> None:
|
|||||||
|
|
||||||
parser = SimpleJsonOutputParser(pydantic_object=Sample)
|
parser = SimpleJsonOutputParser(pydantic_object=Sample)
|
||||||
format_instructions = parser.get_format_instructions()
|
format_instructions = parser.get_format_instructions()
|
||||||
assert (
|
assert "科学文章的标题" in format_instructions, (
|
||||||
"科学文章的标题" in format_instructions
|
"Unicode characters should not be escaped"
|
||||||
), "Unicode characters should not be escaped"
|
)
|
||||||
|
@ -123,7 +123,8 @@ def test_numbered_list() -> None:
|
|||||||
def test_markdown_list() -> None:
|
def test_markdown_list() -> None:
|
||||||
parser = MarkdownListOutputParser()
|
parser = MarkdownListOutputParser()
|
||||||
text1 = (
|
text1 = (
|
||||||
"Your response should be a numbered - not a list item - list with each item on a new line." # noqa: E501
|
"Your response should be a numbered - not a list item - "
|
||||||
|
"list with each item on a new line."
|
||||||
"For example: \n- foo\n- bar\n- baz"
|
"For example: \n- foo\n- bar\n- baz"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -123,9 +123,7 @@ def test_create_system_message_prompt_template_from_template_partial() -> None:
|
|||||||
partial_variables={"instructions": json_prompt_instructions},
|
partial_variables={"instructions": json_prompt_instructions},
|
||||||
)
|
)
|
||||||
assert graph_analyst_template.format(history="history") == SystemMessage(
|
assert graph_analyst_template.format(history="history") == SystemMessage(
|
||||||
content="\n Your instructions are:\n "
|
content="\n Your instructions are:\n {}\n History:\n history\n "
|
||||||
" {}\n History:\n "
|
|
||||||
"history\n "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -234,7 +232,11 @@ def test_chat_prompt_template_from_messages(
|
|||||||
"""Test creating a chat prompt template from messages."""
|
"""Test creating a chat prompt template from messages."""
|
||||||
chat_prompt_template = ChatPromptTemplate.from_messages(messages)
|
chat_prompt_template = ChatPromptTemplate.from_messages(messages)
|
||||||
assert sorted(chat_prompt_template.input_variables) == sorted(
|
assert sorted(chat_prompt_template.input_variables) == sorted(
|
||||||
["context", "foo", "bar"]
|
[
|
||||||
|
"context",
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
]
|
||||||
)
|
)
|
||||||
assert len(chat_prompt_template.messages) == 4
|
assert len(chat_prompt_template.messages) == 4
|
||||||
|
|
||||||
@ -377,7 +379,11 @@ def test_chat_prompt_template_with_messages(
|
|||||||
messages + [HumanMessage(content="foo")]
|
messages + [HumanMessage(content="foo")]
|
||||||
)
|
)
|
||||||
assert sorted(chat_prompt_template.input_variables) == sorted(
|
assert sorted(chat_prompt_template.input_variables) == sorted(
|
||||||
["context", "foo", "bar"]
|
[
|
||||||
|
"context",
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
]
|
||||||
)
|
)
|
||||||
assert len(chat_prompt_template.messages) == 5
|
assert len(chat_prompt_template.messages) == 5
|
||||||
prompt_value = chat_prompt_template.format_prompt(
|
prompt_value = chat_prompt_template.format_prompt(
|
||||||
@ -835,7 +841,10 @@ async def test_messages_prompt_accepts_list() -> None:
|
|||||||
|
|
||||||
# Assert still raises a nice error
|
# Assert still raises a nice error
|
||||||
prompt = ChatPromptTemplate(
|
prompt = ChatPromptTemplate(
|
||||||
[("system", "You are a {foo}"), MessagesPlaceholder("history")]
|
[
|
||||||
|
("system", "You are a {foo}"),
|
||||||
|
MessagesPlaceholder("history"),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
prompt.invoke([("user", "Hi there")]) # type: ignore
|
prompt.invoke([("user", "Hi there")]) # type: ignore
|
||||||
@ -872,7 +881,11 @@ def test_chat_input_schema(snapshot: SnapshotAssertion) -> None:
|
|||||||
|
|
||||||
def test_chat_prompt_w_msgs_placeholder_ser_des(snapshot: SnapshotAssertion) -> None:
|
def test_chat_prompt_w_msgs_placeholder_ser_des(snapshot: SnapshotAssertion) -> None:
|
||||||
prompt = ChatPromptTemplate.from_messages(
|
prompt = ChatPromptTemplate.from_messages(
|
||||||
[("system", "foo"), MessagesPlaceholder("bar"), ("human", "baz")]
|
[
|
||||||
|
("system", "foo"),
|
||||||
|
MessagesPlaceholder("bar"),
|
||||||
|
("human", "baz"),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
assert dumpd(MessagesPlaceholder("bar")) == snapshot(name="placeholder")
|
assert dumpd(MessagesPlaceholder("bar")) == snapshot(name="placeholder")
|
||||||
assert load(dumpd(MessagesPlaceholder("bar"))) == MessagesPlaceholder("bar")
|
assert load(dumpd(MessagesPlaceholder("bar"))) == MessagesPlaceholder("bar")
|
||||||
|
@ -243,7 +243,7 @@ def test_prompt_jinja2_functionality(
|
|||||||
)
|
)
|
||||||
output = prompt.format(foo="hello", bar="bye")
|
output = prompt.format(foo="hello", bar="bye")
|
||||||
expected_output = (
|
expected_output = (
|
||||||
"Starting with hello\n\n" "happy: sad\n\n" "tall: short\n\n" "Ending with bye"
|
"Starting with hello\n\nhappy: sad\n\ntall: short\n\nEnding with bye"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert output == expected_output
|
assert output == expected_output
|
||||||
|
@ -49,12 +49,12 @@ def test_ensure_config() -> None:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
config = ctx.run(ensure_config, cast(RunnableConfig, arg))
|
config = ctx.run(ensure_config, cast(RunnableConfig, arg))
|
||||||
assert (
|
assert len(arg["callbacks"]) == 1, (
|
||||||
len(arg["callbacks"]) == 1
|
"ensure_config should not modify the original config"
|
||||||
), "ensure_config should not modify the original config"
|
)
|
||||||
assert (
|
assert json.dumps({**arg, "callbacks": []}) == arg_str, (
|
||||||
json.dumps({**arg, "callbacks": []}) == arg_str
|
"ensure_config should not modify the original config"
|
||||||
), "ensure_config should not modify the original config"
|
)
|
||||||
assert config is not arg
|
assert config is not arg
|
||||||
assert config["callbacks"] is not arg["callbacks"]
|
assert config["callbacks"] is not arg["callbacks"]
|
||||||
assert config["metadata"] is not arg["metadata"]
|
assert config["metadata"] is not arg["metadata"]
|
||||||
|
@ -215,7 +215,11 @@ async def test_abatch() -> None:
|
|||||||
)
|
)
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
await runnable_with_single.abatch(
|
await runnable_with_single.abatch(
|
||||||
[{"text": "foo"}, {"text": "bar"}, {"text": "baz"}]
|
[
|
||||||
|
{"text": "foo"},
|
||||||
|
{"text": "bar"},
|
||||||
|
{"text": "baz"},
|
||||||
|
]
|
||||||
)
|
)
|
||||||
actual = await runnable_with_single.abatch(
|
actual = await runnable_with_single.abatch(
|
||||||
[{"text": "foo"}, {"text": "bar"}, {"text": "baz"}], return_exceptions=True
|
[{"text": "foo"}, {"text": "bar"}, {"text": "baz"}], return_exceptions=True
|
||||||
|
@ -622,19 +622,17 @@ def test_lambda_schemas(snapshot: SnapshotAssertion) -> None:
|
|||||||
"byebye": input["yo"],
|
"byebye": input["yo"],
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (
|
assert _normalize_schema(
|
||||||
_normalize_schema(
|
|
||||||
RunnableLambda(
|
RunnableLambda(
|
||||||
aget_values_typed # type: ignore[arg-type]
|
aget_values_typed # type: ignore[arg-type]
|
||||||
).get_input_jsonschema()
|
).get_input_jsonschema()
|
||||||
)
|
) == _normalize_schema(
|
||||||
== _normalize_schema(
|
|
||||||
{
|
{
|
||||||
"$defs": {
|
"$defs": {
|
||||||
"InputType": {
|
"InputType": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"variable_name": {
|
"variable_name": {
|
||||||
"title": "Variable " "Name",
|
"title": "Variable Name",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"yo": {"title": "Yo", "type": "integer"},
|
"yo": {"title": "Yo", "type": "integer"},
|
||||||
@ -648,7 +646,6 @@ def test_lambda_schemas(snapshot: SnapshotAssertion) -> None:
|
|||||||
"title": "aget_values_typed_input",
|
"title": "aget_values_typed_input",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
if PYDANTIC_VERSION >= (2, 9):
|
if PYDANTIC_VERSION >= (2, 9):
|
||||||
assert _normalize_schema(
|
assert _normalize_schema(
|
||||||
@ -2793,7 +2790,10 @@ async def test_router_runnable(
|
|||||||
assert result == "4"
|
assert result == "4"
|
||||||
|
|
||||||
result2 = chain.batch(
|
result2 = chain.batch(
|
||||||
[{"key": "math", "question": "2 + 2"}, {"key": "english", "question": "2 + 2"}]
|
[
|
||||||
|
{"key": "math", "question": "2 + 2"},
|
||||||
|
{"key": "english", "question": "2 + 2"},
|
||||||
|
]
|
||||||
)
|
)
|
||||||
assert result2 == ["4", "2"]
|
assert result2 == ["4", "2"]
|
||||||
|
|
||||||
@ -2801,7 +2801,10 @@ async def test_router_runnable(
|
|||||||
assert result == "4"
|
assert result == "4"
|
||||||
|
|
||||||
result2 = await chain.abatch(
|
result2 = await chain.abatch(
|
||||||
[{"key": "math", "question": "2 + 2"}, {"key": "english", "question": "2 + 2"}]
|
[
|
||||||
|
{"key": "math", "question": "2 + 2"},
|
||||||
|
{"key": "english", "question": "2 + 2"},
|
||||||
|
]
|
||||||
)
|
)
|
||||||
assert result2 == ["4", "2"]
|
assert result2 == ["4", "2"]
|
||||||
|
|
||||||
@ -2855,7 +2858,10 @@ async def test_higher_order_lambda_runnable(
|
|||||||
assert result == "4"
|
assert result == "4"
|
||||||
|
|
||||||
result2 = chain.batch(
|
result2 = chain.batch(
|
||||||
[{"key": "math", "question": "2 + 2"}, {"key": "english", "question": "2 + 2"}]
|
[
|
||||||
|
{"key": "math", "question": "2 + 2"},
|
||||||
|
{"key": "english", "question": "2 + 2"},
|
||||||
|
]
|
||||||
)
|
)
|
||||||
assert result2 == ["4", "2"]
|
assert result2 == ["4", "2"]
|
||||||
|
|
||||||
@ -2863,7 +2869,10 @@ async def test_higher_order_lambda_runnable(
|
|||||||
assert result == "4"
|
assert result == "4"
|
||||||
|
|
||||||
result2 = await chain.abatch(
|
result2 = await chain.abatch(
|
||||||
[{"key": "math", "question": "2 + 2"}, {"key": "english", "question": "2 + 2"}]
|
[
|
||||||
|
{"key": "math", "question": "2 + 2"},
|
||||||
|
{"key": "english", "question": "2 + 2"},
|
||||||
|
]
|
||||||
)
|
)
|
||||||
assert result2 == ["4", "2"]
|
assert result2 == ["4", "2"]
|
||||||
|
|
||||||
@ -3058,7 +3067,10 @@ def test_map_stream() -> None:
|
|||||||
assert len(streamed_chunks) == len(llm_res)
|
assert len(streamed_chunks) == len(llm_res)
|
||||||
|
|
||||||
chain_pick_two = chain.assign(hello=RunnablePick("llm").pipe(llm)).pick(
|
chain_pick_two = chain.assign(hello=RunnablePick("llm").pipe(llm)).pick(
|
||||||
["llm", "hello"]
|
[
|
||||||
|
"llm",
|
||||||
|
"hello",
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
assert chain_pick_two.get_output_jsonschema() == {
|
assert chain_pick_two.get_output_jsonschema() == {
|
||||||
@ -5441,7 +5453,11 @@ def test_schema_for_prompt_and_chat_model() -> None:
|
|||||||
chain = prompt | chat
|
chain = prompt | chat
|
||||||
assert (
|
assert (
|
||||||
chain.invoke(
|
chain.invoke(
|
||||||
{"model_json_schema": "hello", "_private": "goodbye", "json": "json"}
|
{
|
||||||
|
"model_json_schema": "hello",
|
||||||
|
"_private": "goodbye",
|
||||||
|
"json": "json",
|
||||||
|
}
|
||||||
).content
|
).content
|
||||||
== chat_res
|
== chat_res
|
||||||
)
|
)
|
||||||
|
@ -795,7 +795,10 @@ async def test_astream_events_from_model() -> None:
|
|||||||
async def test_event_stream_with_simple_chain() -> None:
|
async def test_event_stream_with_simple_chain() -> None:
|
||||||
"""Test as event stream."""
|
"""Test as event stream."""
|
||||||
template = ChatPromptTemplate.from_messages(
|
template = ChatPromptTemplate.from_messages(
|
||||||
[("system", "You are Cat Agent 007"), ("human", "{question}")]
|
[
|
||||||
|
("system", "You are Cat Agent 007"),
|
||||||
|
("human", "{question}"),
|
||||||
|
]
|
||||||
).with_config({"run_name": "my_template", "tags": ["my_template"]})
|
).with_config({"run_name": "my_template", "tags": ["my_template"]})
|
||||||
|
|
||||||
infinite_cycle = cycle(
|
infinite_cycle = cycle(
|
||||||
@ -1681,7 +1684,10 @@ async def test_event_stream_with_retry() -> None:
|
|||||||
async def test_with_llm() -> None:
|
async def test_with_llm() -> None:
|
||||||
"""Test with regular llm."""
|
"""Test with regular llm."""
|
||||||
prompt = ChatPromptTemplate.from_messages(
|
prompt = ChatPromptTemplate.from_messages(
|
||||||
[("system", "You are Cat Agent 007"), ("human", "{question}")]
|
[
|
||||||
|
("system", "You are Cat Agent 007"),
|
||||||
|
("human", "{question}"),
|
||||||
|
]
|
||||||
).with_config({"run_name": "my_template", "tags": ["my_template"]})
|
).with_config({"run_name": "my_template", "tags": ["my_template"]})
|
||||||
llm = FakeStreamingListLLM(responses=["abc"])
|
llm = FakeStreamingListLLM(responses=["abc"])
|
||||||
|
|
||||||
@ -1730,7 +1736,7 @@ async def test_with_llm() -> None:
|
|||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"input": {
|
"input": {
|
||||||
"prompts": ["System: You are Cat Agent 007\n" "Human: hello"]
|
"prompts": ["System: You are Cat Agent 007\nHuman: hello"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"event": "on_llm_start",
|
"event": "on_llm_start",
|
||||||
@ -1743,7 +1749,7 @@ async def test_with_llm() -> None:
|
|||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"input": {
|
"input": {
|
||||||
"prompts": ["System: You are Cat Agent 007\n" "Human: hello"]
|
"prompts": ["System: You are Cat Agent 007\nHuman: hello"]
|
||||||
},
|
},
|
||||||
"output": {
|
"output": {
|
||||||
"generations": [
|
"generations": [
|
||||||
@ -1918,7 +1924,10 @@ async def test_runnable_with_message_history() -> None:
|
|||||||
return InMemoryHistory(messages=store[session_id])
|
return InMemoryHistory(messages=store[session_id])
|
||||||
|
|
||||||
infinite_cycle = cycle(
|
infinite_cycle = cycle(
|
||||||
[AIMessage(content="hello", id="ai3"), AIMessage(content="world", id="ai4")]
|
[
|
||||||
|
AIMessage(content="hello", id="ai3"),
|
||||||
|
AIMessage(content="world", id="ai4"),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
prompt = ChatPromptTemplate.from_messages(
|
prompt = ChatPromptTemplate.from_messages(
|
||||||
|
@ -55,12 +55,12 @@ def _with_nulled_run_id(events: Sequence[StreamEvent]) -> list[StreamEvent]:
|
|||||||
for event in events:
|
for event in events:
|
||||||
assert "run_id" in event, f"Event {event} does not have a run_id."
|
assert "run_id" in event, f"Event {event} does not have a run_id."
|
||||||
assert "parent_ids" in event, f"Event {event} does not have parent_ids."
|
assert "parent_ids" in event, f"Event {event} does not have parent_ids."
|
||||||
assert isinstance(
|
assert isinstance(event["run_id"], str), (
|
||||||
event["run_id"], str
|
f"Event {event} run_id is not a string."
|
||||||
), f"Event {event} run_id is not a string."
|
)
|
||||||
assert isinstance(
|
assert isinstance(event["parent_ids"], list), (
|
||||||
event["parent_ids"], list
|
f"Event {event} parent_ids is not a list."
|
||||||
), f"Event {event} parent_ids is not a list."
|
)
|
||||||
|
|
||||||
return cast(
|
return cast(
|
||||||
list[StreamEvent],
|
list[StreamEvent],
|
||||||
@ -828,7 +828,10 @@ async def test_astream_with_model_in_chain() -> None:
|
|||||||
async def test_event_stream_with_simple_chain() -> None:
|
async def test_event_stream_with_simple_chain() -> None:
|
||||||
"""Test as event stream."""
|
"""Test as event stream."""
|
||||||
template = ChatPromptTemplate.from_messages(
|
template = ChatPromptTemplate.from_messages(
|
||||||
[("system", "You are Cat Agent 007"), ("human", "{question}")]
|
[
|
||||||
|
("system", "You are Cat Agent 007"),
|
||||||
|
("human", "{question}"),
|
||||||
|
]
|
||||||
).with_config({"run_name": "my_template", "tags": ["my_template"]})
|
).with_config({"run_name": "my_template", "tags": ["my_template"]})
|
||||||
|
|
||||||
infinite_cycle = cycle(
|
infinite_cycle = cycle(
|
||||||
@ -1628,7 +1631,10 @@ async def test_event_stream_with_retry() -> None:
|
|||||||
async def test_with_llm() -> None:
|
async def test_with_llm() -> None:
|
||||||
"""Test with regular llm."""
|
"""Test with regular llm."""
|
||||||
prompt = ChatPromptTemplate.from_messages(
|
prompt = ChatPromptTemplate.from_messages(
|
||||||
[("system", "You are Cat Agent 007"), ("human", "{question}")]
|
[
|
||||||
|
("system", "You are Cat Agent 007"),
|
||||||
|
("human", "{question}"),
|
||||||
|
]
|
||||||
).with_config({"run_name": "my_template", "tags": ["my_template"]})
|
).with_config({"run_name": "my_template", "tags": ["my_template"]})
|
||||||
llm = FakeStreamingListLLM(responses=["abc"])
|
llm = FakeStreamingListLLM(responses=["abc"])
|
||||||
|
|
||||||
@ -1677,7 +1683,7 @@ async def test_with_llm() -> None:
|
|||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"input": {
|
"input": {
|
||||||
"prompts": ["System: You are Cat Agent 007\n" "Human: hello"]
|
"prompts": ["System: You are Cat Agent 007\nHuman: hello"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"event": "on_llm_start",
|
"event": "on_llm_start",
|
||||||
@ -1690,7 +1696,7 @@ async def test_with_llm() -> None:
|
|||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"input": {
|
"input": {
|
||||||
"prompts": ["System: You are Cat Agent 007\n" "Human: hello"]
|
"prompts": ["System: You are Cat Agent 007\nHuman: hello"]
|
||||||
},
|
},
|
||||||
"output": {
|
"output": {
|
||||||
"generations": [
|
"generations": [
|
||||||
@ -1865,7 +1871,10 @@ async def test_runnable_with_message_history() -> None:
|
|||||||
return InMemoryHistory(messages=store[session_id])
|
return InMemoryHistory(messages=store[session_id])
|
||||||
|
|
||||||
infinite_cycle = cycle(
|
infinite_cycle = cycle(
|
||||||
[AIMessage(content="hello", id="ai3"), AIMessage(content="world", id="ai4")]
|
[
|
||||||
|
AIMessage(content="hello", id="ai3"),
|
||||||
|
AIMessage(content="world", id="ai4"),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
prompt = ChatPromptTemplate.from_messages(
|
prompt = ChatPromptTemplate.from_messages(
|
||||||
@ -2372,7 +2381,10 @@ async def test_with_explicit_config() -> None:
|
|||||||
return x
|
return x
|
||||||
|
|
||||||
chain = passthrough_to_trigger_issue | model.with_config(
|
chain = passthrough_to_trigger_issue | model.with_config(
|
||||||
{"tags": ["hello"], "callbacks": callbacks}
|
{
|
||||||
|
"tags": ["hello"],
|
||||||
|
"callbacks": callbacks,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return await chain.ainvoke(query)
|
return await chain.ainvoke(query)
|
||||||
|
@ -333,7 +333,7 @@ async def test_runnable_sequence_parallel_trace_nesting(method: str) -> None:
|
|||||||
"other_thing": "RunnableParallel<chain_result,other_thing>",
|
"other_thing": "RunnableParallel<chain_result,other_thing>",
|
||||||
"after": "RunnableSequence",
|
"after": "RunnableSequence",
|
||||||
}
|
}
|
||||||
assert len(posts) == sum([1 if isinstance(n, str) else len(n) for n in name_order])
|
assert len(posts) == sum(1 if isinstance(n, str) else len(n) for n in name_order)
|
||||||
prev_dotted_order = None
|
prev_dotted_order = None
|
||||||
dotted_order_map = {}
|
dotted_order_map = {}
|
||||||
id_map = {}
|
id_map = {}
|
||||||
@ -360,9 +360,9 @@ async def test_runnable_sequence_parallel_trace_nesting(method: str) -> None:
|
|||||||
if prev_dotted_order is not None and not str(
|
if prev_dotted_order is not None and not str(
|
||||||
expected_parents[name]
|
expected_parents[name]
|
||||||
).startswith("RunnableParallel"):
|
).startswith("RunnableParallel"):
|
||||||
assert (
|
assert dotted_order > prev_dotted_order, (
|
||||||
dotted_order > prev_dotted_order
|
f"{name} not after {name_order[i - 1]}"
|
||||||
), f"{name} not after {name_order[i-1]}"
|
)
|
||||||
prev_dotted_order = dotted_order
|
prev_dotted_order = dotted_order
|
||||||
if name in dotted_order_map:
|
if name in dotted_order_map:
|
||||||
msg = f"Duplicate name {name}"
|
msg = f"Duplicate name {name}"
|
||||||
@ -377,9 +377,9 @@ async def test_runnable_sequence_parallel_trace_nesting(method: str) -> None:
|
|||||||
dotted_order = dotted_order_map[name]
|
dotted_order = dotted_order_map[name]
|
||||||
if parent_ is not None:
|
if parent_ is not None:
|
||||||
parent_dotted_order = dotted_order_map[parent_]
|
parent_dotted_order = dotted_order_map[parent_]
|
||||||
assert dotted_order.startswith(
|
assert dotted_order.startswith(parent_dotted_order), (
|
||||||
parent_dotted_order
|
f"{name}, {parent_dotted_order} not in {dotted_order}"
|
||||||
), f"{name}, {parent_dotted_order} not in {dotted_order}"
|
)
|
||||||
assert str(parent_id_map[name]) == str(id_map[parent_])
|
assert str(parent_id_map[name]) == str(id_map[parent_])
|
||||||
else:
|
else:
|
||||||
assert dotted_order.split(".")[0] == dotted_order
|
assert dotted_order.split(".")[0] == dotted_order
|
||||||
|
@ -47,39 +47,36 @@ def test_message_init() -> None:
|
|||||||
def test_message_chunks() -> None:
|
def test_message_chunks() -> None:
|
||||||
assert AIMessageChunk(content="I am", id="ai3") + AIMessageChunk(
|
assert AIMessageChunk(content="I am", id="ai3") + AIMessageChunk(
|
||||||
content=" indeed."
|
content=" indeed."
|
||||||
) == AIMessageChunk(
|
) == AIMessageChunk(content="I am indeed.", id="ai3"), (
|
||||||
content="I am indeed.", id="ai3"
|
"MessageChunk + MessageChunk should be a MessageChunk"
|
||||||
), "MessageChunk + MessageChunk should be a MessageChunk"
|
)
|
||||||
|
|
||||||
assert (
|
assert AIMessageChunk(content="I am", id="ai2") + HumanMessageChunk(
|
||||||
AIMessageChunk(content="I am", id="ai2")
|
content=" indeed.", id="human1"
|
||||||
+ HumanMessageChunk(content=" indeed.", id="human1")
|
) == AIMessageChunk(content="I am indeed.", id="ai2"), (
|
||||||
== AIMessageChunk(content="I am indeed.", id="ai2")
|
"MessageChunk + MessageChunk should be a MessageChunk "
|
||||||
), "MessageChunk + MessageChunk should be a MessageChunk of same class as the left side" # noqa: E501
|
"of same class as the left side"
|
||||||
|
)
|
||||||
|
|
||||||
assert (
|
assert AIMessageChunk(
|
||||||
AIMessageChunk(content="", additional_kwargs={"foo": "bar"})
|
content="", additional_kwargs={"foo": "bar"}
|
||||||
+ AIMessageChunk(content="", additional_kwargs={"baz": "foo"})
|
) + AIMessageChunk(content="", additional_kwargs={"baz": "foo"}) == AIMessageChunk(
|
||||||
== AIMessageChunk(content="", additional_kwargs={"foo": "bar", "baz": "foo"})
|
content="", additional_kwargs={"foo": "bar", "baz": "foo"}
|
||||||
), "MessageChunk + MessageChunk should be a MessageChunk with merged additional_kwargs" # noqa: E501
|
), (
|
||||||
|
"MessageChunk + MessageChunk should be a MessageChunk "
|
||||||
|
"with merged additional_kwargs"
|
||||||
|
)
|
||||||
|
|
||||||
assert (
|
assert AIMessageChunk(
|
||||||
AIMessageChunk(
|
|
||||||
content="", additional_kwargs={"function_call": {"name": "web_search"}}
|
content="", additional_kwargs={"function_call": {"name": "web_search"}}
|
||||||
)
|
) + AIMessageChunk(
|
||||||
+ AIMessageChunk(
|
|
||||||
content="", additional_kwargs={"function_call": {"arguments": None}}
|
content="", additional_kwargs={"function_call": {"arguments": None}}
|
||||||
)
|
) + AIMessageChunk(
|
||||||
+ AIMessageChunk(
|
|
||||||
content="", additional_kwargs={"function_call": {"arguments": "{\n"}}
|
content="", additional_kwargs={"function_call": {"arguments": "{\n"}}
|
||||||
)
|
) + AIMessageChunk(
|
||||||
+ AIMessageChunk(
|
|
||||||
content="",
|
content="",
|
||||||
additional_kwargs={
|
additional_kwargs={"function_call": {"arguments": ' "query": "turtles"\n}'}},
|
||||||
"function_call": {"arguments": ' "query": "turtles"\n}'}
|
) == AIMessageChunk(
|
||||||
},
|
|
||||||
)
|
|
||||||
== AIMessageChunk(
|
|
||||||
content="",
|
content="",
|
||||||
additional_kwargs={
|
additional_kwargs={
|
||||||
"function_call": {
|
"function_call": {
|
||||||
@ -87,8 +84,10 @@ def test_message_chunks() -> None:
|
|||||||
"arguments": '{\n "query": "turtles"\n}',
|
"arguments": '{\n "query": "turtles"\n}',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
), (
|
||||||
|
"MessageChunk + MessageChunk should be a MessageChunk "
|
||||||
|
"with merged additional_kwargs"
|
||||||
)
|
)
|
||||||
), "MessageChunk + MessageChunk should be a MessageChunk with merged additional_kwargs" # noqa: E501
|
|
||||||
|
|
||||||
# Test tool calls
|
# Test tool calls
|
||||||
assert (
|
assert (
|
||||||
@ -181,97 +180,107 @@ def test_message_chunks() -> None:
|
|||||||
def test_chat_message_chunks() -> None:
|
def test_chat_message_chunks() -> None:
|
||||||
assert ChatMessageChunk(role="User", content="I am", id="ai4") + ChatMessageChunk(
|
assert ChatMessageChunk(role="User", content="I am", id="ai4") + ChatMessageChunk(
|
||||||
role="User", content=" indeed."
|
role="User", content=" indeed."
|
||||||
) == ChatMessageChunk(
|
) == ChatMessageChunk(id="ai4", role="User", content="I am indeed."), (
|
||||||
id="ai4", role="User", content="I am indeed."
|
"ChatMessageChunk + ChatMessageChunk should be a ChatMessageChunk"
|
||||||
), "ChatMessageChunk + ChatMessageChunk should be a ChatMessageChunk"
|
)
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
ChatMessageChunk(role="User", content="I am") + ChatMessageChunk(
|
ChatMessageChunk(role="User", content="I am") + ChatMessageChunk(
|
||||||
role="Assistant", content=" indeed."
|
role="Assistant", content=" indeed."
|
||||||
)
|
)
|
||||||
|
|
||||||
assert (
|
assert ChatMessageChunk(role="User", content="I am") + AIMessageChunk(
|
||||||
ChatMessageChunk(role="User", content="I am")
|
content=" indeed."
|
||||||
+ AIMessageChunk(content=" indeed.")
|
) == ChatMessageChunk(role="User", content="I am indeed."), (
|
||||||
== ChatMessageChunk(role="User", content="I am indeed.")
|
"ChatMessageChunk + other MessageChunk should be a ChatMessageChunk "
|
||||||
), "ChatMessageChunk + other MessageChunk should be a ChatMessageChunk with the left side's role" # noqa: E501
|
"with the left side's role"
|
||||||
|
)
|
||||||
|
|
||||||
assert AIMessageChunk(content="I am") + ChatMessageChunk(
|
assert AIMessageChunk(content="I am") + ChatMessageChunk(
|
||||||
role="User", content=" indeed."
|
role="User", content=" indeed."
|
||||||
) == AIMessageChunk(
|
) == AIMessageChunk(content="I am indeed."), (
|
||||||
content="I am indeed."
|
"Other MessageChunk + ChatMessageChunk should be a MessageChunk "
|
||||||
), "Other MessageChunk + ChatMessageChunk should be a MessageChunk as the left side"
|
"as the left side"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_complex_ai_message_chunks() -> None:
|
def test_complex_ai_message_chunks() -> None:
|
||||||
assert AIMessageChunk(content=["I am"], id="ai4") + AIMessageChunk(
|
assert AIMessageChunk(content=["I am"], id="ai4") + AIMessageChunk(
|
||||||
content=[" indeed."]
|
content=[" indeed."]
|
||||||
) == AIMessageChunk(
|
) == AIMessageChunk(id="ai4", content=["I am", " indeed."]), (
|
||||||
id="ai4", content=["I am", " indeed."]
|
"Content concatenation with arrays of strings should naively combine"
|
||||||
), "Content concatenation with arrays of strings should naively combine"
|
)
|
||||||
|
|
||||||
assert AIMessageChunk(content=[{"index": 0, "text": "I am"}]) + AIMessageChunk(
|
assert AIMessageChunk(content=[{"index": 0, "text": "I am"}]) + AIMessageChunk(
|
||||||
content=" indeed."
|
content=" indeed."
|
||||||
) == AIMessageChunk(
|
) == AIMessageChunk(content=[{"index": 0, "text": "I am"}, " indeed."]), (
|
||||||
content=[{"index": 0, "text": "I am"}, " indeed."]
|
"Concatenating mixed content arrays should naively combine them"
|
||||||
), "Concatenating mixed content arrays should naively combine them"
|
)
|
||||||
|
|
||||||
assert (
|
assert AIMessageChunk(content=[{"index": 0, "text": "I am"}]) + AIMessageChunk(
|
||||||
AIMessageChunk(content=[{"index": 0, "text": "I am"}])
|
content=[{"index": 0, "text": " indeed."}]
|
||||||
+ AIMessageChunk(content=[{"index": 0, "text": " indeed."}])
|
) == AIMessageChunk(content=[{"index": 0, "text": "I am indeed."}]), (
|
||||||
== AIMessageChunk(content=[{"index": 0, "text": "I am indeed."}])
|
"Concatenating when both content arrays are dicts with the same index "
|
||||||
), "Concatenating when both content arrays are dicts with the same index should merge" # noqa: E501
|
"should merge"
|
||||||
|
)
|
||||||
|
|
||||||
assert AIMessageChunk(content=[{"index": 0, "text": "I am"}]) + AIMessageChunk(
|
assert AIMessageChunk(content=[{"index": 0, "text": "I am"}]) + AIMessageChunk(
|
||||||
content=[{"text": " indeed."}]
|
content=[{"text": " indeed."}]
|
||||||
|
) == AIMessageChunk(content=[{"index": 0, "text": "I am"}, {"text": " indeed."}]), (
|
||||||
|
"Concatenating when one chunk is missing an index should not merge or throw"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert AIMessageChunk(content=[{"index": 0, "text": "I am"}]) + AIMessageChunk(
|
||||||
|
content=[{"index": 2, "text": " indeed."}]
|
||||||
) == AIMessageChunk(
|
) == AIMessageChunk(
|
||||||
content=[{"index": 0, "text": "I am"}, {"text": " indeed."}]
|
|
||||||
), "Concatenating when one chunk is missing an index should not merge or throw" # noqa: E501
|
|
||||||
|
|
||||||
assert (
|
|
||||||
AIMessageChunk(content=[{"index": 0, "text": "I am"}])
|
|
||||||
+ AIMessageChunk(content=[{"index": 2, "text": " indeed."}])
|
|
||||||
== AIMessageChunk(
|
|
||||||
content=[{"index": 0, "text": "I am"}, {"index": 2, "text": " indeed."}]
|
content=[{"index": 0, "text": "I am"}, {"index": 2, "text": " indeed."}]
|
||||||
|
), (
|
||||||
|
"Concatenating when both content arrays are dicts with a gap between indexes "
|
||||||
|
"should not result in a holey array"
|
||||||
)
|
)
|
||||||
), "Concatenating when both content arrays are dicts with a gap between indexes should not result in a holey array" # noqa: E501
|
|
||||||
|
|
||||||
assert (
|
assert AIMessageChunk(content=[{"index": 0, "text": "I am"}]) + AIMessageChunk(
|
||||||
AIMessageChunk(content=[{"index": 0, "text": "I am"}])
|
content=[{"index": 1, "text": " indeed."}]
|
||||||
+ AIMessageChunk(content=[{"index": 1, "text": " indeed."}])
|
) == AIMessageChunk(
|
||||||
== AIMessageChunk(
|
|
||||||
content=[{"index": 0, "text": "I am"}, {"index": 1, "text": " indeed."}]
|
content=[{"index": 0, "text": "I am"}, {"index": 1, "text": " indeed."}]
|
||||||
|
), (
|
||||||
|
"Concatenating when both content arrays are dicts with separate indexes "
|
||||||
|
"should not merge"
|
||||||
)
|
)
|
||||||
), "Concatenating when both content arrays are dicts with separate indexes should not merge" # noqa: E501
|
|
||||||
|
|
||||||
assert (
|
assert AIMessageChunk(
|
||||||
AIMessageChunk(content=[{"index": 0, "text": "I am", "type": "text_block"}])
|
content=[{"index": 0, "text": "I am", "type": "text_block"}]
|
||||||
+ AIMessageChunk(
|
) + AIMessageChunk(
|
||||||
content=[{"index": 0, "text": " indeed.", "type": "text_block"}]
|
content=[{"index": 0, "text": " indeed.", "type": "text_block"}]
|
||||||
)
|
) == AIMessageChunk(
|
||||||
== AIMessageChunk(
|
|
||||||
content=[{"index": 0, "text": "I am indeed.", "type": "text_block"}]
|
content=[{"index": 0, "text": "I am indeed.", "type": "text_block"}]
|
||||||
|
), (
|
||||||
|
"Concatenating when both content arrays are dicts with the same index and type "
|
||||||
|
"should merge"
|
||||||
)
|
)
|
||||||
), "Concatenating when both content arrays are dicts with the same index and type should merge" # noqa: E501
|
|
||||||
|
|
||||||
assert (
|
assert AIMessageChunk(
|
||||||
AIMessageChunk(content=[{"index": 0, "text": "I am", "type": "text_block"}])
|
content=[{"index": 0, "text": "I am", "type": "text_block"}]
|
||||||
+ AIMessageChunk(
|
) + AIMessageChunk(
|
||||||
content=[{"index": 0, "text": " indeed.", "type": "text_block_delta"}]
|
content=[{"index": 0, "text": " indeed.", "type": "text_block_delta"}]
|
||||||
)
|
) == AIMessageChunk(
|
||||||
== AIMessageChunk(
|
|
||||||
content=[{"index": 0, "text": "I am indeed.", "type": "text_block"}]
|
content=[{"index": 0, "text": "I am indeed.", "type": "text_block"}]
|
||||||
|
), (
|
||||||
|
"Concatenating when both content arrays are dicts with the same index "
|
||||||
|
"and different types should merge without updating type"
|
||||||
)
|
)
|
||||||
), "Concatenating when both content arrays are dicts with the same index and different types should merge without updating type" # noqa: E501
|
|
||||||
|
|
||||||
assert (
|
assert AIMessageChunk(
|
||||||
AIMessageChunk(content=[{"index": 0, "text": "I am", "type": "text_block"}])
|
content=[{"index": 0, "text": "I am", "type": "text_block"}]
|
||||||
+ AIMessageChunk(content="", response_metadata={"extra": "value"})
|
) + AIMessageChunk(
|
||||||
== AIMessageChunk(
|
content="", response_metadata={"extra": "value"}
|
||||||
|
) == AIMessageChunk(
|
||||||
content=[{"index": 0, "text": "I am", "type": "text_block"}],
|
content=[{"index": 0, "text": "I am", "type": "text_block"}],
|
||||||
response_metadata={"extra": "value"},
|
response_metadata={"extra": "value"},
|
||||||
|
), (
|
||||||
|
"Concatenating when one content is an array and one is an empty string "
|
||||||
|
"should not add a new item, but should concat other fields"
|
||||||
)
|
)
|
||||||
), "Concatenating when one content is an array and one is an empty string should not add a new item, but should concat other fields" # noqa: E501
|
|
||||||
|
|
||||||
|
|
||||||
def test_function_message_chunks() -> None:
|
def test_function_message_chunks() -> None:
|
||||||
@ -290,9 +299,9 @@ def test_function_message_chunks() -> None:
|
|||||||
def test_ai_message_chunks() -> None:
|
def test_ai_message_chunks() -> None:
|
||||||
assert AIMessageChunk(example=True, content="I am") + AIMessageChunk(
|
assert AIMessageChunk(example=True, content="I am") + AIMessageChunk(
|
||||||
example=True, content=" indeed."
|
example=True, content=" indeed."
|
||||||
) == AIMessageChunk(
|
) == AIMessageChunk(example=True, content="I am indeed."), (
|
||||||
example=True, content="I am indeed."
|
"AIMessageChunk + AIMessageChunk should be a AIMessageChunk"
|
||||||
), "AIMessageChunk + AIMessageChunk should be a AIMessageChunk"
|
)
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
AIMessageChunk(example=True, content="I am") + AIMessageChunk(
|
AIMessageChunk(example=True, content="I am") + AIMessageChunk(
|
||||||
|
@ -5,24 +5,25 @@ from langchain_core.outputs import ChatGenerationChunk, GenerationChunk
|
|||||||
def test_generation_chunk() -> None:
|
def test_generation_chunk() -> None:
|
||||||
assert GenerationChunk(text="Hello, ") + GenerationChunk(
|
assert GenerationChunk(text="Hello, ") + GenerationChunk(
|
||||||
text="world!"
|
text="world!"
|
||||||
) == GenerationChunk(
|
) == GenerationChunk(text="Hello, world!"), (
|
||||||
text="Hello, world!"
|
"GenerationChunk + GenerationChunk should be a GenerationChunk"
|
||||||
), "GenerationChunk + GenerationChunk should be a GenerationChunk"
|
)
|
||||||
|
|
||||||
assert (
|
assert GenerationChunk(text="Hello, ") + GenerationChunk(
|
||||||
GenerationChunk(text="Hello, ")
|
text="world!", generation_info={"foo": "bar"}
|
||||||
+ GenerationChunk(text="world!", generation_info={"foo": "bar"})
|
) == GenerationChunk(text="Hello, world!", generation_info={"foo": "bar"}), (
|
||||||
== GenerationChunk(text="Hello, world!", generation_info={"foo": "bar"})
|
"GenerationChunk + GenerationChunk should be a GenerationChunk "
|
||||||
), "GenerationChunk + GenerationChunk should be a GenerationChunk with merged generation_info" # noqa: E501
|
"with merged generation_info"
|
||||||
|
)
|
||||||
assert (
|
|
||||||
GenerationChunk(text="Hello, ")
|
assert GenerationChunk(text="Hello, ") + GenerationChunk(
|
||||||
+ GenerationChunk(text="world!", generation_info={"foo": "bar"})
|
text="world!", generation_info={"foo": "bar"}
|
||||||
+ GenerationChunk(text="!", generation_info={"baz": "foo"})
|
) + GenerationChunk(text="!", generation_info={"baz": "foo"}) == GenerationChunk(
|
||||||
== GenerationChunk(
|
text="Hello, world!!", generation_info={"foo": "bar", "baz": "foo"}
|
||||||
text="Hello, world!!", generation_info={"foo": "bar", "baz": "foo"}
|
), (
|
||||||
|
"GenerationChunk + GenerationChunk should be a GenerationChunk "
|
||||||
|
"with merged generation_info"
|
||||||
)
|
)
|
||||||
), "GenerationChunk + GenerationChunk should be a GenerationChunk with merged generation_info" # noqa: E501
|
|
||||||
|
|
||||||
|
|
||||||
def test_chat_generation_chunk() -> None:
|
def test_chat_generation_chunk() -> None:
|
||||||
@ -30,31 +31,32 @@ def test_chat_generation_chunk() -> None:
|
|||||||
message=HumanMessageChunk(content="Hello, ")
|
message=HumanMessageChunk(content="Hello, ")
|
||||||
) + ChatGenerationChunk(
|
) + ChatGenerationChunk(
|
||||||
message=HumanMessageChunk(content="world!")
|
message=HumanMessageChunk(content="world!")
|
||||||
) == ChatGenerationChunk(
|
) == ChatGenerationChunk(message=HumanMessageChunk(content="Hello, world!")), (
|
||||||
message=HumanMessageChunk(content="Hello, world!")
|
"ChatGenerationChunk + ChatGenerationChunk should be a ChatGenerationChunk"
|
||||||
), "ChatGenerationChunk + ChatGenerationChunk should be a ChatGenerationChunk"
|
|
||||||
|
|
||||||
assert (
|
|
||||||
ChatGenerationChunk(message=HumanMessageChunk(content="Hello, "))
|
|
||||||
+ ChatGenerationChunk(
|
|
||||||
message=HumanMessageChunk(content="world!"), generation_info={"foo": "bar"}
|
|
||||||
)
|
)
|
||||||
== ChatGenerationChunk(
|
|
||||||
|
assert ChatGenerationChunk(
|
||||||
|
message=HumanMessageChunk(content="Hello, ")
|
||||||
|
) + ChatGenerationChunk(
|
||||||
|
message=HumanMessageChunk(content="world!"), generation_info={"foo": "bar"}
|
||||||
|
) == ChatGenerationChunk(
|
||||||
message=HumanMessageChunk(content="Hello, world!"),
|
message=HumanMessageChunk(content="Hello, world!"),
|
||||||
generation_info={"foo": "bar"},
|
generation_info={"foo": "bar"},
|
||||||
|
), (
|
||||||
|
"GenerationChunk + GenerationChunk should be a GenerationChunk "
|
||||||
|
"with merged generation_info"
|
||||||
)
|
)
|
||||||
), "GenerationChunk + GenerationChunk should be a GenerationChunk with merged generation_info" # noqa: E501
|
|
||||||
|
|
||||||
assert (
|
assert ChatGenerationChunk(
|
||||||
ChatGenerationChunk(message=HumanMessageChunk(content="Hello, "))
|
message=HumanMessageChunk(content="Hello, ")
|
||||||
+ ChatGenerationChunk(
|
) + ChatGenerationChunk(
|
||||||
message=HumanMessageChunk(content="world!"), generation_info={"foo": "bar"}
|
message=HumanMessageChunk(content="world!"), generation_info={"foo": "bar"}
|
||||||
)
|
) + ChatGenerationChunk(
|
||||||
+ ChatGenerationChunk(
|
|
||||||
message=HumanMessageChunk(content="!"), generation_info={"baz": "foo"}
|
message=HumanMessageChunk(content="!"), generation_info={"baz": "foo"}
|
||||||
)
|
) == ChatGenerationChunk(
|
||||||
== ChatGenerationChunk(
|
|
||||||
message=HumanMessageChunk(content="Hello, world!!"),
|
message=HumanMessageChunk(content="Hello, world!!"),
|
||||||
generation_info={"foo": "bar", "baz": "foo"},
|
generation_info={"foo": "bar", "baz": "foo"},
|
||||||
|
), (
|
||||||
|
"GenerationChunk + GenerationChunk should be a GenerationChunk "
|
||||||
|
"with merged generation_info"
|
||||||
)
|
)
|
||||||
), "GenerationChunk + GenerationChunk should be a GenerationChunk with merged generation_info" # noqa: E501
|
|
||||||
|
@ -1571,7 +1571,12 @@ def test_tool_injected_arg_without_schema(tool_: BaseTool) -> None:
|
|||||||
}
|
}
|
||||||
assert tool_.invoke({"x": 5, "y": "bar"}) == "bar"
|
assert tool_.invoke({"x": 5, "y": "bar"}) == "bar"
|
||||||
assert tool_.invoke(
|
assert tool_.invoke(
|
||||||
{"name": "foo", "args": {"x": 5, "y": "bar"}, "id": "123", "type": "tool_call"}
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"args": {"x": 5, "y": "bar"},
|
||||||
|
"id": "123",
|
||||||
|
"type": "tool_call",
|
||||||
|
}
|
||||||
) == ToolMessage("bar", tool_call_id="123", name="foo")
|
) == ToolMessage("bar", tool_call_id="123", name="foo")
|
||||||
expected_error = (
|
expected_error = (
|
||||||
ValidationError if not isinstance(tool_, InjectedTool) else TypeError
|
ValidationError if not isinstance(tool_, InjectedTool) else TypeError
|
||||||
@ -1614,7 +1619,12 @@ def test_tool_injected_arg_with_schema(tool_: BaseTool) -> None:
|
|||||||
}
|
}
|
||||||
assert tool_.invoke({"x": 5, "y": "bar"}) == "bar"
|
assert tool_.invoke({"x": 5, "y": "bar"}) == "bar"
|
||||||
assert tool_.invoke(
|
assert tool_.invoke(
|
||||||
{"name": "foo", "args": {"x": 5, "y": "bar"}, "id": "123", "type": "tool_call"}
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"args": {"x": 5, "y": "bar"},
|
||||||
|
"id": "123",
|
||||||
|
"type": "tool_call",
|
||||||
|
}
|
||||||
) == ToolMessage("bar", tool_call_id="123", name="foo")
|
) == ToolMessage("bar", tool_call_id="123", name="foo")
|
||||||
expected_error = (
|
expected_error = (
|
||||||
ValidationError if not isinstance(tool_, InjectedTool) else TypeError
|
ValidationError if not isinstance(tool_, InjectedTool) else TypeError
|
||||||
@ -1654,7 +1664,12 @@ def test_tool_injected_arg() -> None:
|
|||||||
}
|
}
|
||||||
assert tool_.invoke({"x": 5, "y": "bar"}) == "bar"
|
assert tool_.invoke({"x": 5, "y": "bar"}) == "bar"
|
||||||
assert tool_.invoke(
|
assert tool_.invoke(
|
||||||
{"name": "foo", "args": {"x": 5, "y": "bar"}, "id": "123", "type": "tool_call"}
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"args": {"x": 5, "y": "bar"},
|
||||||
|
"id": "123",
|
||||||
|
"type": "tool_call",
|
||||||
|
}
|
||||||
) == ToolMessage("bar", tool_call_id="123", name="foo")
|
) == ToolMessage("bar", tool_call_id="123", name="foo")
|
||||||
expected_error = (
|
expected_error = (
|
||||||
ValidationError if not isinstance(tool_, InjectedTool) else TypeError
|
ValidationError if not isinstance(tool_, InjectedTool) else TypeError
|
||||||
@ -1715,7 +1730,12 @@ def test_tool_inherited_injected_arg() -> None:
|
|||||||
}
|
}
|
||||||
assert tool_.invoke({"x": 5, "y": "bar"}) == "bar"
|
assert tool_.invoke({"x": 5, "y": "bar"}) == "bar"
|
||||||
assert tool_.invoke(
|
assert tool_.invoke(
|
||||||
{"name": "foo", "args": {"x": 5, "y": "bar"}, "id": "123", "type": "tool_call"}
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"args": {"x": 5, "y": "bar"},
|
||||||
|
"id": "123",
|
||||||
|
"type": "tool_call",
|
||||||
|
}
|
||||||
) == ToolMessage("bar", tool_call_id="123", name="foo")
|
) == ToolMessage("bar", tool_call_id="123", name="foo")
|
||||||
expected_error = (
|
expected_error = (
|
||||||
ValidationError if not isinstance(tool_, InjectedTool) else TypeError
|
ValidationError if not isinstance(tool_, InjectedTool) else TypeError
|
||||||
@ -1987,6 +2007,7 @@ def test__get_all_basemodel_annotations_v2(use_v1_namespace: bool) -> None:
|
|||||||
|
|
||||||
class ModelA(BaseModel1, Generic[A], extra="allow"):
|
class ModelA(BaseModel1, Generic[A], extra="allow"):
|
||||||
a: A
|
a: A
|
||||||
|
|
||||||
else:
|
else:
|
||||||
from pydantic import BaseModel as BaseModel2
|
from pydantic import BaseModel as BaseModel2
|
||||||
from pydantic import ConfigDict
|
from pydantic import ConfigDict
|
||||||
@ -2272,7 +2293,12 @@ def test_tool_injected_tool_call_id() -> None:
|
|||||||
return ToolMessage(x, tool_call_id=tool_call_id) # type: ignore
|
return ToolMessage(x, tool_call_id=tool_call_id) # type: ignore
|
||||||
|
|
||||||
assert foo.invoke(
|
assert foo.invoke(
|
||||||
{"type": "tool_call", "args": {"x": 0}, "name": "foo", "id": "bar"}
|
{
|
||||||
|
"type": "tool_call",
|
||||||
|
"args": {"x": 0},
|
||||||
|
"name": "foo",
|
||||||
|
"id": "bar",
|
||||||
|
}
|
||||||
) == ToolMessage(0, tool_call_id="bar") # type: ignore
|
) == ToolMessage(0, tool_call_id="bar") # type: ignore
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
@ -2284,7 +2310,12 @@ def test_tool_injected_tool_call_id() -> None:
|
|||||||
return ToolMessage(x, tool_call_id=tool_call_id) # type: ignore
|
return ToolMessage(x, tool_call_id=tool_call_id) # type: ignore
|
||||||
|
|
||||||
assert foo2.invoke(
|
assert foo2.invoke(
|
||||||
{"type": "tool_call", "args": {"x": 0}, "name": "foo", "id": "bar"}
|
{
|
||||||
|
"type": "tool_call",
|
||||||
|
"args": {"x": 0},
|
||||||
|
"name": "foo",
|
||||||
|
"id": "bar",
|
||||||
|
}
|
||||||
) == ToolMessage(0, tool_call_id="bar") # type: ignore
|
) == ToolMessage(0, tool_call_id="bar") # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@ -2321,7 +2352,12 @@ def test_tool_return_output_mixin() -> None:
|
|||||||
return Bar(x=x)
|
return Bar(x=x)
|
||||||
|
|
||||||
assert foo.invoke(
|
assert foo.invoke(
|
||||||
{"type": "tool_call", "args": {"x": 0}, "name": "foo", "id": "bar"}
|
{
|
||||||
|
"type": "tool_call",
|
||||||
|
"args": {"x": 0},
|
||||||
|
"name": "foo",
|
||||||
|
"id": "bar",
|
||||||
|
}
|
||||||
) == Bar(x=0)
|
) == Bar(x=0)
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,9 +54,9 @@ async def test_same_event_loop() -> None:
|
|||||||
# To verify that the producer and consumer are running in parallel, we
|
# To verify that the producer and consumer are running in parallel, we
|
||||||
# expect the delta_time to be smaller than the sleep delay in the producer
|
# expect the delta_time to be smaller than the sleep delay in the producer
|
||||||
# * # of items = 30 ms
|
# * # of items = 30 ms
|
||||||
assert (
|
assert math.isclose(delta_time, 0, abs_tol=0.010) is True, (
|
||||||
math.isclose(delta_time, 0, abs_tol=0.010) is True
|
f"delta_time: {delta_time}"
|
||||||
), f"delta_time: {delta_time}"
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_queue_for_streaming_via_sync_call() -> None:
|
async def test_queue_for_streaming_via_sync_call() -> None:
|
||||||
@ -107,9 +107,9 @@ async def test_queue_for_streaming_via_sync_call() -> None:
|
|||||||
# To verify that the producer and consumer are running in parallel, we
|
# To verify that the producer and consumer are running in parallel, we
|
||||||
# expect the delta_time to be smaller than the sleep delay in the producer
|
# expect the delta_time to be smaller than the sleep delay in the producer
|
||||||
# * # of items = 30 ms
|
# * # of items = 30 ms
|
||||||
assert (
|
assert math.isclose(delta_time, 0, abs_tol=0.010) is True, (
|
||||||
math.isclose(delta_time, 0, abs_tol=0.010) is True
|
f"delta_time: {delta_time}"
|
||||||
), f"delta_time: {delta_time}"
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_send_to_closed_stream() -> None:
|
def test_send_to_closed_stream() -> None:
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import operator
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from langchain_core.utils.usage import _dict_int_op
|
from langchain_core.utils.usage import _dict_int_op
|
||||||
@ -6,7 +8,7 @@ from langchain_core.utils.usage import _dict_int_op
|
|||||||
def test_dict_int_op_add() -> None:
|
def test_dict_int_op_add() -> None:
|
||||||
left = {"a": 1, "b": 2}
|
left = {"a": 1, "b": 2}
|
||||||
right = {"b": 3, "c": 4}
|
right = {"b": 3, "c": 4}
|
||||||
result = _dict_int_op(left, right, lambda x, y: x + y)
|
result = _dict_int_op(left, right, operator.add)
|
||||||
assert result == {"a": 1, "b": 5, "c": 4}
|
assert result == {"a": 1, "b": 5, "c": 4}
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +22,7 @@ def test_dict_int_op_subtract() -> None:
|
|||||||
def test_dict_int_op_nested() -> None:
|
def test_dict_int_op_nested() -> None:
|
||||||
left = {"a": 1, "b": {"c": 2, "d": 3}}
|
left = {"a": 1, "b": {"c": 2, "d": 3}}
|
||||||
right = {"a": 2, "b": {"c": 1, "e": 4}}
|
right = {"a": 2, "b": {"c": 1, "e": 4}}
|
||||||
result = _dict_int_op(left, right, lambda x, y: x + y)
|
result = _dict_int_op(left, right, operator.add)
|
||||||
assert result == {"a": 3, "b": {"c": 3, "d": 3, "e": 4}}
|
assert result == {"a": 3, "b": {"c": 3, "d": 3, "e": 4}}
|
||||||
|
|
||||||
|
|
||||||
@ -28,11 +30,11 @@ def test_dict_int_op_max_depth_exceeded() -> None:
|
|||||||
left = {"a": {"b": {"c": 1}}}
|
left = {"a": {"b": {"c": 1}}}
|
||||||
right = {"a": {"b": {"c": 2}}}
|
right = {"a": {"b": {"c": 2}}}
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
_dict_int_op(left, right, lambda x, y: x + y, max_depth=2)
|
_dict_int_op(left, right, operator.add, max_depth=2)
|
||||||
|
|
||||||
|
|
||||||
def test_dict_int_op_invalid_types() -> None:
|
def test_dict_int_op_invalid_types() -> None:
|
||||||
left = {"a": 1, "b": "string"}
|
left = {"a": 1, "b": "string"}
|
||||||
right = {"a": 2, "b": 3}
|
right = {"a": 2, "b": 3}
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
_dict_int_op(left, right, lambda x, y: x + y)
|
_dict_int_op(left, right, operator.add)
|
||||||
|
Loading…
Reference in New Issue
Block a user