alita-sdk 0.3.404__py3-none-any.whl → 0.3.406__py3-none-any.whl

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.

Potentially problematic release.


This version of alita-sdk might be problematic. Click here for more details.

@@ -127,8 +127,23 @@ class SharepointApiWrapper(NonCodeIndexerToolkit):
127
127
  result.append(temp_props)
128
128
  return result if result else ToolException("Can not get files or folder is empty. Please, double check folder name and read permissions.")
129
129
  except Exception as e:
130
- logging.error(f"Failed to load files from sharepoint: {e}")
131
- return ToolException("Can not get files. Please, double check folder name and read permissions.")
130
+ # attempt to get via graph api
131
+ try:
132
+ # attempt to get files via graph api
133
+ from .authorization_helper import SharepointAuthorizationHelper
134
+ auth_helper = SharepointAuthorizationHelper(
135
+ client_id=self.client_id,
136
+ client_secret=self.client_secret.get_secret_value(),
137
+ tenant="", # optional for graph api
138
+ scope="", # optional for graph api
139
+ token_json="", # optional for graph api
140
+ )
141
+ files = auth_helper.get_files_list(self.site_url, folder_name, limit_files)
142
+ return files
143
+ except Exception as graph_e:
144
+ logging.error(f"Failed to load files from sharepoint via base api: {e}")
145
+ logging.error(f"Failed to load files from sharepoint via graph api: {graph_e}")
146
+ return ToolException(f"Can not get files. Please, double check folder name and read permissions: {e} and {graph_e}")
132
147
 
