From d313137f8c177f1312f842c4915f10e49594f104 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 16 Aug 2024 16:47:19 -0300 Subject: [PATCH] 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. --- .../base/langflow/initial_setup/load.py | 25 +++++ .../starter_projects/__init__.py | 19 ++++ .../starter_projects/basic_prompting.py | 30 ++++++ .../starter_projects/blog_writer.py | 48 +++++++++ .../starter_projects/complex_agent.py | 98 +++++++++++++++++++ .../starter_projects/document_qa.py | 44 +++++++++ .../hierarchical_tasks_agent.py | 70 +++++++++++++ .../starter_projects/memory_chatbot.py | 28 ++++++ .../sequential_tasks_agent.py | 91 +++++++++++++++++ .../starter_projects/vector_store_rag.py | 65 ++++++++++++ .../tests/unit/test_data_components.py | 33 +++---- 11 files changed, 533 insertions(+), 18 deletions(-) create mode 100644 src/backend/base/langflow/initial_setup/load.py create mode 100644 src/backend/base/langflow/initial_setup/starter_projects/__init__.py create mode 100644 src/backend/base/langflow/initial_setup/starter_projects/basic_prompting.py create mode 100644 src/backend/base/langflow/initial_setup/starter_projects/blog_writer.py create mode 100644 src/backend/base/langflow/initial_setup/starter_projects/complex_agent.py create mode 100644 src/backend/base/langflow/initial_setup/starter_projects/document_qa.py create mode 100644 src/backend/base/langflow/initial_setup/starter_projects/hierarchical_tasks_agent.py create mode 100644 src/backend/base/langflow/initial_setup/starter_projects/memory_chatbot.py create mode 100644 src/backend/base/langflow/initial_setup/starter_projects/sequential_tasks_agent.py create mode 100644 src/backend/base/langflow/initial_setup/starter_projects/vector_store_rag.py diff --git a/src/backend/base/langflow/initial_setup/load.py b/src/backend/base/langflow/initial_setup/load.py new file mode 100644 index 000000000..081453f56 --- /dev/null +++ b/src/backend/base/langflow/initial_setup/load.py @@ -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()] diff --git a/src/backend/base/langflow/initial_setup/starter_projects/__init__.py b/src/backend/base/langflow/initial_setup/starter_projects/__init__.py new file mode 100644 index 000000000..b8770da14 --- /dev/null +++ b/src/backend/base/langflow/initial_setup/starter_projects/__init__.py @@ -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", +] diff --git a/src/backend/base/langflow/initial_setup/starter_projects/basic_prompting.py b/src/backend/base/langflow/initial_setup/starter_projects/basic_prompting.py new file mode 100644 index 000000000..0f0f81ba7 --- /dev/null +++ b/src/backend/base/langflow/initial_setup/starter_projects/basic_prompting.py @@ -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 diff --git a/src/backend/base/langflow/initial_setup/starter_projects/blog_writer.py b/src/backend/base/langflow/initial_setup/starter_projects/blog_writer.py new file mode 100644 index 000000000..9396ee651 --- /dev/null +++ b/src/backend/base/langflow/initial_setup/starter_projects/blog_writer.py @@ -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 diff --git a/src/backend/base/langflow/initial_setup/starter_projects/complex_agent.py b/src/backend/base/langflow/initial_setup/starter_projects/complex_agent.py new file mode 100644 index 000000000..21dfc2ea9 --- /dev/null +++ b/src/backend/base/langflow/initial_setup/starter_projects/complex_agent.py @@ -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 diff --git a/src/backend/base/langflow/initial_setup/starter_projects/document_qa.py b/src/backend/base/langflow/initial_setup/starter_projects/document_qa.py new file mode 100644 index 000000000..b1d98a182 --- /dev/null +++ b/src/backend/base/langflow/initial_setup/starter_projects/document_qa.py @@ -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 diff --git a/src/backend/base/langflow/initial_setup/starter_projects/hierarchical_tasks_agent.py b/src/backend/base/langflow/initial_setup/starter_projects/hierarchical_tasks_agent.py new file mode 100644 index 000000000..9b00800e5 --- /dev/null +++ b/src/backend/base/langflow/initial_setup/starter_projects/hierarchical_tasks_agent.py @@ -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 diff --git a/src/backend/base/langflow/initial_setup/starter_projects/memory_chatbot.py b/src/backend/base/langflow/initial_setup/starter_projects/memory_chatbot.py new file mode 100644 index 000000000..1b0f44b9d --- /dev/null +++ b/src/backend/base/langflow/initial_setup/starter_projects/memory_chatbot.py @@ -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 diff --git a/src/backend/base/langflow/initial_setup/starter_projects/sequential_tasks_agent.py b/src/backend/base/langflow/initial_setup/starter_projects/sequential_tasks_agent.py new file mode 100644 index 000000000..6e340259d --- /dev/null +++ b/src/backend/base/langflow/initial_setup/starter_projects/sequential_tasks_agent.py @@ -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 diff --git a/src/backend/base/langflow/initial_setup/starter_projects/vector_store_rag.py b/src/backend/base/langflow/initial_setup/starter_projects/vector_store_rag.py new file mode 100644 index 000000000..b8679ad21 --- /dev/null +++ b/src/backend/base/langflow/initial_setup/starter_projects/vector_store_rag.py @@ -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() diff --git a/src/backend/tests/unit/test_data_components.py b/src/backend/tests/unit/test_data_components.py index 89dbccb47..f6938c5d9 100644 --- a/src/backend/tests/unit/test_data_components.py +++ b/src/backend/tests/unit/test_data_components.py @@ -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