fix: update message reload and update langchain-core (#3054)

* refactor: update langchain-core to version 0.2.24

* refactor: convert inner messages to BaseMessage in load_lc_prompt method

* refactor: update ChatPromptTemplate instantiation in message.py

* refactor: update langflow-base dependency to use local path for development

* [autofix.ci] apply automated fixes

* refactor: update ChatPromptTemplate instantiation in message.py

* refactor: add async_from_template_and_variables and sync from_template_and_variables

* feat(tests): add unit test for Message schema serialization and prompt loading

* refactor: update langchain-core dependency to version 0.2.24

* chore: new lock

* mypy

* chore: format pyproject

* refactor: rename async_from_template_and_variables to from_template_and_variables in Message class

* refactor: Rename async_from_template_and_variables to from_template_and_variables in Message class

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-07-30 10:54:51 -03:00 • committed by GitHub
commit 4382e42d77
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 565 additions and 590 deletions

1063
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,7 @@ langflow = "langflow.__main__:main"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = ">=3.10,<3.13" python = ">=3.10,<3.13"
langflow-base = { path = "./src/backend/base", develop = true } langflow-base ={ path = "./src/backend/base", develop = true }
beautifulsoup4 = "^4.12.2" beautifulsoup4 = "^4.12.2"
google-search-results = "^2.4.1" google-search-results = "^2.4.1"
google-api-python-client = "^2.130.0" google-api-python-client = "^2.130.0"

View file

@ -1014,7 +1014,7 @@ class Graph:
else: else:
self.run_manager.add_to_vertices_being_run(next_v_id) self.run_manager.add_to_vertices_being_run(next_v_id)
if cache and self.flow_id: if cache and self.flow_id:
set_cache_coro = partial(get_chat_service().set_cache, key=self.flow_id) set_cache_coro = partial(get_chat_service().set_cache, self.flow_id)
await set_cache_coro(self, lock) await set_cache_coro(self, lock)
return next_runnable_vertices return next_runnable_vertices

View file

@ -1,3 +1,4 @@
import asyncio
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Annotated, Any, AsyncIterator, Iterator, List, Optional from typing import Annotated, Any, AsyncIterator, Iterator, List, Optional
from uuid import UUID from uuid import UUID
@ -15,10 +16,10 @@ from langflow.base.prompts.utils import dict_values_to_string
from langflow.schema.data import Data from langflow.schema.data import Data
from langflow.schema.image import Image, get_file_paths, is_image_file from langflow.schema.image import Image, get_file_paths, is_image_file
from langflow.utils.constants import ( from langflow.utils.constants import (
MESSAGE_SENDER_USER,
MESSAGE_SENDER_NAME_USER,
MESSAGE_SENDER_NAME_AI,
MESSAGE_SENDER_AI, MESSAGE_SENDER_AI,
MESSAGE_SENDER_NAME_AI,
MESSAGE_SENDER_NAME_USER,
MESSAGE_SENDER_USER,
) )
@ -171,20 +172,23 @@ class Message(Data):
def load_lc_prompt(self): def load_lc_prompt(self):
if "prompt" not in self: if "prompt" not in self:
raise ValueError("Prompt is required.") raise ValueError("Prompt is required.")
loaded_prompt = load(self.prompt) # self.prompt was passed through jsonable_encoder
# Rebuild HumanMessages if they are instance of BaseMessage # so inner messages are not BaseMessage
if isinstance(loaded_prompt, ChatPromptTemplate): # we need to convert them to BaseMessage
messages = [] messages = []
for message in loaded_prompt.messages: for message in self.prompt.get("kwargs", {}).get("messages", []):
if isinstance(message, HumanMessage): match message:
case HumanMessage():
messages.append(message) messages.append(message)
elif message.type == "human": case _ if message.get("type") == "human":
messages.append(HumanMessage(content=message.content)) messages.append(HumanMessage(content=message.get("content")))
elif message.type == "system": case _ if message.get("type") == "system":
messages.append(SystemMessage(content=message.content)) messages.append(SystemMessage(content=message.get("content")))
elif message.type == "ai": case _ if message.get("type") == "ai":
messages.append(AIMessage(content=message.content)) messages.append(AIMessage(content=message.get("content")))
loaded_prompt.messages = messages
self.prompt["kwargs"]["messages"] = messages
loaded_prompt = load(self.prompt)
return loaded_prompt return loaded_prompt
@classmethod @classmethod
@ -216,7 +220,17 @@ class Message(Data):
if contents: if contents:
message = HumanMessage(content=[{"type": "text", "text": text}] + contents) message = HumanMessage(content=[{"type": "text", "text": text}] + contents)
prompt_template = ChatPromptTemplate.from_messages([message]) # type: ignore prompt_template = ChatPromptTemplate(messages=[message]) # type: ignore
instance.prompt = jsonable_encoder(prompt_template.to_json()) instance.prompt = jsonable_encoder(prompt_template.to_json())
instance.messages = instance.prompt.get("kwargs", {}).get("messages", []) instance.messages = instance.prompt.get("kwargs", {}).get("messages", [])
return instance return instance
@classmethod
def sync_from_template_and_variables(cls, template: str, **variables):
# Run the async version in a sync way
try:
loop = asyncio.get_running_loop()
except RuntimeError:
return asyncio.run(cls.from_template_and_variables(template, **variables))
else:
return loop.run_until_complete(cls.from_template_and_variables(template, **variables))

View file

@ -1314,13 +1314,13 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
[[package]] [[package]]
name = "langchain-core" name = "langchain-core"
version = "0.2.23" version = "0.2.24"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "langchain_core-0.2.23-py3-none-any.whl", hash = "sha256:ef0b4184b37e356a27182514aedcc8c41ffacbd6348a801bc775c1ce1f608637"}, {file = "langchain_core-0.2.24-py3-none-any.whl", hash = "sha256:9444fc082d21ef075d925590a684a73fe1f9688a3d90087580ec929751be55e7"},
{file = "langchain_core-0.2.23.tar.gz", hash = "sha256:ac8165f283d8f5214576ffc38387106ef0de7eb8d2c52576d06e8dd3285294b0"}, {file = "langchain_core-0.2.24.tar.gz", hash = "sha256:f2e3fa200b124e8c45d270da9bf836bed9c09532612c96ff3225e59b9a232f5a"},
] ]
[package.dependencies] [package.dependencies]
@ -3597,4 +3597,4 @@ local = []
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.10,<3.13" python-versions = ">=3.10,<3.13"
content-hash = "ec75a891f635b67266af56a85c699abe72a73dad69a05a9d5750d8dce56b4bf8" content-hash = "824e91c3465ec9eb51bcd5f26297c45232f1fce0298fe2b5673fd5cbb2503734"