133
148
  def read_file(self, path,
134
149
  is_capture_image: bool = False,
@@ -141,11 +156,28 @@ class SharepointApiWrapper(NonCodeIndexerToolkit):
141
156
  self._client.load(file).execute_query()
142
157
 
143
158
  file_content = file.read()
159
+ file_name = file.name
144
160
  self._client.execute_query()
145
161
  except Exception as e:
146
- logging.error(f"Failed to load file from SharePoint: {e}. Path: {path}. Please, double check file name and path.")
147
- return ToolException("File not found. Please, check file name and path.")
148
- return parse_file_content(file_name=file.name,
162
+ # attempt to get via graph api
163
+ try:
164
+ # attempt to get files via graph api
165
+ from .authorization_helper import SharepointAuthorizationHelper
166
+ auth_helper = SharepointAuthorizationHelper(
167
+ client_id=self.client_id,
168
+ client_secret=self.client_secret.get_secret_value(),
169
+ tenant="", # optional for graph api
170
+ scope="", # optional for graph api
171
+ token_json="", # optional for graph api
172
+ )
173
+ file_content = auth_helper.get_file_content(self.site_url, path)
174
+ file_name = path.split('/')[-1]
175
+ except Exception as graph_e:
176
+ logging.error(f"Failed to load file from SharePoint via base api: {e}. Path: {path}. Please, double check file name and path.")
177
+ logging.error(f"Failed to load file from SharePoint via graph api: {graph_e}. Path: {path}. Please, double check file name and path.")
178
+ return ToolException(f"File not found. Please, check file name and path: {e} and {graph_e}")
179
+ #
180
+ return parse_file_content(file_name=file_name,
149
181
  file_content=file_content,
150
182
  is_capture_image=is_capture_image,
151
183
  page_number=page_number,
@@ -1,4 +1,5 @@
1
1
  from datetime import datetime, timezone
2
+ from urllib.parse import unquote, urlparse
2
3
 
3
4
  import jwt
4
5
  import requests
@@ -54,4 +55,131 @@ class SharepointAuthorizationHelper:
54
55
  except jwt.ExpiredSignatureError:
55
56
  return False
56
57
  except jwt.InvalidTokenError:
57
- return False
58
+ return False
59
+
60
+
61
+ def generate_token_and_site_id(self, site_url: str) -> tuple[str, str]:
62
+ try:
63
+ parsed = urlparse(site_url)
64
+ domain = parsed.hostname
65
+ site_path = parsed.path.strip('/')
66
+ if not domain or not site_path:
67
+ raise ValueError(f"site_url missing domain or site path: {site_url}")
68
+ #
69
+ app_name = domain.split('.')[0]
70
+ openid_config_url = f"https://login.microsoftonline.com/{app_name}.onmicrosoft.com/v2.0/.well-known/openid-configuration"
71
+ response = requests.get(openid_config_url)
72
+ if response.status_code != 200:
73
+ raise RuntimeError(f"Failed to get OpenID config: {response.status_code} {response.text}")
74
+ token_url = response.json().get("token_endpoint")
75
+ if not token_url:
76
+ raise KeyError("'token_endpoint' missing in OpenID config response")
77
+ #
78
+ token_data = {
79
+ "grant_type": "client_credentials",
80
+ "client_id": self.client_id,
81
+ "client_secret": self.client_secret,
82
+ "scope": "https://graph.microsoft.com/.default"
83
+ }
84
+ token_response = requests.post(token_url, data=token_data)
85
+ if token_response.status_code != 200:
86
+ raise RuntimeError(f"Failed to get access token: {token_response.status_code} {token_response.text}")
87
+ access_token = token_response.json().get("access_token")
88
+ if not access_token:
89
+ raise KeyError("'access_token' missing in token response")
90
+ #
91
+ graph_site_url = f"https://graph.microsoft.com/v1.0/sites/{domain}:/{site_path}"
92
+ headers = {"Authorization": f"Bearer {access_token}"}
93
+ site_response = requests.get(graph_site_url, headers=headers)
94
+ if site_response.status_code != 200:
95
+ raise RuntimeError(f"Failed to get site info: {site_response.status_code} {site_response.text}")
96
+ site_id = site_response.json().get("id")
97
+ if not site_id:
98
+ raise KeyError("'id' missing in site response")
99
+ #
100
+ return access_token, site_id
101
+ except Exception as e:
102
+ raise RuntimeError(f"Error while obtaining access_token and site_id: {e}")
103
+
104
+ def get_files_list(self, site_url: str, folder_name: str = None, limit_files: int = 100):
105
+ if not site_url or not site_url.startswith("https://"):
106
+ raise ValueError(f"Invalid site_url format: {site_url}")
107
+ if limit_files is not None and (not isinstance(limit_files, int) or limit_files <= 0):
108
+ raise ValueError(f"limit_files must be a positive integer, got: {limit_files}")
109
+ try:
110
+ access_token, site_id = self.generate_token_and_site_id(site_url)
111
+ headers = {"Authorization": f"Bearer {access_token}"}
112
+ drives_url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives"
113
+ drives_response = requests.get(drives_url, headers=headers)
114
+ if drives_response.status_code != 200:
115
+ raise RuntimeError(f"Failed to get drives: {drives_response.status_code} {drives_response.text}")
116
+ drives_json = drives_response.json()
117
+ if "value" not in drives_json or not drives_json["value"]:
118
+ raise KeyError("'value' missing or empty in drives response")
119
+ drive_id = drives_json["value"][0].get("id")
120
+ drive_path = unquote(urlparse(drives_json["value"][0].get("webUrl")).path)
121
+ if not drive_id:
122
+ raise KeyError("'id' missing in drive object")
123
+ #
124
+ # Build the correct endpoint for folder or root
125
+ if folder_name:
126
+ # Validate folder_name for safe URL usage
127
+ if any(c in folder_name for c in ['..', '//', '\\']):
128
+ raise ValueError(f"Unsafe folder_name: {folder_name}")
129
+ url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives/{drive_id}/root:/{folder_name}:/children?$top={limit_files}"
130
+ else:
131
+ url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives/{drive_id}/root/children?$top={limit_files}"
132
+ response = requests.get(url, headers=headers)
133
+ if response.status_code != 200:
134
+ raise RuntimeError(f"Failed to get files list: {response.status_code} {response.text}")
135
+ files_json = response.json()
136
+ if "value" not in files_json:
137
+ raise KeyError("'value' missing in files response")
138
+ #
139
+ result = []
140
+ for file in files_json["value"]:
141
+ file_name = file.get('name', '')
142
+ parts = [drive_path.strip('/'), folder_name.strip('/') if folder_name else '', file_name.strip('/')]
143
+ #
144
+ temp_props = {
145
+ 'Name': file_name,
146
+ 'Path': '/'.join([part for part in parts if part]),
147
+ 'Created': file.get('createdDateTime'),
148
+ 'Modified': file.get('lastModifiedDateTime'),
149
+ 'Link': file.get('webUrl'),
150
+ 'id': file.get('id')
151
+ }
152
+ if not all([temp_props['Name'], temp_props['Path'], temp_props['id']]):
153
+ raise KeyError(f"Missing required file fields in: {file}")
154
+ result.append(temp_props)
155
+ # If API doesn't respect $top, slice in Python
156
+ if limit_files is not None:
157
+ result = result[:limit_files]
158
+ return result
159
+ except Exception as e:
160
+ raise RuntimeError(f"Error in get_files_list: {e}")
161
+
162
+ def get_file_content(self, site_url: str, path: str):
163
+ try:
164
+ access_token, site_id = self.generate_token_and_site_id(site_url)
165
+ headers = {"Authorization": f"Bearer {access_token}"}
166
+ drives_url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives"
167
+ drives_response = requests.get(drives_url, headers=headers)
168
+ if drives_response.status_code != 200:
169
+ raise RuntimeError(f"Failed to get drives: {drives_response.status_code} {drives_response.text}")
170
+ drives_json = drives_response.json()
171
+ if "value" not in drives_json or not drives_json["value"]:
172
+ raise KeyError("'value' missing or empty in drives response")
173
+ drive_id = drives_json["value"][0].get("id")
174
+ if not drive_id:
175
+ raise KeyError("'id' missing in drive object")
176
+ #
177
+ # Build the endpoint for file content
178
+ url = f"https://graph.microsoft.com/v1.0/drives/{drive_id}/root:/{path}:/content"
179
+ response = requests.get(url, headers=headers)
180
+ if response.status_code != 200:
181
+ raise RuntimeError(f"Failed to get files list: {response.status_code} {response.text}")
182
+ #
183
+ return response.content
184
+ except Exception as e:
185
+ raise RuntimeError(f"Error in get_files_list: {e}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alita_sdk
3
- Version: 0.3.404
3
+ Version: 0.3.406
4
4
  Summary: SDK for building langchain agents using resources from Alita
5
5
  Author-email: Artem Rozumenko <artyom.rozumenko@gmail.com>, Mikalai Biazruchka <mikalai_biazruchka@epam.com>, Roman Mitusov <roman_mitusov@epam.com>, Ivan Krakhmaliuk <lifedj27@gmail.com>, Artem Dubrovskiy <ad13box@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -317,8 +317,8 @@ alita_sdk/tools/servicenow/__init__.py,sha256=ziEt2juPrGFyB98ZXbGf25v6gZo4UJTHsz
317
317
  alita_sdk/tools/servicenow/api_wrapper.py,sha256=WpH-bBLGFdhehs4g-K-WAkNuaD1CSrwsDpdgB3RG53s,6120
318
318
  alita_sdk/tools/servicenow/servicenow_client.py,sha256=Rdqfu-ll-qbnclMzChLZBsfXRDzgoX_FdeI2WLApWxc,3269
319
319
  alita_sdk/tools/sharepoint/__init__.py,sha256=5z2iSmm-0kbHKf70wN6OOgS4Px7tOzwkIpHXz0Vrbj4,4045
320
- alita_sdk/tools/sharepoint/api_wrapper.py,sha256=6D0pGsCx33KxfbZkG331I1gucmqLToI2qBMonFZtl6o,12245
321
- alita_sdk/tools/sharepoint/authorization_helper.py,sha256=n-nL5dlBoLMK70nHu7P2RYCb8C6c9HMA_gEaw8LxuhE,2007
320
+ alita_sdk/tools/sharepoint/api_wrapper.py,sha256=d8B0I4C9x8qt1dvmLjsfYnas98T_gXueJAWrNykZP0U,14075
321
+ alita_sdk/tools/sharepoint/authorization_helper.py,sha256=-wNPxkKcg42o5Z3tkmzIr6rrrCMkA-Og2j6GbDjq7yU,9166
322
322
  alita_sdk/tools/sharepoint/utils.py,sha256=fZ1YzAu5CTjKSZeslowpOPH974902S8vCp1Wu7L44LM,446
323
323
  alita_sdk/tools/slack/__init__.py,sha256=YiPAoRc6y6uVpfHl0K1Qi-flcyPlWFIMVcVbhicGWXY,3990
324
324
  alita_sdk/tools/slack/api_wrapper.py,sha256=5VrV7iSGno8ZcDzEHdGPNhInhtODGPPvAzoZ9W9iQWE,14009
@@ -353,8 +353,8 @@ alita_sdk/tools/zephyr_scale/api_wrapper.py,sha256=kT0TbmMvuKhDUZc0i7KO18O38JM9S
353
353
  alita_sdk/tools/zephyr_squad/__init__.py,sha256=0ne8XLJEQSLOWfzd2HdnqOYmQlUliKHbBED5kW_Vias,2895
354
354
  alita_sdk/tools/zephyr_squad/api_wrapper.py,sha256=kmw_xol8YIYFplBLWTqP_VKPRhL_1ItDD0_vXTe_UuI,14906
355
355
  alita_sdk/tools/zephyr_squad/zephyr_squad_cloud_client.py,sha256=R371waHsms4sllHCbijKYs90C-9Yu0sSR3N4SUfQOgU,5066
356
- alita_sdk-0.3.404.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
357
- alita_sdk-0.3.404.dist-info/METADATA,sha256=KvmNI9txwowkZbaBsfmquBSNCrTRkagGpMSM3_M_8rw,19071
358
- alita_sdk-0.3.404.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
359
- alita_sdk-0.3.404.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
360
- alita_sdk-0.3.404.dist-info/RECORD,,
356
+ alita_sdk-0.3.406.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
357
+ alita_sdk-0.3.406.dist-info/METADATA,sha256=qKUH3FgZr8aK9zs2JOuh5VCEoRswwX5frgyDYb2Vszo,19071
358
+ alita_sdk-0.3.406.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
359
+ alita_sdk-0.3.406.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
360
+ alita_sdk-0.3.406.dist-info/RECORD,,