mirror of
				https://github.com/deepset-ai/haystack.git
				synced 2025-10-31 09:49:48 +00:00 
			
		
		
		
	 2c56305ed3
			
		
	
	
		2c56305ed3
		
			
		
	
	
	
	
		
			
			* correct serialization of numpy arrays and pandas dataframes * Update Documentation & Code Style * set additional json_encoders globally * Update Documentation & Code Style * add tests for non primitive return types Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
		
			
				
	
	
		
			121 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from typing import Dict, Any
 | |
| 
 | |
| import collections
 | |
| import logging
 | |
| import time
 | |
| import json
 | |
| 
 | |
| from pydantic import BaseConfig
 | |
| from fastapi import FastAPI, APIRouter
 | |
| import haystack
 | |
| from haystack import Pipeline
 | |
| from haystack.telemetry import send_event_if_public_demo
 | |
| 
 | |
| from rest_api.utils import get_app, get_pipelines
 | |
| from rest_api.config import LOG_LEVEL
 | |
| from rest_api.schema import QueryRequest, QueryResponse
 | |
| 
 | |
| 
 | |
| logging.getLogger("haystack").setLevel(LOG_LEVEL)
 | |
| logger = logging.getLogger("haystack")
 | |
| 
 | |
| 
 | |
| BaseConfig.arbitrary_types_allowed = True
 | |
| 
 | |
| 
 | |
| router = APIRouter()
 | |
| app: FastAPI = get_app()
 | |
| query_pipeline: Pipeline = get_pipelines().get("query_pipeline", None)
 | |
| concurrency_limiter = get_pipelines().get("concurrency_limiter", None)
 | |
| 
 | |
| 
 | |
| @router.get("/initialized")
 | |
| def check_status():
 | |
|     """
 | |
|     This endpoint can be used during startup to understand if the
 | |
|     server is ready to take any requests, or is still loading.
 | |
| 
 | |
|     The recommended approach is to call this endpoint with a short timeout,
 | |
|     like 500ms, and in case of no reply, consider the server busy.
 | |
|     """
 | |
|     return True
 | |
| 
 | |
| 
 | |
| @router.get("/hs_version")
 | |
| def haystack_version():
 | |
|     """
 | |
|     Get the running Haystack version.
 | |
|     """
 | |
|     return {"hs_version": haystack.__version__}
 | |
| 
 | |
| 
 | |
| @router.post("/query", response_model=QueryResponse, response_model_exclude_none=True)
 | |
| def query(request: QueryRequest):
 | |
|     """
 | |
|     This endpoint receives the question as a string and allows the requester to set
 | |
|     additional parameters that will be passed on to the Haystack pipeline.
 | |
|     """
 | |
|     with concurrency_limiter.run():
 | |
|         result = _process_request(query_pipeline, request)
 | |
|         return result
 | |
| 
 | |
| 
 | |
| @send_event_if_public_demo
 | |
| def _process_request(pipeline, request) -> Dict[str, Any]:
 | |
|     start_time = time.time()
 | |
| 
 | |
|     params = request.params or {}
 | |
| 
 | |
|     # format global, top-level filters (e.g. "params": {"filters": {"name": ["some"]}})
 | |
|     if "filters" in params.keys():
 | |
|         params["filters"] = _format_filters(params["filters"])
 | |
| 
 | |
|     # format targeted node filters (e.g. "params": {"Retriever": {"filters": {"value"}}})
 | |
|     for key in params.keys():
 | |
|         if isinstance(params[key], collections.Mapping) and "filters" in params[key].keys():
 | |
|             params[key]["filters"] = _format_filters(params[key]["filters"])
 | |
| 
 | |
|     result = pipeline.run(query=request.query, params=params, debug=request.debug)
 | |
| 
 | |
|     # Ensure answers and documents exist, even if they're empty lists
 | |
|     if not "documents" in result:
 | |
|         result["documents"] = []
 | |
|     if not "answers" in result:
 | |
|         result["answers"] = []
 | |
| 
 | |
|     logger.info(
 | |
|         json.dumps({"request": request, "response": result, "time": f"{(time.time() - start_time):.2f}"}, default=str)
 | |
|     )
 | |
|     return result
 | |
| 
 | |
| 
 | |
| def _format_filters(filters):
 | |
|     """
 | |
|     Adjust filters to compliant format:
 | |
|     Put filter values into a list and remove filters with null value.
 | |
|     """
 | |
|     new_filters = {}
 | |
|     if filters is None:
 | |
|         logger.warning(
 | |
|             f"Request with deprecated filter format ('\"filters\": null'). "
 | |
|             f"Remove empty filters from params to be compliant with future versions"
 | |
|         )
 | |
|     else:
 | |
|         for key, values in filters.items():
 | |
|             if values is None:
 | |
|                 logger.warning(
 | |
|                     f"Request with deprecated filter format ('{key}: null'). "
 | |
|                     f"Remove null values from filters to be compliant with future versions"
 | |
|                 )
 | |
|                 continue
 | |
| 
 | |
|             if not isinstance(values, list):
 | |
|                 logger.warning(
 | |
|                     f"Request with deprecated filter format ('{key}': {values}). "
 | |
|                     f"Change to '{key}':[{values}]' to be compliant with future versions"
 | |
|                 )
 | |
|                 values = [values]
 | |
| 
 | |
|             new_filters[key] = values
 | |
|     return new_filters
 |