View file

@ -32,7 +32,7 @@ httpx = "*"
uvicorn = "^0.30.0" uvicorn = "^0.30.0"
gunicorn = "^22.0.0" gunicorn = "^22.0.0"
langchain = "~0.2.0" langchain = "~0.2.0"
langchain-core = "0.2.23" langchain-core = "^0.2.24"
langchainhub = "~0.1.15" langchainhub = "~0.1.15"
sqlmodel = "^0.0.18" sqlmodel = "^0.0.18"
loguru = "^0.7.1" loguru = "^0.7.1"

View file

@ -0,0 +1,30 @@
import pytest
from langchain_core.prompts.chat import ChatPromptTemplate
from langflow.schema.message import Message
@pytest.fixture
def client():
pass
@pytest.mark.asyncio
async def test_message_async_prompt_serialization():
template = "Hello, {name}!"
message = await Message.from_template_and_variables(template, name="Langflow")
assert message.text == "Hello, Langflow!"
prompt = message.load_lc_prompt()
assert isinstance(prompt, ChatPromptTemplate)
assert prompt.messages[0].content == "Hello, Langflow!"
def test_message_prompt_serialization():
template = "Hello, {name}!"
message = Message.sync_from_template_and_variables(template, name="Langflow")
assert message.text == "Hello, Langflow!"
prompt = message.load_lc_prompt()
assert isinstance(prompt, ChatPromptTemplate)
assert prompt.messages[0].content == "Hello, Langflow!"