microsoft-sharepoint-haystack 1.0.0__tar.gz → 1.2.0__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.
Files changed (21) hide show
  1. microsoft_sharepoint_haystack-1.2.0/CHANGELOG.md +16 -0
  2. {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.2.0}/PKG-INFO +20 -4
  3. microsoft_sharepoint_haystack-1.2.0/README.md +22 -0
  4. {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.2.0}/pydoc/config_docusaurus.yml +1 -0
  5. {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.2.0}/pyproject.toml +4 -2
  6. {microsoft_sharepoint_haystack-1.0.0/src/haystack_integrations/components/retrievers → microsoft_sharepoint_haystack-1.2.0/src/haystack_integrations/common}/microsoft_sharepoint/__init__.py +0 -2
  7. {microsoft_sharepoint_haystack-1.0.0/src/haystack_integrations/components/retrievers → microsoft_sharepoint_haystack-1.2.0/src/haystack_integrations/common}/microsoft_sharepoint/errors.py +2 -2
  8. microsoft_sharepoint_haystack-1.2.0/src/haystack_integrations/common/microsoft_sharepoint/utils.py +129 -0
  9. microsoft_sharepoint_haystack-1.2.0/src/haystack_integrations/components/fetchers/microsoft_sharepoint/__init__.py +17 -0
  10. microsoft_sharepoint_haystack-1.2.0/src/haystack_integrations/components/fetchers/microsoft_sharepoint/fetcher.py +506 -0
  11. microsoft_sharepoint_haystack-1.2.0/src/haystack_integrations/components/fetchers/py.typed +0 -0
  12. microsoft_sharepoint_haystack-1.2.0/src/haystack_integrations/components/retrievers/microsoft_sharepoint/__init__.py +17 -0
  13. {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.2.0}/src/haystack_integrations/components/retrievers/microsoft_sharepoint/retriever.py +44 -96
  14. microsoft_sharepoint_haystack-1.2.0/src/haystack_integrations/components/retrievers/py.typed +0 -0
  15. microsoft_sharepoint_haystack-1.2.0/tests/test_fetcher.py +590 -0
  16. {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.2.0}/tests/test_retriever.py +96 -6
  17. microsoft_sharepoint_haystack-1.0.0/README.md +0 -6
  18. {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.2.0}/.gitignore +0 -0
  19. {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.2.0}/LICENSE.txt +0 -0
  20. {microsoft_sharepoint_haystack-1.0.0/src/haystack_integrations/components/retrievers → microsoft_sharepoint_haystack-1.2.0/src/haystack_integrations/common}/py.typed +0 -0
  21. {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.2.0}/tests/__init__.py +0 -0
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## [integrations/microsoft_sharepoint-v1.1.0] - 2026-06-25
4
+
5
+ ### 🚀 Features
6
+
7
+ - *(microsoft_sharepoint)* Add MSSharePointFetcher (#3485)
8
+
9
+
10
+ ## [integrations/microsoft_sharepoint-v1.0.0] - 2026-06-22
11
+
12
+ ### 🚀 Features
13
+
14
+ - *(microsoft_sharepoint)* Add Microsoft SharePoint retriever integration (#3429)
15
+
16
+ <!-- generated by git-cliff -->
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: microsoft-sharepoint-haystack
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: Haystack integration for microsoft_sharepoint
5
5
  Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/microsoft_sharepoint#readme
6
6
  Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
@@ -8,7 +8,7 @@ Project-URL: Source, https://github.com/deepset-ai/haystack-core-integrations/tr
8
8
  Author-email: deepset GmbH <info@deepset.ai>
9
9
  License-Expression: Apache-2.0
10
10
  License-File: LICENSE.txt
11
- Keywords: haystack,microsoft,retriever,sharepoint
11
+ Keywords: fetcher,haystack,microsoft,retriever,sharepoint
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: License :: OSI Approved :: Apache Software License
14
14
  Classifier: Programming Language :: Python
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.14
20
20
  Classifier: Programming Language :: Python :: Implementation :: CPython
21
21
  Classifier: Programming Language :: Python :: Implementation :: PyPy
22
22
  Requires-Python: >=3.10
23
- Requires-Dist: haystack-ai
23
+ Requires-Dist: haystack-ai>=2.17.0
24
24
  Requires-Dist: httpx>=0.28.0
25
25
  Requires-Dist: tenacity>=8.2.3
26
26
  Description-Content-Type: text/markdown
@@ -30,4 +30,20 @@ Description-Content-Type: text/markdown
30
30
  [![PyPI - Version](https://img.shields.io/pypi/v/microsoft-sharepoint-haystack.svg)](https://pypi.org/project/microsoft-sharepoint-haystack)
31
31
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/microsoft-sharepoint-haystack.svg)](https://pypi.org/project/microsoft-sharepoint-haystack)
32
32
 
33
+ - [Integration page](https://haystack.deepset.ai/integrations/microsoft-sharepoint)
33
34
  - [Changelog](https://github.com/deepset-ai/haystack-core-integrations/blob/main/integrations/microsoft_sharepoint/CHANGELOG.md)
35
+
36
+ ---
37
+
38
+ ## Contributing
39
+
40
+ Refer to the general [Contribution Guidelines](https://github.com/deepset-ai/haystack-core-integrations/blob/main/CONTRIBUTING.md).
41
+
42
+ The integration tests run against live Microsoft Graph and are skipped unless their
43
+ environment variables are set:
44
+
45
+ - `MS_SHAREPOINT_ACCESS_TOKEN` — a delegated Microsoft Graph access token; required by the
46
+ retriever and fetcher tests.
47
+ - `MS_SHAREPOINT_TEST_FILE_URL` — a SharePoint file URL; also required by the fetcher tests.
48
+ - `MS_GRAPH_TENANT_ID`, `MS_GRAPH_CLIENT_ID` and `MS_GRAPH_CLIENT_SECRET` — required by the
49
+ app-only retriever tests.
@@ -0,0 +1,22 @@
1
+ # microsoft-sharepoint-haystack
2
+
3
+ [![PyPI - Version](https://img.shields.io/pypi/v/microsoft-sharepoint-haystack.svg)](https://pypi.org/project/microsoft-sharepoint-haystack)
4
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/microsoft-sharepoint-haystack.svg)](https://pypi.org/project/microsoft-sharepoint-haystack)
5
+
6
+ - [Integration page](https://haystack.deepset.ai/integrations/microsoft-sharepoint)
7
+ - [Changelog](https://github.com/deepset-ai/haystack-core-integrations/blob/main/integrations/microsoft_sharepoint/CHANGELOG.md)
8
+
9
+ ---
10
+
11
+ ## Contributing
12
+
13
+ Refer to the general [Contribution Guidelines](https://github.com/deepset-ai/haystack-core-integrations/blob/main/CONTRIBUTING.md).
14
+
15
+ The integration tests run against live Microsoft Graph and are skipped unless their
16
+ environment variables are set:
17
+
18
+ - `MS_SHAREPOINT_ACCESS_TOKEN` — a delegated Microsoft Graph access token; required by the
19
+ retriever and fetcher tests.
20
+ - `MS_SHAREPOINT_TEST_FILE_URL` — a SharePoint file URL; also required by the fetcher tests.
21
+ - `MS_GRAPH_TENANT_ID`, `MS_GRAPH_CLIENT_ID` and `MS_GRAPH_CLIENT_SECRET` — required by the
22
+ app-only retriever tests.
@@ -1,6 +1,7 @@
1
1
  loaders:
2
2
  - modules:
3
3
  - haystack_integrations.components.retrievers.microsoft_sharepoint.retriever
4
+ - haystack_integrations.components.fetchers.microsoft_sharepoint.fetcher
4
5
  search_path: [../src]
5
6
  processors:
6
7
  - type: filter
@@ -14,6 +14,7 @@ keywords = [
14
14
  "microsoft",
15
15
  "sharepoint",
16
16
  "retriever",
17
+ "fetcher",
17
18
  ]
18
19
  authors = [{ name = "deepset GmbH", email = "info@deepset.ai" }]
19
20
  classifiers = [
@@ -28,7 +29,7 @@ classifiers = [
28
29
  "Programming Language :: Python :: Implementation :: CPython",
29
30
  "Programming Language :: Python :: Implementation :: PyPy",
30
31
  ]
31
- dependencies = ["haystack-ai", "httpx>=0.28.0", "tenacity>=8.2.3"]
32
+ dependencies = ["haystack-ai>=2.17.0", "httpx>=0.28.0", "tenacity>=8.2.3"]
32
33
 
33
34
  [project.urls]
34
35
  Documentation = "https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/microsoft_sharepoint#readme"
@@ -71,7 +72,7 @@ integration = 'pytest -m "integration" {args:tests}'
71
72
  all = 'pytest {args:tests}'
72
73
  unit-cov-retry = 'pytest --cov=haystack_integrations --reruns 3 --reruns-delay 30 -x -m "not integration" {args:tests}'
73
74
  integration-cov-append-retry = 'pytest --cov=haystack_integrations --cov-append --reruns 3 --reruns-delay 30 -x -m "integration" {args:tests}'
74
- types = "mypy -p haystack_integrations.components.retrievers.microsoft_sharepoint {args}"
75
+ types = "mypy -p haystack_integrations.common.microsoft_sharepoint -p haystack_integrations.components.retrievers.microsoft_sharepoint -p haystack_integrations.components.fetchers.microsoft_sharepoint {args}"
75
76
 
76
77
  [tool.mypy]
77
78
  install_types = true
@@ -132,6 +133,7 @@ ignore = [
132
133
  "PLR0912",
133
134
  "PLR0913",
134
135
  "PLR0915",
136
+ "PLR0917",
135
137
  # Allow `Any` type - used legitimately for dynamic types and SDK boundaries
136
138
  "ANN401",
137
139
  ]
@@ -2,10 +2,8 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  from .errors import SharePointConfigError, SharePointError, SharePointRequestError
5
- from .retriever import MSSharePointRetriever
6
5
 
7
6
  __all__ = [
8
- "MSSharePointRetriever",
9
7
  "SharePointConfigError",
10
8
  "SharePointError",
11
9
  "SharePointRequestError",
@@ -8,11 +8,11 @@ class SharePointError(Exception):
8
8
 
9
9
 
10
10
  class SharePointConfigError(SharePointError):
11
- """Raised when the SharePoint retriever is misconfigured."""
11
+ """Raised when a Microsoft SharePoint component is misconfigured."""
12
12
 
13
13
 
14
14
  class SharePointRequestError(SharePointError):
15
- """Raised when a Microsoft Graph search request fails."""
15
+ """Raised when a Microsoft Graph request fails."""
16
16
 
17
17
  def __init__(self, message: str, status_code: int | None = None) -> None:
18
18
  super().__init__(message)
@@ -0,0 +1,129 @@
1
+ # SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import asyncio
6
+ from typing import TypeVar
7
+
8
+ import httpx
9
+ from haystack import logging
10
+ from haystack.utils import Secret
11
+ from tenacity import (
12
+ AsyncRetrying,
13
+ RetryCallState,
14
+ Retrying,
15
+ retry_if_result,
16
+ stop_after_attempt,
17
+ wait_exponential,
18
+ )
19
+
20
+ from .errors import SharePointConfigError, SharePointRequestError
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ T = TypeVar("T")
25
+
26
+ DEFAULT_GRAPH_URL = "https://graph.microsoft.com/v1.0"
27
+ _RETRYABLE_STATUS = frozenset({429, 500, 502, 503, 504})
28
+ # Fallback backoff when a throttled response carries no `Retry-After` header: 1s, 2s, 4s, ...
29
+ _EXPONENTIAL_BACKOFF = wait_exponential()
30
+
31
+
32
+ async def _gather_tasks_with_cancel(tasks: list[asyncio.Task[T]]) -> list[T]:
33
+ """
34
+ Wait for all tasks, cancelling and draining unfinished siblings if one fails.
35
+
36
+ :param tasks: Tasks to wait for.
37
+ :returns:
38
+ The task results in input order.
39
+ """
40
+ try:
41
+ return await asyncio.gather(*tasks)
42
+ except BaseException:
43
+ for task in tasks:
44
+ task.cancel()
45
+ await asyncio.gather(*tasks, return_exceptions=True)
46
+ raise
47
+
48
+
49
+ def resolve_access_token(access_token: str | Secret) -> str:
50
+ """Return the bearer token string, resolving it if a `Secret` was passed."""
51
+ if not isinstance(access_token, Secret):
52
+ return access_token
53
+ resolved = access_token.resolve_value()
54
+ if not isinstance(resolved, str):
55
+ msg = "The access_token Secret did not resolve to a string value."
56
+ raise SharePointConfigError(msg)
57
+ return resolved
58
+
59
+
60
+ def _retry_response(retry_state: RetryCallState) -> httpx.Response | None:
61
+ """Return the `httpx.Response` recorded for a retry attempt, or `None` if the attempt raised."""
62
+ outcome = retry_state.outcome
63
+ if outcome is None or outcome.failed:
64
+ return None
65
+ return outcome.result()
66
+
67
+
68
+ def _is_retryable_response(response: httpx.Response) -> bool:
69
+ """Return whether a Microsoft Graph response should trigger a retry."""
70
+ return response.status_code in _RETRYABLE_STATUS
71
+
72
+
73
+ def _wait_with_retry_after(retry_state: RetryCallState) -> float:
74
+ """Wait strategy honoring a numeric `Retry-After` header, falling back to exponential backoff."""
75
+ response = _retry_response(retry_state)
76
+ if response is not None:
77
+ retry_after = response.headers.get("Retry-After")
78
+ if retry_after:
79
+ try:
80
+ return float(retry_after)
81
+ except ValueError:
82
+ pass
83
+
84
+ return _EXPONENTIAL_BACKOFF(retry_state)
85
+
86
+
87
+ def _last_response(retry_state: RetryCallState) -> httpx.Response:
88
+ """Return the final response once retries are exhausted, so the caller can raise a typed error."""
89
+ response = _retry_response(retry_state)
90
+ if response is None:
91
+ msg = "Microsoft Graph retries were exhausted without a recorded response."
92
+ raise SharePointRequestError(msg)
93
+ return response
94
+
95
+
96
+ def _log_retry(retry_state: RetryCallState) -> None:
97
+ """Log a warning before sleeping between Microsoft Graph retries."""
98
+ response = _retry_response(retry_state)
99
+ next_action = retry_state.next_action
100
+ if response is None or next_action is None:
101
+ return
102
+ logger.warning(
103
+ "Microsoft Graph returned status {status}; retrying in {delay}s (attempt {attempt}).",
104
+ status=response.status_code,
105
+ delay=next_action.sleep,
106
+ attempt=retry_state.attempt_number,
107
+ )
108
+
109
+
110
+ def build_retrying(max_retries: int) -> Retrying:
111
+ """Build a tenacity `Retrying` that retries Microsoft Graph requests on throttling/transient errors."""
112
+ return Retrying(
113
+ stop=stop_after_attempt(max_retries + 1),
114
+ wait=_wait_with_retry_after,
115
+ retry=retry_if_result(_is_retryable_response),
116
+ before_sleep=_log_retry,
117
+ retry_error_callback=_last_response,
118
+ )
119
+
120
+
121
+ def build_async_retrying(max_retries: int) -> AsyncRetrying:
122
+ """Async variant of `build_retrying`."""
123
+ return AsyncRetrying(
124
+ stop=stop_after_attempt(max_retries + 1),
125
+ wait=_wait_with_retry_after,
126
+ retry=retry_if_result(_is_retryable_response),
127
+ before_sleep=_log_retry,
128
+ retry_error_callback=_last_response,
129
+ )
@@ -0,0 +1,17 @@
1
+ # SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ from haystack_integrations.common.microsoft_sharepoint.errors import (
5
+ SharePointConfigError,
6
+ SharePointError,
7
+ SharePointRequestError,
8
+ )
9
+
10
+ from .fetcher import MSSharePointFetcher
11
+
12
+ __all__ = [
13
+ "MSSharePointFetcher",
14
+ "SharePointConfigError",
15
+ "SharePointError",
16
+ "SharePointRequestError",
17
+ ]