microsoft-sharepoint-haystack 1.0.0__tar.gz → 1.1.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.
- microsoft_sharepoint_haystack-1.1.0/CHANGELOG.md +9 -0
- {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.1.0}/PKG-INFO +14 -3
- {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.1.0}/README.md +11 -0
- {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.1.0}/pydoc/config_docusaurus.yml +1 -0
- {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.1.0}/pyproject.toml +3 -2
- {microsoft_sharepoint_haystack-1.0.0/src/haystack_integrations/components/retrievers → microsoft_sharepoint_haystack-1.1.0/src/haystack_integrations/common}/microsoft_sharepoint/__init__.py +0 -2
- {microsoft_sharepoint_haystack-1.0.0/src/haystack_integrations/components/retrievers → microsoft_sharepoint_haystack-1.1.0/src/haystack_integrations/common}/microsoft_sharepoint/errors.py +2 -2
- microsoft_sharepoint_haystack-1.1.0/src/haystack_integrations/common/microsoft_sharepoint/utils.py +107 -0
- microsoft_sharepoint_haystack-1.1.0/src/haystack_integrations/components/fetchers/microsoft_sharepoint/__init__.py +17 -0
- microsoft_sharepoint_haystack-1.1.0/src/haystack_integrations/components/fetchers/microsoft_sharepoint/fetcher.py +504 -0
- microsoft_sharepoint_haystack-1.1.0/src/haystack_integrations/components/fetchers/py.typed +0 -0
- microsoft_sharepoint_haystack-1.1.0/src/haystack_integrations/components/retrievers/microsoft_sharepoint/__init__.py +17 -0
- {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.1.0}/src/haystack_integrations/components/retrievers/microsoft_sharepoint/retriever.py +25 -86
- microsoft_sharepoint_haystack-1.1.0/src/haystack_integrations/components/retrievers/py.typed +0 -0
- microsoft_sharepoint_haystack-1.1.0/tests/test_fetcher.py +535 -0
- {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.1.0}/tests/test_retriever.py +25 -6
- {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.1.0}/.gitignore +0 -0
- {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.1.0}/LICENSE.txt +0 -0
- {microsoft_sharepoint_haystack-1.0.0/src/haystack_integrations/components/retrievers → microsoft_sharepoint_haystack-1.1.0/src/haystack_integrations/common}/py.typed +0 -0
- {microsoft_sharepoint_haystack-1.0.0 → microsoft_sharepoint_haystack-1.1.0}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microsoft-sharepoint-haystack
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.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
|
|
@@ -31,3 +31,14 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
[](https://pypi.org/project/microsoft-sharepoint-haystack)
|
|
32
32
|
|
|
33
33
|
- [Changelog](https://github.com/deepset-ai/haystack-core-integrations/blob/main/integrations/microsoft_sharepoint/CHANGELOG.md)
|
|
34
|
+
|
|
35
|
+
## Running the tests
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
hatch run test:unit # offline, no credentials needed
|
|
39
|
+
hatch run test:integration # hits live Microsoft Graph (see below)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The integration tests (the `TestLive` classes in `tests/`) exercise the retriever and the fetcher
|
|
43
|
+
against live Microsoft Graph. They **skip themselves** when their inputs are not set, so the default
|
|
44
|
+
local and CI runs stay offline. To run them you need a delegated Microsoft Graph access token.
|
|
@@ -4,3 +4,14 @@
|
|
|
4
4
|
[](https://pypi.org/project/microsoft-sharepoint-haystack)
|
|
5
5
|
|
|
6
6
|
- [Changelog](https://github.com/deepset-ai/haystack-core-integrations/blob/main/integrations/microsoft_sharepoint/CHANGELOG.md)
|
|
7
|
+
|
|
8
|
+
## Running the tests
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
hatch run test:unit # offline, no credentials needed
|
|
12
|
+
hatch run test:integration # hits live Microsoft Graph (see below)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The integration tests (the `TestLive` classes in `tests/`) exercise the retriever and the fetcher
|
|
16
|
+
against live Microsoft Graph. They **skip themselves** when their inputs are not set, so the default
|
|
17
|
+
local and CI runs stay offline. To run them you need a delegated Microsoft Graph access token.
|
|
@@ -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
|
|
@@ -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
|
|
11
|
+
"""Raised when a Microsoft SharePoint component is misconfigured."""
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class SharePointRequestError(SharePointError):
|
|
15
|
-
"""Raised when a Microsoft Graph
|
|
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)
|
microsoft_sharepoint_haystack-1.1.0/src/haystack_integrations/common/microsoft_sharepoint/utils.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
from haystack import logging
|
|
7
|
+
from haystack.utils import Secret
|
|
8
|
+
from tenacity import (
|
|
9
|
+
AsyncRetrying,
|
|
10
|
+
RetryCallState,
|
|
11
|
+
Retrying,
|
|
12
|
+
retry_if_result,
|
|
13
|
+
stop_after_attempt,
|
|
14
|
+
wait_exponential,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from .errors import SharePointConfigError, SharePointRequestError
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
DEFAULT_GRAPH_URL = "https://graph.microsoft.com/v1.0"
|
|
22
|
+
_RETRYABLE_STATUS = frozenset({429, 500, 502, 503, 504})
|
|
23
|
+
# Fallback backoff when a throttled response carries no `Retry-After` header: 1s, 2s, 4s, ...
|
|
24
|
+
_EXPONENTIAL_BACKOFF = wait_exponential()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def resolve_access_token(access_token: str | Secret) -> str:
|
|
28
|
+
"""Return the bearer token string, resolving it if a `Secret` was passed."""
|
|
29
|
+
if not isinstance(access_token, Secret):
|
|
30
|
+
return access_token
|
|
31
|
+
resolved = access_token.resolve_value()
|
|
32
|
+
if not isinstance(resolved, str):
|
|
33
|
+
msg = "The access_token Secret did not resolve to a string value."
|
|
34
|
+
raise SharePointConfigError(msg)
|
|
35
|
+
return resolved
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _retry_response(retry_state: RetryCallState) -> httpx.Response | None:
|
|
39
|
+
"""Return the `httpx.Response` recorded for a retry attempt, or `None` if the attempt raised."""
|
|
40
|
+
outcome = retry_state.outcome
|
|
41
|
+
if outcome is None or outcome.failed:
|
|
42
|
+
return None
|
|
43
|
+
return outcome.result()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _is_retryable_response(response: httpx.Response) -> bool:
|
|
47
|
+
"""Return whether a Microsoft Graph response should trigger a retry."""
|
|
48
|
+
return response.status_code in _RETRYABLE_STATUS
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _wait_with_retry_after(retry_state: RetryCallState) -> float:
|
|
52
|
+
"""Wait strategy honoring a numeric `Retry-After` header, falling back to exponential backoff."""
|
|
53
|
+
response = _retry_response(retry_state)
|
|
54
|
+
if response is not None:
|
|
55
|
+
retry_after = response.headers.get("Retry-After")
|
|
56
|
+
if retry_after:
|
|
57
|
+
try:
|
|
58
|
+
return float(retry_after)
|
|
59
|
+
except ValueError:
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
return _EXPONENTIAL_BACKOFF(retry_state)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _last_response(retry_state: RetryCallState) -> httpx.Response:
|
|
66
|
+
"""Return the final response once retries are exhausted, so the caller can raise a typed error."""
|
|
67
|
+
response = _retry_response(retry_state)
|
|
68
|
+
if response is None:
|
|
69
|
+
msg = "Microsoft Graph retries were exhausted without a recorded response."
|
|
70
|
+
raise SharePointRequestError(msg)
|
|
71
|
+
return response
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _log_retry(retry_state: RetryCallState) -> None:
|
|
75
|
+
"""Log a warning before sleeping between Microsoft Graph retries."""
|
|
76
|
+
response = _retry_response(retry_state)
|
|
77
|
+
next_action = retry_state.next_action
|
|
78
|
+
if response is None or next_action is None:
|
|
79
|
+
return
|
|
80
|
+
logger.warning(
|
|
81
|
+
"Microsoft Graph returned status {status}; retrying in {delay}s (attempt {attempt}).",
|
|
82
|
+
status=response.status_code,
|
|
83
|
+
delay=next_action.sleep,
|
|
84
|
+
attempt=retry_state.attempt_number,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def build_retrying(max_retries: int) -> Retrying:
|
|
89
|
+
"""Build a tenacity `Retrying` that retries Microsoft Graph requests on throttling/transient errors."""
|
|
90
|
+
return Retrying(
|
|
91
|
+
stop=stop_after_attempt(max_retries + 1),
|
|
92
|
+
wait=_wait_with_retry_after,
|
|
93
|
+
retry=retry_if_result(_is_retryable_response),
|
|
94
|
+
before_sleep=_log_retry,
|
|
95
|
+
retry_error_callback=_last_response,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def build_async_retrying(max_retries: int) -> AsyncRetrying:
|
|
100
|
+
"""Async variant of `build_retrying`."""
|
|
101
|
+
return AsyncRetrying(
|
|
102
|
+
stop=stop_after_attempt(max_retries + 1),
|
|
103
|
+
wait=_wait_with_retry_after,
|
|
104
|
+
retry=retry_if_result(_is_retryable_response),
|
|
105
|
+
before_sleep=_log_retry,
|
|
106
|
+
retry_error_callback=_last_response,
|
|
107
|
+
)
|
|
@@ -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
|
+
]
|