deepsights-api 1.7.2__tar.gz → 1.7.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/CHANGELOG.md +8 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/PKG-INFO +2 -2
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/_version.py +1 -1
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/contentstore/resources/_model.py +4 -4
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/contentstore/resources/_search.py +6 -3
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/_search.py +7 -5
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/documents/documents.py +7 -5
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/pyproject.toml +3 -0
- deepsights_api-1.7.4/tests/deepsights/contentstore/test_contentstore_search_null_tolerance_unit.py +84 -0
- deepsights_api-1.7.4/tests/deepsights/documentstore/test_documentstore_search_null_tolerance_unit.py +98 -0
- deepsights_api-1.7.4/tests/deepsights/userclient/documents/test_userclient_search_null_tolerance_unit.py +87 -0
- deepsights_api-1.7.2/ds-b2b-client-product-prod.yaml +0 -8363
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/.claude/settings.local.json +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/.github/workflows/build_docs.yml +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/.github/workflows/run_pylint.yml +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/.github/workflows/run_tests.yml +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/.gitignore +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/.pre-commit-config.yaml +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/.python-version +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/.vscode/settings.json +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/AGENTS.md +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/CLAUDE.md +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/LICENSE +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/README.md +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/api/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/api/api.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/api/resource.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/api/unified_token.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/contentstore/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/contentstore/contentstore.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/contentstore/resources/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/contentstore/resources/_download.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/contentstore/resources/_url.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/contentstore/resources/news.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/contentstore/resources/secondary.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/deepsights/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/deepsights/_mip_identity.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/deepsights/deepsights.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/deepsights/resources/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/deepsights/resources/quota/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/deepsights/resources/quota/_model.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/deepsights/resources/quota/quota.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/documentstore.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/_cache.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/_delete.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/_download.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/_list.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/_load.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/_model.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/_segmenter.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/_taxonomy.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/_upload.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/documents/documents.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/taxonomies/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/taxonomies/_model.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/taxonomies/_taxon_crud.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/taxonomies/_taxon_type_crud.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/taxonomies/_taxonomy_crud.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/documentstore/resources/taxonomies/taxonomies.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/exceptions.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/_model.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/answersV2/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/answersV2/_model.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/answersV2/answerV2.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/documents/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/documents/_download.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/reports/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/reports/_model.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/reports/report.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/userclient.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/utils/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/utils/_cache.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/utils/_ranking.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/utils/_utils.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/utils/model.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/docs/.gitkeep +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/main.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/mypy.ini +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/pytest.ini +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/requirements.txt +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/scripts/precommit.sh +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/setup.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/conftest.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/contentstore/test_contentstore_unified.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/contentstore/test_news_search.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/contentstore/test_secondary_search.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/data/test_data.json +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/data/test_presentation.pdf +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/data/test_text.txt +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/api/test_api_headers_timeout.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/api/test_error_mapping.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/api/test_get_content.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/api/test_retry_logic.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/api/test_unified_token.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/contentstore/test_validation.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/documentstore/test_deprecation.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/documentstore/test_documentstore_topic_search_unit.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/identity/test_mip_identity.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/models/test_models_defaults.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/quota/test_quota.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/test_deepsights_init.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/userclient/documents/test_pages_load_unit.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/userclient/documents/test_userclient_topic_search_unit.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/userclient/test_answers_reports_unit.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/userclient/test_userclient.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/userclient/test_userclient_cache_jitter.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/utils/test_parallel.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/deepsights/utils/test_polling.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/documentstore/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/documentstore/test_document_taxonomy.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/documentstore/test_download.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/documentstore/test_lifecycle.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/documentstore/test_list.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/documentstore/test_load.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/documentstore/test_search.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/documentstore/test_search_taxonomy_filters.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/documentstore/test_taxonomy.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/helpers/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/helpers/common.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/helpers/validation.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/answersV2/test_answersV2.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/documents/__init__.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/documents/test_document_pages_load.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/documents/test_documents_list.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/documents/test_documents_load.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/documents/test_documents_search.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/documents/test_download.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/documents/test_topic_search.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/reports/test_reports.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/test_rate_limiting.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/test_userclient_refresh.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/tests/userclient/test_userclient_unified.py +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/typings/cachetools/__init__.pyi +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/typings/pydantic/__init__.pyi +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/typings/ratelimit/__init__.pyi +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/typings/requests/__init__.pyi +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/typings/requests/adapters.pyi +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/typings/requests/exceptions.pyi +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/typings/tenacity/__init__.pyi +0 -0
- {deepsights_api-1.7.2 → deepsights_api-1.7.4}/uv.lock +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v1.7.4 (09-Sep-2026)
|
|
6
|
+
|
|
7
|
+
- Treat null `context`, `search_results`, and `items` in search responses as empty result sets instead of crashing when the backend is degraded (documentstore, userclient, and contentstore search functions).
|
|
8
|
+
|
|
9
|
+
## v1.7.3 (02-Sep-2026)
|
|
10
|
+
|
|
11
|
+
- Exclude the production OpenAPI specification from source distributions.
|
|
12
|
+
|
|
5
13
|
## v1.7.2 (12-May-2026)
|
|
6
14
|
|
|
7
15
|
- Add optional content type filtering to topic search for userclient and documentstore clients.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: deepsights-api
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.4
|
|
4
4
|
Summary: Python library for the DeepSights APIs
|
|
5
5
|
Project-URL: Documentation, https://marketlogicsoftware.github.io/deepsights-api/
|
|
6
6
|
Project-URL: Repository, https://github.com/marketlogicsoftware/deepsights-api
|
|
@@ -49,9 +49,9 @@ class ContentStoreSearchResult(DeepSightsIdTitleModel):
|
|
|
49
49
|
|
|
50
50
|
Attributes:
|
|
51
51
|
|
|
52
|
-
description (str): The description of the item.
|
|
52
|
+
description (Optional[str], optional): The description of the item. Defaults to None.
|
|
53
53
|
image_url (Optional[str], optional): The URL of the item's image. Defaults to None.
|
|
54
|
-
url (str): The URL of the item.
|
|
54
|
+
url (Optional[str], optional): The URL of the item. Defaults to None.
|
|
55
55
|
language (Optional[str], optional): The language of the item. Defaults to None.
|
|
56
56
|
publication_date (Optional[datetime], optional): The publication_date of the item's publication. Defaults to None.
|
|
57
57
|
source (Optional[str], optional): The source of the item. Defaults to None.
|
|
@@ -59,9 +59,9 @@ class ContentStoreSearchResult(DeepSightsIdTitleModel):
|
|
|
59
59
|
rank (Optional[int], optional): The final rank of the item in the search results. Defaults to None.
|
|
60
60
|
"""
|
|
61
61
|
|
|
62
|
-
description: str = Field(description="The description of the item.")
|
|
62
|
+
description: Optional[str] = Field(description="The description of the item; may be None.", default=None)
|
|
63
63
|
image_url: Optional[str] = Field(description="The URL of the item's thumbnail image.", default=None)
|
|
64
|
-
url: str = Field(description="The URL of the item.")
|
|
64
|
+
url: Optional[str] = Field(description="The URL of the item; may be None.", default=None)
|
|
65
65
|
language: Optional[str] = Field(description="The language of the item; may be None.", default=None)
|
|
66
66
|
publication_date: Optional[datetime] = Field(
|
|
67
67
|
alias="published_at",
|
|
@@ -158,7 +158,8 @@ def contentstore_hybrid_search(
|
|
|
158
158
|
response = api.post("item-service/items/_hybrid-search", body=body)
|
|
159
159
|
|
|
160
160
|
# parse
|
|
161
|
-
|
|
161
|
+
# a degraded backend may return HTTP 200 with "items": null; treat as empty
|
|
162
|
+
results: List[T] = [search_result(i) for i in response.get("items") or []]
|
|
162
163
|
|
|
163
164
|
# record rank
|
|
164
165
|
for rank, result in enumerate(results):
|
|
@@ -223,7 +224,8 @@ def contentstore_vector_search(
|
|
|
223
224
|
response = api.post("item-service/items/_vector-search", body=body)
|
|
224
225
|
|
|
225
226
|
# parse
|
|
226
|
-
|
|
227
|
+
# a degraded backend may return HTTP 200 with "items": null; treat as empty
|
|
228
|
+
results: List[T] = [search_result(i) for i in response.get("items") or []]
|
|
227
229
|
|
|
228
230
|
# re-rank
|
|
229
231
|
return rerank_by_recency(results, recency_weight=recency_weight)
|
|
@@ -291,7 +293,8 @@ def contentstore_text_search(
|
|
|
291
293
|
response = api.post("item-service/items/_text-search", body=body)
|
|
292
294
|
|
|
293
295
|
# parse
|
|
294
|
-
|
|
296
|
+
# a degraded backend may return HTTP 200 with "items": null; treat as empty
|
|
297
|
+
results: List[T] = [search_result(i) for i in response.get("items") or []]
|
|
295
298
|
|
|
296
299
|
# record rank
|
|
297
300
|
for rank, result in enumerate(results):
|
|
@@ -253,8 +253,9 @@ def hybrid_search(
|
|
|
253
253
|
|
|
254
254
|
response = resource.api.post("supercharged-search-service/hybrid-searches", body=body)
|
|
255
255
|
|
|
256
|
-
# Extract the search results from the response
|
|
257
|
-
|
|
256
|
+
# Extract the search results from the response; a degraded backend may return
|
|
257
|
+
# HTTP 200 with "context": null or "search_results": null, treat both as empty
|
|
258
|
+
search_results = (response.get("context") or {}).get("search_results") or []
|
|
258
259
|
return [HybridSearchResult(**result) for result in search_results]
|
|
259
260
|
|
|
260
261
|
|
|
@@ -303,6 +304,7 @@ def topic_search(
|
|
|
303
304
|
|
|
304
305
|
response = resource.api.post("supercharged-search-service/topic-searches", body=body)
|
|
305
306
|
|
|
306
|
-
# Extract the search results from the response
|
|
307
|
-
|
|
308
|
-
|
|
307
|
+
# Extract the search results from the response; a degraded backend may return
|
|
308
|
+
# HTTP 200 with "context": null or "search_results": null, treat both as empty
|
|
309
|
+
search_results = (response.get("context") or {}).get("search_results") or []
|
|
310
|
+
return [TopicSearchResult(**result) for result in search_results if result]
|
{deepsights_api-1.7.2 → deepsights_api-1.7.4}/deepsights/userclient/resources/documents/documents.py
RENAMED
|
@@ -91,9 +91,10 @@ def topic_search(
|
|
|
91
91
|
|
|
92
92
|
response = resource.api.post("end-user-gateway-service/topic-searches", body=body)
|
|
93
93
|
|
|
94
|
-
# Extract the search results from the response
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
# Extract the search results from the response; a degraded backend may return
|
|
95
|
+
# HTTP 200 with "context": null or "search_results": null, treat both as empty
|
|
96
|
+
search_results = (response.get("context") or {}).get("search_results") or []
|
|
97
|
+
return [TopicSearchResult(**result) for result in search_results if result]
|
|
97
98
|
|
|
98
99
|
|
|
99
100
|
#################################################
|
|
@@ -125,8 +126,9 @@ def hybrid_search(resource: APIResource, query: str, extended_search: bool = Fal
|
|
|
125
126
|
# Temporarily allow 5xx responses to debug the error
|
|
126
127
|
response = resource.api.post("end-user-gateway-service/hybrid-searches", body=body)
|
|
127
128
|
|
|
128
|
-
# Extract the search results from the response
|
|
129
|
-
|
|
129
|
+
# Extract the search results from the response; a degraded backend may return
|
|
130
|
+
# HTTP 200 with "context": null or "search_results": null, treat both as empty
|
|
131
|
+
search_results = (response.get("context") or {}).get("search_results") or []
|
|
130
132
|
return [HybridSearchResult(**result) for result in search_results]
|
|
131
133
|
|
|
132
134
|
|
deepsights_api-1.7.4/tests/deepsights/contentstore/test_contentstore_search_null_tolerance_unit.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for null-tolerant parsing of Content Store search responses (no network).
|
|
3
|
+
|
|
4
|
+
A degraded backend may return HTTP 200 with "items": null (or no "items" key);
|
|
5
|
+
this must be treated as an empty result set.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, cast
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from deepsights.contentstore.resources._model import ContentStoreSearchResult
|
|
13
|
+
from deepsights.contentstore.resources._search import (
|
|
14
|
+
contentstore_hybrid_search,
|
|
15
|
+
contentstore_text_search,
|
|
16
|
+
contentstore_vector_search,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _DummyAPI:
|
|
21
|
+
def __init__(self, response):
|
|
22
|
+
self._response = response
|
|
23
|
+
|
|
24
|
+
def post(self, path, params=None, body=None, timeout=None, expected_statuscodes=None):
|
|
25
|
+
return self._response
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
_ITEM = {
|
|
29
|
+
"id": "item-1",
|
|
30
|
+
"title": "Title",
|
|
31
|
+
"description": "Description",
|
|
32
|
+
"url": "https://example.com",
|
|
33
|
+
"language": "en",
|
|
34
|
+
"publication_date": "2024-01-02T03:04:05Z",
|
|
35
|
+
"source": "Source",
|
|
36
|
+
"source_id": "src-1",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_NULL_RESPONSES = [{"items": None}, {}]
|
|
40
|
+
_NULL_IDS = ["items-null", "no-items-key"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _search_result(i: Any) -> ContentStoreSearchResult:
|
|
44
|
+
return ContentStoreSearchResult(**i)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest.mark.parametrize("response", _NULL_RESPONSES, ids=_NULL_IDS)
|
|
48
|
+
def test_hybrid_search_null_response_returns_empty(response):
|
|
49
|
+
api = cast(Any, _DummyAPI(response))
|
|
50
|
+
assert contentstore_hybrid_search(api, "query", item_type="NEWS", search_result=_search_result) == []
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.mark.parametrize("response", _NULL_RESPONSES, ids=_NULL_IDS)
|
|
54
|
+
def test_text_search_null_response_returns_empty(response):
|
|
55
|
+
api = cast(Any, _DummyAPI(response))
|
|
56
|
+
assert contentstore_text_search(api, "query", item_type="NEWS", search_result=_search_result) == []
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.parametrize("response", _NULL_RESPONSES, ids=_NULL_IDS)
|
|
60
|
+
def test_vector_search_null_response_returns_empty(response):
|
|
61
|
+
api = cast(Any, _DummyAPI(response))
|
|
62
|
+
assert contentstore_vector_search(api, [0.0] * 1536, item_type="NEWS", search_result=_search_result) == []
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_hybrid_search_parses_non_empty_payload_and_records_rank():
|
|
66
|
+
api = cast(Any, _DummyAPI({"items": [_ITEM, {**_ITEM, "id": "item-2"}]}))
|
|
67
|
+
results = contentstore_hybrid_search(api, "query", item_type="NEWS", search_result=_search_result)
|
|
68
|
+
assert [r.id for r in results] == ["item-1", "item-2"]
|
|
69
|
+
assert [r.rank for r in results] == [1, 2]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_text_search_parses_non_empty_payload_and_records_rank():
|
|
73
|
+
api = cast(Any, _DummyAPI({"items": [_ITEM]}))
|
|
74
|
+
results = contentstore_text_search(api, "query", item_type="NEWS", search_result=_search_result)
|
|
75
|
+
assert len(results) == 1
|
|
76
|
+
assert results[0].id == "item-1"
|
|
77
|
+
assert results[0].rank == 1
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_vector_search_parses_non_empty_payload():
|
|
81
|
+
api = cast(Any, _DummyAPI({"items": [_ITEM]}))
|
|
82
|
+
results = contentstore_vector_search(api, [0.0] * 1536, item_type="NEWS", search_result=_search_result)
|
|
83
|
+
assert len(results) == 1
|
|
84
|
+
assert results[0].id == "item-1"
|
deepsights_api-1.7.4/tests/deepsights/documentstore/test_documentstore_search_null_tolerance_unit.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for null-tolerant parsing of documentstore search responses (no network).
|
|
3
|
+
|
|
4
|
+
A degraded backend may return HTTP 200 with "context": null or
|
|
5
|
+
"search_results": null; both must be treated as an empty result set.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from deepsights.api.resource import APIResource
|
|
11
|
+
from deepsights.documentstore.resources.documents._search import hybrid_search, topic_search
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _DummyAPI:
|
|
15
|
+
def __init__(self, response):
|
|
16
|
+
self._response = response
|
|
17
|
+
|
|
18
|
+
def post(self, path, params=None, body=None, timeout=None, expected_statuscodes=None):
|
|
19
|
+
return self._response
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_ARTIFACT = {
|
|
23
|
+
"artifact_id": "art-1",
|
|
24
|
+
"artifact_title": "Title",
|
|
25
|
+
"artifact_summary": "Summary",
|
|
26
|
+
"artifact_source": "Source",
|
|
27
|
+
"artifact_content_type": "standalone_document",
|
|
28
|
+
"artifact_publication_date": "2024-01-02T03:04:05Z",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_HYBRID_RESULT = {
|
|
32
|
+
**_ARTIFACT,
|
|
33
|
+
"page_references": [{"id": "page-1", "external_id": None, "number": 1, "title": "P1", "text": "Text"}],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_TOPIC_RESULT = {
|
|
37
|
+
**_ARTIFACT,
|
|
38
|
+
"page_references": [
|
|
39
|
+
{
|
|
40
|
+
"id": "page-1",
|
|
41
|
+
"external_id": None,
|
|
42
|
+
"number": 1,
|
|
43
|
+
"title": "P1",
|
|
44
|
+
"text": "Text",
|
|
45
|
+
"relevance_class": "HIGH",
|
|
46
|
+
"relevance_assessment": "Good match",
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"relevance_class": "HIGH",
|
|
50
|
+
"relevance_assessment": "Good match",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.mark.parametrize(
|
|
55
|
+
"response",
|
|
56
|
+
[
|
|
57
|
+
{"context": None},
|
|
58
|
+
{"context": {"search_results": None}},
|
|
59
|
+
{"context": {}},
|
|
60
|
+
{},
|
|
61
|
+
],
|
|
62
|
+
ids=["context-null", "search-results-null", "context-empty", "no-context-key"],
|
|
63
|
+
)
|
|
64
|
+
def test_hybrid_search_null_response_returns_empty(response):
|
|
65
|
+
resource = APIResource(api=_DummyAPI(response))
|
|
66
|
+
assert hybrid_search(resource, "query") == []
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@pytest.mark.parametrize(
|
|
70
|
+
"response",
|
|
71
|
+
[
|
|
72
|
+
{"context": None},
|
|
73
|
+
{"context": {"search_results": None}},
|
|
74
|
+
{"context": {}},
|
|
75
|
+
{},
|
|
76
|
+
],
|
|
77
|
+
ids=["context-null", "search-results-null", "context-empty", "no-context-key"],
|
|
78
|
+
)
|
|
79
|
+
def test_topic_search_null_response_returns_empty(response):
|
|
80
|
+
resource = APIResource(api=_DummyAPI(response))
|
|
81
|
+
assert topic_search(resource, "query") == []
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_hybrid_search_parses_non_empty_payload():
|
|
85
|
+
resource = APIResource(api=_DummyAPI({"context": {"search_results": [_HYBRID_RESULT]}}))
|
|
86
|
+
results = hybrid_search(resource, "query")
|
|
87
|
+
assert len(results) == 1
|
|
88
|
+
assert results[0].artifact_id == "art-1"
|
|
89
|
+
assert results[0].page_references[0].id == "page-1"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_topic_search_parses_non_empty_payload_and_skips_empty_entries():
|
|
93
|
+
resource = APIResource(api=_DummyAPI({"context": {"search_results": [_TOPIC_RESULT, {}, None]}}))
|
|
94
|
+
results = topic_search(resource, "query")
|
|
95
|
+
assert len(results) == 1
|
|
96
|
+
assert results[0].artifact_id == "art-1"
|
|
97
|
+
assert results[0].relevance_class == "HIGH"
|
|
98
|
+
assert results[0].page_references[0].relevance_class == "HIGH"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for null-tolerant parsing of userclient search responses (no network).
|
|
3
|
+
|
|
4
|
+
A degraded backend may return HTTP 200 with "context": null or
|
|
5
|
+
"search_results": null; both must be treated as an empty result set.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from deepsights.api.resource import APIResource
|
|
11
|
+
from deepsights.userclient.resources.documents.documents import hybrid_search, topic_search
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _DummyAPI:
|
|
15
|
+
def __init__(self, response):
|
|
16
|
+
self._response = response
|
|
17
|
+
|
|
18
|
+
def post(self, path, params=None, body=None, timeout=None, expected_statuscodes=None):
|
|
19
|
+
return self._response
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_ARTIFACT = {
|
|
23
|
+
"artifact_id": "art-1",
|
|
24
|
+
"artifact_title": "Title",
|
|
25
|
+
"artifact_summary": "Summary",
|
|
26
|
+
"artifact_source": "Source",
|
|
27
|
+
"artifact_content_type": "standalone_document",
|
|
28
|
+
"artifact_publication_date": "2024-01-02T03:04:05Z",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_HYBRID_RESULT = {
|
|
32
|
+
**_ARTIFACT,
|
|
33
|
+
"page_references": [{"id": "page-1", "external_id": None, "number": 1, "title": "P1", "text": "Text"}],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_TOPIC_RESULT = {
|
|
37
|
+
**_ARTIFACT,
|
|
38
|
+
"page_references": [
|
|
39
|
+
{
|
|
40
|
+
"id": "page-1",
|
|
41
|
+
"external_id": None,
|
|
42
|
+
"number": 1,
|
|
43
|
+
"title": "P1",
|
|
44
|
+
"text": "Text",
|
|
45
|
+
"relevance_class": "HIGH",
|
|
46
|
+
"relevance_assessment": "Good match",
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"relevance_class": "HIGH",
|
|
50
|
+
"relevance_assessment": "Good match",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_NULL_RESPONSES = [
|
|
54
|
+
{"context": None},
|
|
55
|
+
{"context": {"search_results": None}},
|
|
56
|
+
{"context": {}},
|
|
57
|
+
{},
|
|
58
|
+
]
|
|
59
|
+
_NULL_IDS = ["context-null", "search-results-null", "context-empty", "no-context-key"]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@pytest.mark.parametrize("response", _NULL_RESPONSES, ids=_NULL_IDS)
|
|
63
|
+
def test_hybrid_search_null_response_returns_empty(response):
|
|
64
|
+
resource = APIResource(api=_DummyAPI(response))
|
|
65
|
+
assert hybrid_search(resource, "query") == []
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.mark.parametrize("response", _NULL_RESPONSES, ids=_NULL_IDS)
|
|
69
|
+
def test_topic_search_null_response_returns_empty(response):
|
|
70
|
+
resource = APIResource(api=_DummyAPI(response))
|
|
71
|
+
assert topic_search(resource, "query") == []
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_hybrid_search_parses_non_empty_payload():
|
|
75
|
+
resource = APIResource(api=_DummyAPI({"context": {"search_results": [_HYBRID_RESULT]}}))
|
|
76
|
+
results = hybrid_search(resource, "query")
|
|
77
|
+
assert len(results) == 1
|
|
78
|
+
assert results[0].artifact_id == "art-1"
|
|
79
|
+
assert results[0].page_references[0].id == "page-1"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_topic_search_parses_non_empty_payload_and_skips_empty_entries():
|
|
83
|
+
resource = APIResource(api=_DummyAPI({"context": {"search_results": [_TOPIC_RESULT, {}, None]}}))
|
|
84
|
+
results = topic_search(resource, "query")
|
|
85
|
+
assert len(results) == 1
|
|
86
|
+
assert results[0].artifact_id == "art-1"
|
|
87
|
+
assert results[0].relevance_class == "HIGH"
|