Skip to content

Commit eb34205

Browse files
nicoloboschiautofix-ci[bot]ogabrielluizCristhianzl
authored
feat: migrate chains and memories to Component syntax (langflow-ai#2528)
* feat: migrate chains and memories to Component syntax * use base class * add classes * [autofix.ci] apply automated fixes * fix tests * fix tests * ✅ (filterSidebar.spec.ts): increase waitForTimeout from 1000ms to 2000ms to ensure elements are fully loaded before interaction --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida <[email protected]> Co-authored-by: cristhianzl <[email protected]> Co-authored-by: Cristhian Zanforlin Lousa <[email protected]>
1 parent 5d9b29e commit eb34205

24 files changed

+526
-1091
lines changed

src/backend/base/langflow/base/chains/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from langflow.custom import Component
2+
from langflow.template import Output
3+
4+
5+
class LCChainComponent(Component):
6+
trace_type = "chain"
7+
8+
outputs = [Output(display_name="Text", name="text", method="invoke_chain")]
9+
10+
def _validate_outputs(self):
11+
required_output_methods = ["invoke_chain"]
12+
output_names = [output.name for output in self.outputs]
13+
for method_name in required_output_methods:
14+
if method_name not in output_names:
15+
raise ValueError(f"Output with name '{method_name}' must be defined.")
16+
elif not hasattr(self, method_name):
17+
raise ValueError(f"Method '{method_name}' must be defined.")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from abc import abstractmethod
2+
3+
from langflow.custom import Component
4+
from langflow.field_typing import BaseChatMessageHistory, BaseChatMemory
5+
from langflow.template import Output
6+
from langchain.memory import ConversationBufferMemory
7+
8+
9+
class LCChatMemoryComponent(Component):
10+
trace_type = "chat_memory"
11+
outputs = [
12+
Output(
13+
display_name="Memory",
14+
name="base_memory",
15+
method="build_base_memory",
16+
)
17+
]
18+
19+
def _validate_outputs(self):
20+
required_output_methods = ["build_base_memory"]
21+
output_names = [output.name for output in self.outputs]
22+
for method_name in required_output_methods:
23+
if method_name not in output_names:
24+
raise ValueError(f"Output with name '{method_name}' must be defined.")
25+
elif not hasattr(self, method_name):
26+
raise ValueError(f"Method '{method_name}' must be defined.")
27+
28+
def build_base_memory(self) -> BaseChatMemory:
29+
return ConversationBufferMemory(chat_memory=self.build_message_history())
30+
31+
@abstractmethod
32+
def build_message_history(self) -> BaseChatMessageHistory:
33+
"""
34+
Builds the chat message history memory.
35+
"""
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,41 @@
1-
from typing import Optional
2-
31
from langchain.chains import ConversationChain
42

5-
from langflow.custom import CustomComponent
6-
from langflow.field_typing import BaseMemory, LanguageModel, Text
3+
from langflow.base.chains.model import LCChainComponent
4+
from langflow.field_typing import Message
5+
from langflow.inputs import MultilineInput, HandleInput
76

87

9-
class ConversationChainComponent(CustomComponent):
8+
class ConversationChainComponent(LCChainComponent):
109
display_name = "ConversationChain"
1110
description = "Chain to have a conversation and load context from memory."
1211
name = "ConversationChain"
1312

14-
def build_config(self):
15-
return {
16-
"prompt": {"display_name": "Prompt"},
17-
"llm": {"display_name": "LLM"},
18-
"memory": {
19-
"display_name": "Memory",
20-
"info": "Memory to load context from. If none is provided, a ConversationBufferMemory will be used.",
21-
},
22-
"input_value": {
23-
"display_name": "Input Value",
24-
"info": "The input value to pass to the chain.",
25-
},
26-
}
13+
inputs = [
14+
MultilineInput(
15+
name="input_value", display_name="Input", info="The input value to pass to the chain.", required=True
16+
),
17+
HandleInput(name="llm", display_name="Language Model", input_types=["LanguageModel"], required=True),
18+
HandleInput(
19+
name="memory",
20+
display_name="Memory",
21+
input_types=["BaseChatMemory"],
22+
),
23+
]
2724

28-
def build(
29-
self,
30-
input_value: Text,
31-
llm: LanguageModel,
32-
memory: Optional[BaseMemory] = None,
33-
) -> Text:
34-
if memory is None:
35-
chain = ConversationChain(llm=llm)
25+
def invoke_chain(self) -> Message:
26+
if not self.memory:
27+
chain = ConversationChain(llm=self.llm)
3628
else:
37-
chain = ConversationChain(llm=llm, memory=memory)
38-
result = chain.invoke({"input": input_value})
29+
chain = ConversationChain(llm=self.llm, memory=self.memory)
30+
31+
result = chain.invoke({"input": self.input_value})
3932
if isinstance(result, dict):
4033
result = result.get(chain.output_key, "") # type: ignore
4134

4235
elif isinstance(result, str):
4336
result = result
4437
else:
4538
result = result.get("response")
39+
result = str(result)
4640
self.status = result
47-
return str(result)
41+
return Message(text=result)

src/backend/base/langflow/components/chains/LLMChain.py

-34
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,27 @@
11
from langchain.chains import LLMCheckerChain
22

3-
from langflow.custom import CustomComponent
4-
from langflow.field_typing import LanguageModel, Text
3+
from langflow.base.chains.model import LCChainComponent
4+
from langflow.field_typing import Message
5+
from langflow.inputs import MultilineInput, HandleInput
56

67

7-
class LLMCheckerChainComponent(CustomComponent):
8+
class LLMCheckerChainComponent(LCChainComponent):
89
display_name = "LLMCheckerChain"
9-
description = ""
10+
description = "Chain for question-answering with self-verification."
1011
documentation = "https://python.langchain.com/docs/modules/chains/additional/llm_checker"
1112
name = "LLMCheckerChain"
1213

13-
def build_config(self):
14-
return {
15-
"llm": {"display_name": "LLM"},
16-
"input_value": {
17-
"display_name": "Input Value",
18-
"info": "The input value to pass to the chain.",
19-
},
20-
}
14+
inputs = [
15+
MultilineInput(
16+
name="input_value", display_name="Input", info="The input value to pass to the chain.", required=True
17+
),
18+
HandleInput(name="llm", display_name="Language Model", input_types=["LanguageModel"], required=True),
19+
]
2120

22-
def build(
23-
self,
24-
input_value: Text,
25-
llm: LanguageModel,
26-
) -> Text:
27-
chain = LLMCheckerChain.from_llm(llm=llm)
28-
response = chain.invoke({chain.input_key: input_value})
21+
def invoke_chain(self) -> Message:
22+
chain = LLMCheckerChain.from_llm(llm=self.llm)
23+
response = chain.invoke({chain.input_key: self.input_value})
2924
result = response.get(chain.output_key, "")
30-
result_str = str(result)
31-
self.status = result_str
32-
return result_str
25+
result = str(result)
26+
self.status = result
27+
return Message(text=result)
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,30 @@
1-
from typing import Optional
1+
from langchain.chains import LLMMathChain
22

3-
from langchain.chains import LLMChain, LLMMathChain
3+
from langflow.base.chains.model import LCChainComponent
4+
from langflow.field_typing import Message
5+
from langflow.inputs import MultilineInput, HandleInput
6+
from langflow.template import Output
47

5-
from langflow.custom import CustomComponent
6-
from langflow.field_typing import BaseMemory, LanguageModel, Text
78

8-
9-
class LLMMathChainComponent(CustomComponent):
9+
class LLMMathChainComponent(LCChainComponent):
1010
display_name = "LLMMathChain"
1111
description = "Chain that interprets a prompt and executes python code to do math."
1212
documentation = "https://python.langchain.com/docs/modules/chains/additional/llm_math"
1313
name = "LLMMathChain"
1414

15-
def build_config(self):
16-
return {
17-
"llm": {"display_name": "LLM"},
18-
"llm_chain": {"display_name": "LLM Chain"},
19-
"memory": {"display_name": "Memory"},
20-
"input_key": {"display_name": "Input Key"},
21-
"output_key": {"display_name": "Output Key"},
22-
"input_value": {
23-
"display_name": "Input Value",
24-
"info": "The input value to pass to the chain.",
25-
},
26-
}
15+
inputs = [
16+
MultilineInput(
17+
name="input_value", display_name="Input", info="The input value to pass to the chain.", required=True
18+
),
19+
HandleInput(name="llm", display_name="Language Model", input_types=["LanguageModel"], required=True),
20+
]
21+
22+
outputs = [Output(display_name="Text", name="text", method="invoke_chain")]
2723

28-
def build(
29-
self,
30-
input_value: Text,
31-
llm: LanguageModel,
32-
llm_chain: LLMChain,
33-
input_key: str = "question",
34-
output_key: str = "answer",
35-
memory: Optional[BaseMemory] = None,
36-
) -> Text:
37-
chain = LLMMathChain(
38-
llm=llm,
39-
llm_chain=llm_chain,
40-
input_key=input_key,
41-
output_key=output_key,
42-
memory=memory,
43-
)
44-
response = chain.invoke({input_key: input_value})
45-
result = response.get(output_key)
46-
result_str = str(result)
47-
self.status = result_str
48-
return result_str
24+
def invoke_chain(self) -> Message:
25+
chain = LLMMathChain.from_llm(llm=self.llm)
26+
response = chain.invoke({chain.input_key: self.input_value})
27+
result = response.get(chain.output_key, "")
28+
result = str(result)
29+
self.status = result
30+
return Message(text=result)

0 commit comments

Comments
 (0)