diff --git a/docs/_src/api/openapi/openapi.json b/docs/_src/api/openapi/openapi.json index bba347894..b5a819b7b 100644 --- a/docs/_src/api/openapi/openapi.json +++ b/docs/_src/api/openapi/openapi.json @@ -755,8 +755,7 @@ "QueryResponse": { "title": "QueryResponse", "required": [ - "query", - "answers" + "query" ], "type": "object", "properties": { @@ -769,14 +768,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/AnswerSerialized" - } + }, + "default": [] }, "documents": { "title": "Documents", "type": "array", "items": { "$ref": "#/components/schemas/DocumentSerialized" - } + }, + "default": [] }, "_debug": { "title": " Debug", diff --git a/rest_api/controller/search.py b/rest_api/controller/search.py index b87a0a463..29a41ffae 100644 --- a/rest_api/controller/search.py +++ b/rest_api/controller/search.py @@ -1,3 +1,5 @@ +from typing import Dict, Any + import logging import time import json @@ -65,7 +67,7 @@ def query(request: QueryRequest): return result -def _process_request(pipeline, request) -> QueryResponse: +def _process_request(pipeline, request) -> Dict[str, Any]: start_time = time.time() params = request.params or {} @@ -75,22 +77,26 @@ def _process_request(pipeline, request) -> QueryResponse: params["filters"] = _format_filters(params["filters"]) # format targeted node filters (e.g. "params": {"Retriever": {"filters": {"value"}}}) - for key, value in params.items(): + for key in params.keys(): if "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"] = [] + # if any of the documents contains an embedding as an ndarray the latter needs to be converted to list of float - for document in result["documents"] or []: + for document in result["documents"]: if isinstance(document.embedding, ndarray): document.embedding = document.embedding.tolist() - end_time = time.time() logger.info( - json.dumps({"request": request, "response": result, "time": f"{(end_time - start_time):.2f}"}, default=str) + json.dumps({"request": request, "response": result, "time": f"{(time.time() - start_time):.2f}"}, default=str) ) - return result diff --git a/rest_api/schema.py b/rest_api/schema.py index 27c63aa60..d56f10f1d 100644 --- a/rest_api/schema.py +++ b/rest_api/schema.py @@ -66,6 +66,6 @@ class CreateLabelSerialized(BaseModel): class QueryResponse(BaseModel): query: str - answers: List[AnswerSerialized] - documents: Optional[List[DocumentSerialized]] + answers: List[AnswerSerialized] = [] + documents: List[DocumentSerialized] = [] debug: Optional[Dict] = Field(None, alias="_debug") diff --git a/rest_api/test/test_rest_api.py b/rest_api/test/test_rest_api.py index 722d1db37..d319d7cd2 100644 --- a/rest_api/test/test_rest_api.py +++ b/rest_api/test/test_rest_api.py @@ -225,6 +225,23 @@ def test_query_with_invalid_filter(populated_client: TestClient): assert len(response_json["answers"]) == 0 +def test_query_with_no_documents_and_no_answers(): + os.environ["PIPELINE_YAML_PATH"] = str( + (Path(__file__).parent / "samples" / "pipeline" / "test_pipeline.yaml").absolute() + ) + os.environ["INDEXING_PIPELINE_NAME"] = "indexing_text_pipeline" + client = TestClient(app) + + # Clean up to make sure the docstore is empty + client.post(url="/documents/delete_by_filters", data='{"filters": {}}') + query = {"query": "Who made the PDF specification?"} + response = client.post(url="/query", json=query) + assert 200 == response.status_code + response_json = response.json() + assert response_json["documents"] == [] + assert response_json["answers"] == [] + + def test_write_feedback(populated_client: TestClient): response = populated_client.post(url="/feedback", json=FEEDBACK) assert 200 == response.status_code