--- title: "Hayhooks" id: hayhooks slug: "/hayhooks" description: "Hayhooks is a web application you can use to serve Haystack pipelines through HTTP endpoints. This page provides an overview of the main features of Hayhooks." --- # Hayhooks Hayhooks is a web application you can use to serve Haystack pipelines through HTTP endpoints. This page provides an overview of the main features of Hayhooks. :::note Hayhooks GitHub You can find the code and an in-depth explanation of the features in the [Hayhooks GitHub repository](https://github.com/deepset-ai/hayhooks). ::: ## Overview Hayhooks simplifies the deployment of Haystack pipelines as REST APIs. It allows you to: - Expose Haystack pipelines as HTTP endpoints, including OpenAI-compatible chat endpoints, - Customize logic while keeping minimal boilerplate, - Deploy pipelines quickly and efficiently. ### Installation Install Hayhooks using pip: ```shell pip install hayhooks ``` The `hayhooks` package ships both the server and the client component, and the client is capable of starting the server. From a shell, start the server with: ```shell $ hayhooks run INFO: Started server process [44782] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://localhost:1416 (Press CTRL+C to quit) ``` ### Check Status From a different shell, you can query the status of the server with: ```shell $ hayhooks status Hayhooks server is up and running. ``` ## Configuration Hayhooks can be configured in three ways: 1. Using an `.env` file in the project root. 2. Passing environment variables when running the command. 3. Using command-line arguments with `hayhooks run`. ### Environment Variables | Variable | Description | | --------------------------------- | ---------------------------------- | | `HAYHOOKS_HOST` | Host address for the server | | `HAYHOOKS_PORT` | Port for the server | | `HAYHOOKS_PIPELINES_DIR` | Directory containing pipelines | | `HAYHOOKS_ROOT_PATH` | Root path of the server | | `HAYHOOKS_ADDITIONAL_PYTHON_PATH` | Additional Python paths to include | | `HAYHOOKS_DISABLE_SSL` | Disable SSL verification (boolean) | | `HAYHOOKS_SHOW_TRACEBACKS` | Show error tracebacks (boolean) | ### CORS Settings | Variable | Description | | ---------------------------------- | --------------------------------------------------- | | `HAYHOOKS_CORS_ALLOW_ORIGINS` | List of allowed origins (default: `[*]`) | | `HAYHOOKS_CORS_ALLOW_METHODS` | List of allowed HTTP methods (default: `[*]`) | | `HAYHOOKS_CORS_ALLOW_HEADERS` | List of allowed headers (default: `[*]`) | | `HAYHOOKS_CORS_ALLOW_CREDENTIALS` | Allow credentials (default: `false`) | | `HAYHOOKS_CORS_ALLOW_ORIGIN_REGEX` | Regex pattern for allowed origins (default: `null`) | | `HAYHOOKS_CORS_EXPOSE_HEADERS` | Headers to expose in response (default: `[]`) | | `HAYHOOKS_CORS_MAX_AGE` | Max age for preflight responses (default: `600`) | ## Running Hayhooks To start the server: ```shell hayhooks run ``` This will launch Hayhooks at `HAYHOOKS_HOST:HAYHOOKS_PORT`. ## Deploying a Pipeline ### Steps 1. Prepare a pipeline definition (`.yml` file) and a `pipeline_wrapper.py` file. 2. Deploy the pipeline: ```shell hayhooks pipeline deploy-files -n my_pipeline my_pipeline_dir ``` 3. Access the pipeline at `{pipeline_name}/run` endpoint. ### Pipeline Wrapper A `PipelineWrapper` class is required to wrap the pipeline: ```python from pathlib import Path from haystack import Pipeline from hayhooks import BasePipelineWrapper class PipelineWrapper(BasePipelineWrapper): def setup(self) -> None: pipeline_yaml = (Path(__file__).parent / "pipeline.yml").read_text() self.pipeline = Pipeline.loads(pipeline_yaml) def run_api(self, input_text: str) -> str: result = self.pipeline.run({"input": {"text": input_text}}) return result["output"]["text"] ``` ## File Uploads Hayhooks enables handling file uploads in your pipeline wrapper’s `run_api` method by including `files: Optional[List[UploadFile]] = None` as an argument. ```python def run_api(self, files: Optional[List[UploadFile]] = None) -> str: if files and len(files) > 0: filenames = [f.filename for f in files if f.filename is not None] file_contents = [f.file.read() for f in files] return f"Received files: {', '.join(filenames)}" return "No files received" ``` Hayhooks automatically processes uploaded files and passes them to the `run_api` method when present. The HTTP request must be a `multipart/form-data` request. ### Combining Files and Parameters Hayhooks also supports handling both files and additional parameters in the same request by including them as arguments in `run_api`: ```python def run_api(self, files: Optional[List[UploadFile]] = None, additional_param: str = "default") -> str: ... ``` ## Running Pipelines from the CLI ### With JSON-Compatible Parameters You can execute a pipeline through the command line using the `hayhooks pipeline run` command. Internally, this triggers the `run_api` method of the pipeline wrapper, passing parameters as a JSON payload. This method is ideal for testing deployed pipelines from the CLI without writing additional code. ```shell hayhooks pipeline run --param 'question="Is this recipe vegan?"' ``` ### With File Uploads To execute a pipeline that requires a file input, use a `multipart/form-data` request. You can submit both files and parameters in the same request. Ensure the deployed pipeline supports file handling. ```shell ## Upload a directory hayhooks pipeline run --dir files_to_index ## Upload a single file hayhooks pipeline run --file file.pdf ## Upload multiple files hayhooks pipeline run --dir files_to_index --file file1.pdf --file file2.pdf ## Upload a file with an additional parameter hayhooks pipeline run --file file.pdf --param 'question="Is this recipe vegan?"' ``` ## MCP Support ### MCP Server Hayhooks supports the Model Context Protocol (MCP) and can act as an MCP Server. It automatically lists your deployed pipelines as MCP Tools using Server-Sent Events (SSE) as the transport method. To start the Hayhooks MCP server, run: ```shell hayhooks mcp run ``` This starts the server at `HAYHOOKS_MCP_HOST:HAYHOOKS_MCP_PORT`. ### Creating a PipelineWrapper To expose a Haystack pipeline as an MCP Tool, you need a `PipelineWrapper` with the following properties: - **name**: The tool's name - **description**: The tool's description - **inputSchema**: A JSON Schema object for the tool's input parameters For each deployed pipeline, Hayhooks will: 1. Use the pipeline wrapper name as the MCP Tool name, 2. Use the `run_api` method's docstring as the MCP Tool description (if present), 3. Generate a Pydantic model from the `run_api` method arguments. #### PipelineWrapper Example ```python from pathlib import Path from typing import List from haystack import Pipeline from hayhooks import BasePipelineWrapper class PipelineWrapper(BasePipelineWrapper): def setup(self) -> None: pipeline_yaml = (Path(__file__).parent / "chat_with_website.yml").read_text() self.pipeline = Pipeline.loads(pipeline_yaml) def run_api(self, urls: List[str], question: str) -> str: """ Ask a question about one or more websites using a Haystack pipeline. """ result = self.pipeline.run({"fetcher": {"urls": urls}, "prompt": {"query": question}}) return result["llm"]["replies"][0] ``` ### Skipping MCP Tool Listing To deploy a pipeline without listing it as an MCP Tool, set `skip_mcp = True` in your class: ```python class PipelineWrapper(BasePipelineWrapper): # This will skip the MCP Tool listing skip_mcp = True def setup(self) -> None: ... def run_api(self, urls: List[str], question: str) -> str: ... ``` ## OpenAI Compatibility Hayhooks supports OpenAI-compatible endpoints through the `run_chat_completion` method. ```python from hayhooks import BasePipelineWrapper, get_last_user_message class PipelineWrapper(BasePipelineWrapper): def run_chat_completion(self, model: str, messages: list, body: dict): question = get_last_user_message(messages) return self.pipeline.run({"query": question}) ``` ### Streaming Responses Hayhooks provides a `streaming_generator` utility to stream pipeline output to the client: ```python from hayhooks import streaming_generator def run_chat_completion(self, model: str, messages: list, body: dict): question = get_last_user_message(messages) return streaming_generator(pipeline=self.pipeline, pipeline_run_args={"query": question}) ``` ## Running Programmatically Hayhooks can be embedded in a FastAPI application: ```python import uvicorn from hayhooks.settings import settings from fastapi import Request from hayhooks import create_app ## Create the Hayhooks app hayhooks = create_app() ## Add a custom route @hayhooks.get("/custom") async def custom_route(): return {"message": "Hi, this is a custom route!"} ## Add a custom middleware @hayhooks.middleware("http") async def custom_middleware(request: Request, call_next): response = await call_next(request) response.headers["X-Custom-Header"] = "custom-header-value" return response if __name__ == "__main__": uvicorn.run("app:hayhooks", host=settings.host, port=settings.port) ```