custom-components.mdx
1 --- 2 title: "Creating Custom Components" 3 id: custom-components 4 slug: "/custom-components" 5 description: "Create your own components and use them standalone or in pipelines." 6 --- 7 8 # Creating Custom Components 9 10 Create your own components and use them standalone or in pipelines. 11 12 With Haystack, you can easily create any custom components for various tasks, from filtering results to integrating with external software. You can then insert, reuse, and share these components within Haystack or even with an external audience by packaging them and submitting them to [Haystack Integrations](../integrations.mdx)! 13 14 ## Requirements 15 16 Here are the requirements for all custom components: 17 18 - `@component`: This decorator marks a class as a component, allowing it to be used in a pipeline. 19 - `run()`: This is a required method in every component. It accepts input arguments and returns a `dict`. The inputs can either come from the pipeline when it’s executed, or from the output of another component when connected using `connect()`. The `run()` method should be compatible with the input/output definitions declared for the component. See an [Extended Example](#extended-example) below to check how it works. 20 21 22 :::note[Avoid in-place input mutation] 23 24 When building custom components, do not change the component's inputs directly. Instead, work on a copy or a new version of the input, modify that, and return it. The reason for this is that the original input values might be reused by other components or by later pipeline steps. Mutating the input directly can lead to unintended side effects and bugs in the pipeline, as other components might rely on the original input values. 25 26 When only one or a few fields of the input need to be changed (for example, `meta` on a `Document`), use `dataclasses.replace()` to create a new instance with the updated fields. This is simpler and more efficient than deep-copying the whole object: 27 28 ```python 29 from dataclasses import replace 30 31 32 def run(self, documents): 33 updated = [replace(doc, meta={**doc.meta, "processed": True}) for doc in documents] 34 return {"documents": updated} 35 ``` 36 37 When you need to modify nested mutable structures, for example `list` or `dict` attributes, or update many fields of the dataclass instance, use a full deep copy instead: 38 39 ```python 40 import copy 41 42 43 def run(self, documents): 44 documents_copy = copy.deepcopy(documents) 45 # mutate documents_copy safely here 46 return {"documents": documents_copy} 47 ``` 48 49 ::: 50 51 52 ### Inputs and Outputs 53 54 Next, define the inputs and outputs for your component. 55 56 #### Inputs 57 58 You can choose between three input options: 59 60 - `set_input_type`: This method defines or updates a single input socket for a component instance. It’s ideal for adding or modifying a specific input at runtime without affecting others. Use this when you need to dynamically set or modify a single input based on specific conditions. 61 - `set_input_types`: This method allows you to define multiple input sockets at once, replacing any existing inputs. It’s useful when you know all the inputs the component will need and want to configure them in bulk. Use this when you want to define multiple inputs during initialization. 62 - Declaring arguments directly in the `run()` method. Use this method when the component’s inputs are static and known at the time of class definition. 63 64 #### Outputs 65 66 You can choose between two output options: 67 68 - `@component.output_types`: This decorator defines the output types and names at the time of class definition. The output names and types must match the `dict` returned by the `run()` method. Use this when the output types are static and known in advance. This decorator is cleaner and more readable for static components. 69 - `set_output_types`: This method defines or updates multiple output sockets for a component instance at runtime. It’s useful when you need flexibility in configuring outputs dynamically. Use this when the output types need to be set at runtime for greater flexibility. 70 71 ## Short Example 72 73 Here is an example of a simple minimal component setup: 74 75 ```python 76 from haystack import component 77 78 79 @component 80 class WelcomeTextGenerator: 81 """ 82 A component generating personal welcome message and making it upper case 83 """ 84 85 @component.output_types(welcome_text=str, note=str) 86 def run(self, name: str): 87 return { 88 "welcome_text": f"Hello {name}, welcome to Haystack!".upper(), 89 "note": "welcome message is ready", 90 } 91 ``` 92 93 Here, the custom component `WelcomeTextGenerator` accepts one input: `name` string and returns two outputs: `welcome_text` and `note`. 94 95 ## Extended Example 96 97 Check out an example below on how to create two custom components and connect them in a Haystack pipeline. 98 99 ```python 100 # import necessary dependencies 101 from haystack import component, Pipeline 102 103 104 # Create two custom components. Note the mandatory @component decorator and @component.output_types, as well as the mandatory run method. 105 @component 106 class WelcomeTextGenerator: 107 """ 108 A component generating personal welcome message and making it upper case 109 """ 110 111 @component.output_types(welcome_text=str, note=str) 112 def run(self, name: str): 113 return { 114 "welcome_text": ( 115 "Hello {name}, welcome to Haystack!".format(name=name) 116 ).upper(), 117 "note": "welcome message is ready", 118 } 119 120 121 @component 122 class WhitespaceSplitter: 123 """ 124 A component for splitting the text by whitespace 125 """ 126 127 @component.output_types(split_text=list[str]) 128 def run(self, text: str): 129 return {"split_text": text.split()} 130 131 132 # create a pipeline and add the custom components to it 133 text_pipeline = Pipeline() 134 text_pipeline.add_component( 135 name="welcome_text_generator", 136 instance=WelcomeTextGenerator(), 137 ) 138 text_pipeline.add_component(name="splitter", instance=WhitespaceSplitter()) 139 140 # connect the components 141 text_pipeline.connect( 142 sender="welcome_text_generator.welcome_text", 143 receiver="splitter.text", 144 ) 145 146 # define the result and run the pipeline 147 result = text_pipeline.run({"welcome_text_generator": {"name": "Bilge"}}) 148 149 print(result["splitter"]["split_text"]) 150 ``` 151 152 ## Extending the Existing Components 153 154 To extend already existing components in Haystack, subclass an existing component and use the `@component` decorator to mark it. Override or extend the `run()` method to process inputs and outputs. Call `super()` with the derived class name from the init of the derived class to avoid initialization issues: 155 156 ```python 157 class DerivedComponent(BaseComponent): 158 def __init__(self): 159 super(DerivedComponent, self).__init__() 160 161 162 ## ... 163 164 dc = DerivedComponent() # ok 165 ``` 166 167 An example of an extended component is Haystack's [FaithfulnessEvaluator](https://github.com/deepset-ai/haystack/blob/e5a80722c22c59eb99416bf0cd712f6de7cd581a/haystack/components/evaluators/faithfulness.py) derived from LLMEvaluator. 168 169 ## Project Template 170 171 If you're building a custom component that you want to package and share, we provide a [GitHub template repository](https://github.com/deepset-ai/custom-component) that gives you a ready-made project structure. It includes the boilerplate for packaging, testing, and distributing your custom component as a standalone Python package. Use it to quickly scaffold a new integration or reusable component without setting up the project from scratch. 172 173 Check out the [video walkthrough](https://www.youtube.com/watch?v=SWC0QecAMcI) for a step-by-step guide on how to use the template. 174 175 ## Additional References 176 177 🧑🍳 Cookbooks: 178 179 - [Build quizzes and adventures with Character Codex and llamafile](https://haystack.deepset.ai/cookbook/charactercodex_llamafile/) 180 - [Run tasks concurrently within a custom component](https://haystack.deepset.ai/cookbook/concurrent_tasks/) 181 - [Chat With Your SQL Database](https://haystack.deepset.ai/cookbook/chat_with_sql_3_ways/) 182 - [Hacker News Summaries with Custom Components](https://haystack.deepset.ai/cookbook/hackernews-custom-component-rag/)