feat: add starter project graphs (#3369)

* feat: Add basic prompting graph function.

* feat: Add blog writer starter project function.

* feat(langflow): Add document QA starter project.

* feat: Add memory chatbot graph function to create chatbot with memory component.

* feat: Add hierarchical tasks agent graph to handle sequential tasks.

* feat: Add a function to create a sequential tasks agent with specific tasks.

* feat: Add vector_store_rag module with ingestion and RAG graphs.

* Refactor: Update the hierarchical task agent to use builder methods for agents and models.

* feat: Refactor sequential tasks agent to utilize build_model and build_output methods.

* refactor: Rename functions in blog_writer, document_qa, and vector_store_rag to end with "_graph".

* feat: Add new graphs to starter projects __init__.py.

* feat: Add complex agent graph setup with prompts, tools, and agents.

* refactor: Add complex agent graph to starter projects.

* feat: Add starter project graphs and dump retrieval functions.

* test: Refactor test_directory_without_mocks method with temporary directory for testing purposes.
This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-08-16 16:47:19 -03:00 • committed by GitHub
commit d313137f8c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 533 additions and 18 deletions

View file

@ -0,0 +1,25 @@
from .starter_projects import (
basic_prompting_graph,
blog_writer_graph,
document_qa_graph,
hierarchical_tasks_agent_graph,
memory_chatbot_graph,
sequential_tasks_agent_graph,
vector_store_rag_graph,
)
def get_starter_projects_graphs():
return [
basic_prompting_graph(),
blog_writer_graph(),
document_qa_graph(),
memory_chatbot_graph(),
vector_store_rag_graph(),
sequential_tasks_agent_graph(),
hierarchical_tasks_agent_graph(),
]
def get_starter_projects_dump():
return [g.dump() for g in get_starter_projects_graphs()]

View file

@ -0,0 +1,19 @@
from .basic_prompting import basic_prompting_graph
from .blog_writer import blog_writer_graph
from .document_qa import document_qa_graph
from .hierarchical_tasks_agent import hierarchical_tasks_agent_graph
from .memory_chatbot import memory_chatbot_graph
from .sequential_tasks_agent import sequential_tasks_agent_graph
from .vector_store_rag import vector_store_rag_graph
from .complex_agent import complex_agent_graph
__all__ = [
"blog_writer_graph",
"document_qa_graph",
"memory_chatbot_graph",
"vector_store_rag_graph",
"basic_prompting_graph",
"sequential_tasks_agent_graph",
"hierarchical_tasks_agent_graph",
"complex_agent_graph",
]

View file

@ -0,0 +1,30 @@
from langflow.components.inputs.ChatInput import ChatInput
from langflow.components.models.OpenAIModel import OpenAIModelComponent
from langflow.components.outputs.ChatOutput import ChatOutput
from langflow.components.prompts.Prompt import PromptComponent
from langflow.graph.graph.base import Graph
def basic_prompting_graph(template: str | None = None):
if template is None:
template = """Answer the user as if you were a pirate.
User: {user_input}
Answer:
"""
chat_input = ChatInput()
prompt_component = PromptComponent()
prompt_component.set(
template=template,
user_input=chat_input.message_response,
)
openai_component = OpenAIModelComponent()
openai_component.set(input_value=prompt_component.build_prompt)
chat_output = ChatOutput()
chat_output.set(input_value=openai_component.text_response)
graph = Graph(start=chat_input, end=chat_output)
return graph

View file

@ -0,0 +1,48 @@
from textwrap import dedent
from langflow.components.data.URL import URLComponent
from langflow.components.helpers.ParseData import ParseDataComponent
from langflow.components.inputs.TextInput import TextInputComponent
from langflow.components.models.OpenAIModel import OpenAIModelComponent
from langflow.components.outputs.ChatOutput import ChatOutput
from langflow.components.prompts.Prompt import PromptComponent
from langflow.graph.graph.base import Graph
def blog_writer_graph(template: str | None = None):
if template is None:
template = dedent("""Reference 1:
{references}
---
{instructions}
Blog:
""")
url_component = URLComponent()
url_component.set(urls=["https://langflow.org/", "https://docs.langflow.org/"])
parse_data_component = ParseDataComponent()
parse_data_component.set(data=url_component.fetch_content)
text_input = TextInputComponent(_display_name="Instructions")
text_input.set(
input_value="Use the references above for style to write a new blog/tutorial about Langflow and AI. Suggest non-covered topics."
)
prompt_component = PromptComponent()
prompt_component.set(
template=template,
instructions=text_input.text_response,
references=parse_data_component.parse_data,
)
openai_component = OpenAIModelComponent()
openai_component.set(input_value=prompt_component.build_prompt)
chat_output = ChatOutput()
chat_output.set(input_value=openai_component.text_response)
graph = Graph(start=text_input, end=chat_output)
return graph

View file

@ -0,0 +1,98 @@
from langflow.components.agents.CrewAIAgent import CrewAIAgentComponent
from langflow.components.agents.HierarchicalCrew import HierarchicalCrewComponent
from langflow.components.helpers.HierarchicalTask import HierarchicalTaskComponent
from langflow.components.inputs.ChatInput import ChatInput
from langflow.components.models.OpenAIModel import OpenAIModelComponent
from langflow.components.outputs.ChatOutput import ChatOutput
from langflow.components.prompts.Prompt import PromptComponent
from langflow.components.tools.SearchAPI import SearchAPIComponent
from langflow.components.tools.YfinanceTool import YfinanceToolComponent
from langflow.graph.graph.base import Graph
def complex_agent_graph():
llm = OpenAIModelComponent(model_name="gpt-4o-mini")
manager_llm = OpenAIModelComponent(model_name="gpt-4o")
search_api_tool = SearchAPIComponent()
yahoo_search_tool = YfinanceToolComponent()
dynamic_agent = CrewAIAgentComponent()
chat_input = ChatInput()
role_prompt = PromptComponent(_display_name="Role Prompt")
role_prompt.set(
template="""Define a Role that could execute or answer well the user's query.
User's query: {query}
Role should be two words max. Something like "Researcher" or "Software Developer".
"""
)
goal_prompt = PromptComponent(_display_name="Goal Prompt")
goal_prompt.set(
template="""Define the Goal of this Role, given the User's Query.
User's query: {query}
Role: {role}
The goal should be concise and specific.
Goal:
""",
query=chat_input.message_response,
role=role_prompt.build_prompt,
)
backstory_prompt = PromptComponent(_display_name="Backstory Prompt")
backstory_prompt.set(
template="""Define a Backstory of this Role and Goal, given the User's Query.
User's query: {query}
Role: {role}
Goal: {goal}
The backstory should be specific and well aligned with the rest of the information.
Backstory:""",
query=chat_input.message_response,
role=role_prompt.build_prompt,
goal=goal_prompt.build_prompt,
)
dynamic_agent.set(
tools=[search_api_tool.build_tool, yahoo_search_tool.build_tool],
llm=llm.build_model,
role=role_prompt.build_prompt,
goal=goal_prompt.build_prompt,
backstory=backstory_prompt.build_prompt,
)
response_prompt = PromptComponent()
response_prompt.set(
template="""User's query:
{query}
Respond to the user with as much as information as you can about the topic. Delete if needed. If it is just a general query (e.g a greeting) you can respond them directly.""",
query=chat_input.message_response,
)
manager_agent = CrewAIAgentComponent()
manager_agent.set(
llm=manager_llm.build_model,
role="Manager",
goal="You can answer general questions from the User and may call others for help if needed.",
backstory="You are polite and helpful. You've always been a beacon of politeness.",
)
task = HierarchicalTaskComponent()
task.set(
task_description=response_prompt.build_prompt,
expected_output="Succinct response that answers the User's query.",
)
crew_component = HierarchicalCrewComponent()
crew_component.set(
tasks=task.build_task, agents=[dynamic_agent.build_output], manager_agent=manager_agent.build_output
)
chat_output = ChatOutput()
chat_output.set(input_value=crew_component.build_output)
graph = Graph(
start=chat_input,
end=chat_output,
flow_name="Sequential Tasks Agent",
description="This Agent runs tasks in a predefined sequence.",
)
return graph

View file

@ -0,0 +1,44 @@
from langflow.components.data.File import FileComponent
from langflow.components.helpers.ParseData import ParseDataComponent
from langflow.components.inputs.ChatInput import ChatInput
from langflow.components.models.OpenAIModel import OpenAIModelComponent
from langflow.components.outputs.ChatOutput import ChatOutput
from langflow.components.prompts.Prompt import PromptComponent
from langflow.graph.graph.base import Graph
def document_qa_graph(template: str | None = None):
if template is None:
template = """Answer user's questions based on the document below:
---
{Document}
---
Question:
{Question}
Answer:
"""
file_component = FileComponent()
parse_data_component = ParseDataComponent()
parse_data_component.set(data=file_component.load_file)
chat_input = ChatInput()
prompt_component = PromptComponent()
prompt_component.set(
template=template,
context=parse_data_component.parse_data,
question=chat_input.message_response,
)
openai_component = OpenAIModelComponent()
openai_component.set(input_value=prompt_component.build_prompt)
chat_output = ChatOutput()
chat_output.set(input_value=openai_component.text_response)
graph = Graph(start=chat_input, end=chat_output)
return graph

View file

@ -0,0 +1,70 @@
from langflow.components.agents.CrewAIAgent import CrewAIAgentComponent
from langflow.components.agents.HierarchicalCrew import HierarchicalCrewComponent
from langflow.components.helpers.HierarchicalTask import HierarchicalTaskComponent
from langflow.components.inputs.ChatInput import ChatInput
from langflow.components.models.OpenAIModel import OpenAIModelComponent
from langflow.components.outputs.ChatOutput import ChatOutput
from langflow.components.prompts.Prompt import PromptComponent
from langflow.components.tools.SearchAPI import SearchAPIComponent
from langflow.graph.graph.base import Graph
def hierarchical_tasks_agent_graph():
llm = OpenAIModelComponent(model_name="gpt-4o-mini")
manager_llm = OpenAIModelComponent(model_name="gpt-4o")
search_api_tool = SearchAPIComponent()
researcher_agent = CrewAIAgentComponent()
chat_input = ChatInput()
researcher_agent.set(
tools=[search_api_tool.build_tool],
llm=llm.build_model,
role="Researcher",
goal="Search for information about the User's query and answer as best as you can",
backstory="You are a reliable researcher and journalist ",
)
editor_agent = CrewAIAgentComponent()
editor_agent.set(
llm=llm.build_model,
role="Editor",
goal="Evaluate the information for misleading or biased data.",
backstory="You are a reliable researcher and journalist ",
)
response_prompt = PromptComponent()
response_prompt.set(
template="""User's query:
{query}
Respond to the user with as much as information as you can about the topic. Delete if needed. If it is just a general query (e.g a greeting) you can respond them directly.""",
query=chat_input.message_response,
)
manager_agent = CrewAIAgentComponent()
manager_agent.set(
llm=manager_llm.build_model,
role="Manager",
goal="You can answer general questions from the User and may call others for help if needed.",
backstory="You are polite and helpful. You've always been a beacon of politeness.",
)
task = HierarchicalTaskComponent()
task.set(
task_description=response_prompt.build_prompt,
expected_output="Succinct response that answers the User's query.",
)
crew_component = HierarchicalCrewComponent()
crew_component.set(
tasks=task.build_task,
agents=[researcher_agent.build_output, editor_agent.build_output],
manager_agent=manager_agent.build_output,
)
chat_output = ChatOutput()
chat_output.set(input_value=crew_component.build_output)
graph = Graph(
start=chat_input,
end=chat_output,
flow_name="Sequential Tasks Agent",
description="This Agent runs tasks in a predefined sequence.",
)
return graph

View file

@ -0,0 +1,28 @@
from langflow.components.helpers.Memory import MemoryComponent
from langflow.components.inputs.ChatInput import ChatInput
from langflow.components.models.OpenAIModel import OpenAIModelComponent
from langflow.components.outputs.ChatOutput import ChatOutput
from langflow.components.prompts.Prompt import PromptComponent
from langflow.graph import Graph
def memory_chatbot_graph(template: str | None = None):
if template is None:
template = """{context}
User: {user_message}
AI: """
memory_component = MemoryComponent()
chat_input = ChatInput()
prompt_component = PromptComponent()
prompt_component.set(
template=template, user_message=chat_input.message_response, context=memory_component.retrieve_messages_as_text
)
openai_component = OpenAIModelComponent()
openai_component.set(input_value=prompt_component.build_prompt)
chat_output = ChatOutput()
chat_output.set(input_value=openai_component.text_response)
graph = Graph(chat_input, chat_output)
return graph

View file

@ -0,0 +1,91 @@
from langflow.components.agents.CrewAIAgent import CrewAIAgentComponent
from langflow.components.agents.SequentialCrew import SequentialCrewComponent
from langflow.components.helpers.SequentialTask import SequentialTaskComponent
from langflow.components.inputs.TextInput import TextInputComponent
from langflow.components.models.OpenAIModel import OpenAIModelComponent
from langflow.components.outputs.ChatOutput import ChatOutput
from langflow.components.prompts.Prompt import PromptComponent
from langflow.components.tools.SearchAPI import SearchAPIComponent
from langflow.graph.graph.base import Graph
def sequential_tasks_agent_graph():
llm = OpenAIModelComponent()
search_api_tool = SearchAPIComponent()
researcher_agent = CrewAIAgentComponent()
text_input = TextInputComponent(_display_name="Topic")
text_input.set(input_value="Agile")
researcher_agent.set(
tools=[search_api_tool.build_tool],
llm=llm.build_model,
role="Researcher",
goal="Search Google to find information to complete the task.",
backstory="Research has always been your thing. You can quickly find things on the web because of your skills.",
)
research_task = SequentialTaskComponent()
document_prompt_component = PromptComponent()
document_prompt_component.set(
template="""Topic: {topic}
Build a document about this document.""",
topic=text_input.text_response,
)
research_task.set(
agent=researcher_agent.build_output,
task_description=document_prompt_component.build_prompt,
expected_output="Bullet points and small phrases about the research topic.",
)
editor_agent = CrewAIAgentComponent()
editor_task = SequentialTaskComponent()
revision_prompt_component = PromptComponent()
revision_prompt_component.set(
template="""Topic: {topic}
Revise this document.""",
topic=text_input.text_response,
)
editor_agent.set(
llm=llm.build_model,
role="Editor",
goal="You should edit the Information provided by the Researcher to make it more palatable and to not contain misleading information.",
backstory="You are the editor of the most reputable journal in the world.",
)
editor_task.set(
agent=editor_agent.build_output,
task_description=revision_prompt_component.build_prompt,
expected_output="Small paragraphs and bullet points with the corrected content.",
task=research_task.build_task,
)
blog_prompt_component = PromptComponent()
blog_prompt_component.set(
template="""Topic: {topic}
Build a fun blog post about this topic.""",
topic=text_input.text_response,
)
comedian_agent = CrewAIAgentComponent()
comedian_agent.set(
llm=llm.build_model,
role="Comedian",
goal="You write comedic content based on the information provided by the editor.",
backstory="Your formal occupation is Comedian-in-Chief. You write jokes, do standup comedy and write funny articles.",
)
blog_task = SequentialTaskComponent()
blog_task.set(
agent=comedian_agent.build_output,
task_description=blog_prompt_component.build_prompt,
expected_output="A small blog about the topic.",
task=editor_task.build_task,
)
sequential_crew_component = SequentialCrewComponent()
sequential_crew_component.set(tasks=blog_task.build_task)
chat_output = ChatOutput()
chat_output.set(input_value=sequential_crew_component.build_output)
graph = Graph(
start=text_input,
end=chat_output,
flow_name="Sequential Tasks Agent",
description="This Agent runs tasks in a predefined sequence.",
)
return graph

View file

@ -0,0 +1,65 @@
from textwrap import dedent
from langflow.components.data.File import FileComponent
from langflow.components.embeddings.OpenAIEmbeddings import OpenAIEmbeddingsComponent
from langflow.components.helpers.ParseData import ParseDataComponent
from langflow.components.helpers.SplitText import SplitTextComponent
from langflow.components.inputs.ChatInput import ChatInput
from langflow.components.models.OpenAIModel import OpenAIModelComponent
from langflow.components.outputs.ChatOutput import ChatOutput
from langflow.components.prompts.Prompt import PromptComponent
from langflow.components.vectorstores.AstraDB import AstraVectorStoreComponent
from langflow.graph.graph.base import Graph
def ingestion_graph():
# Ingestion Graph
file_component = FileComponent()
text_splitter = SplitTextComponent()
text_splitter.set(data_inputs=file_component.load_file)
openai_embeddings = OpenAIEmbeddingsComponent()
vector_store = AstraVectorStoreComponent()
vector_store.set(
embedding=openai_embeddings.build_embeddings,
ingest_data=text_splitter.split_text,
)
ingestion_graph = Graph(file_component, vector_store)
return ingestion_graph
def rag_graph():
# RAG Graph
openai_embeddings = OpenAIEmbeddingsComponent()
chat_input = ChatInput()
rag_vector_store = AstraVectorStoreComponent()
rag_vector_store.set(
search_input=chat_input.message_response,
embedding=openai_embeddings.build_embeddings,
)
parse_data = ParseDataComponent()
parse_data.set(data=rag_vector_store.search_documents)
prompt_component = PromptComponent()
prompt_component.set(
template=dedent("""Given the following context, answer the question.
Context:{context}
Question: {question}
Answer:"""),
context=parse_data.parse_data,
question=chat_input.message_response,
)
openai_component = OpenAIModelComponent()
openai_component.set(input_value=prompt_component.build_prompt)
chat_output = ChatOutput()
chat_output.set(input_value=openai_component.text_response)
graph = Graph(start=chat_input, end=chat_output)
return graph
def vector_store_rag_graph():
return ingestion_graph() + rag_graph()

View file

@ -1,12 +1,11 @@
import json
import os
import tempfile
from pathlib import Path
from unittest.mock import Mock, patch
import httpx
import pytest
import respx
from dictdiffer import diff
from httpx import Response
from langflow.components import data
@ -167,23 +166,21 @@ def test_directory_component_build_with_multithreading(
def test_directory_without_mocks():
directory_component = data.DirectoryComponent()
from langflow.initial_setup import setup
from langflow.initial_setup.setup import load_starter_projects
_, projects = zip(*load_starter_projects())
# the setup module has a folder where the projects are stored
# the contents of that folder are in the projects variable
# the directory component can be used to load the projects
# and we can validate if the contents are the same as the projects variable
setup_path = Path(setup.__file__).parent / "starter_projects"
directory_component.set_attributes({"path": str(setup_path), "use_multithreading": False})
results = directory_component.load_directory()
assert len(results) == len(projects)
# each result is a Data that contains the content attribute
# each are dict that are exactly the same as one of the projects
for i, result in enumerate(results):
file_dict = json.loads(result.text)
assert file_dict in projects, list(diff(file_dict, projects[i]))
with tempfile.TemporaryDirectory() as temp_dir:
with open(temp_dir + "/test.txt", "w") as f:
f.write("test")
# also add a json file
with open(temp_dir + "/test.json", "w") as f:
f.write('{"test": "test"}')
directory_component.set_attributes({"path": str(temp_dir), "use_multithreading": False})
results = directory_component.load_directory()
assert len(results) == 2
values = ["test", '{"test":"test"}']
assert all(result.text in values for result in results), [
(len(result.text), len(val)) for result, val in zip(results, values)
]
# in ../docs/docs/components there are many mdx files
# check if the directory component can load them