alita-sdk 0.3.204__py3-none-any.whl → 0.3.205__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.
- alita_sdk/runtime/tools/vectorstore.py +143 -13
- alita_sdk/tools/__init__.py +2 -0
- alita_sdk/tools/aws/__init__.py +7 -0
- alita_sdk/tools/aws/delta_lake/__init__.py +136 -0
- alita_sdk/tools/aws/delta_lake/api_wrapper.py +220 -0
- alita_sdk/tools/aws/delta_lake/schemas.py +20 -0
- alita_sdk/tools/aws/delta_lake/tool.py +35 -0
- alita_sdk/tools/elitea_base.py +49 -4
- alita_sdk/tools/google/__init__.py +7 -0
- alita_sdk/tools/google/bigquery/__init__.py +154 -0
- alita_sdk/tools/google/bigquery/api_wrapper.py +502 -0
- alita_sdk/tools/google/bigquery/schemas.py +102 -0
- alita_sdk/tools/google/bigquery/tool.py +34 -0
- alita_sdk/tools/sharepoint/api_wrapper.py +60 -4
- alita_sdk/tools/testrail/__init__.py +9 -1
- alita_sdk/tools/testrail/api_wrapper.py +132 -6
- alita_sdk/tools/zephyr_scale/api_wrapper.py +271 -22
- {alita_sdk-0.3.204.dist-info → alita_sdk-0.3.205.dist-info}/METADATA +3 -1
- {alita_sdk-0.3.204.dist-info → alita_sdk-0.3.205.dist-info}/RECORD +22 -12
- {alita_sdk-0.3.204.dist-info → alita_sdk-0.3.205.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.204.dist-info → alita_sdk-0.3.205.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.204.dist-info → alita_sdk-0.3.205.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
|
|
1
|
+
import json
|
1
2
|
import logging
|
2
|
-
from typing import Optional
|
3
|
+
from typing import Optional, List, Dict, Any
|
3
4
|
|
4
5
|
from ..utils.content_parser import parse_file_content
|
5
6
|
from langchain_core.tools import ToolException
|
@@ -7,7 +8,9 @@ from office365.runtime.auth.client_credential import ClientCredential
|
|
7
8
|
from office365.sharepoint.client_context import ClientContext
|
8
9
|
from pydantic import Field, PrivateAttr, create_model, model_validator, SecretStr
|
9
10
|
|
10
|
-
from ..elitea_base import BaseToolApiWrapper
|
11
|
+
from ..elitea_base import BaseToolApiWrapper, BaseIndexParams, BaseVectorStoreToolApiWrapper
|
12
|
+
from ...runtime.langchain.interfaces.llm_processor import get_embeddings
|
13
|
+
from langchain_core.documents import Document
|
11
14
|
|
12
15
|
NoInput = create_model(
|
13
16
|
"NoInput"
|
@@ -32,14 +35,30 @@ ReadDocument = create_model(
|
|
32
35
|
page_number=(Optional[int], Field(description="Specifies which page to read. If it is None, then full document will be read.", default=None))
|
33
36
|
)
|
34
37
|
|
38
|
+
indexData = create_model(
|
39
|
+
"indexData",
|
40
|
+
__base__=BaseIndexParams,
|
41
|
+
progress_step=(Optional[int], Field(default=None, ge=0, le=100,
|
42
|
+
description="Optional step size for progress reporting during indexing")),
|
43
|
+
clean_index=(Optional[bool], Field(default=False,
|
44
|
+
description="Optional flag to enforce clean existing index before indexing new data")),
|
45
|
+
)
|
46
|
+
|
35
47
|
|
36
|
-
class SharepointApiWrapper(
|
48
|
+
class SharepointApiWrapper(BaseVectorStoreToolApiWrapper):
|
37
49
|
site_url: str
|
38
50
|
client_id: str = None
|
39
51
|
client_secret: SecretStr = None
|
40
52
|
token: SecretStr = None
|
41
53
|
_client: Optional[ClientContext] = PrivateAttr() # Private attribute for the office365 client
|
42
54
|
|
55
|
+
llm: Any = None
|
56
|
+
connection_string: Optional[SecretStr] = None
|
57
|
+
collection_name: Optional[str] = None
|
58
|
+
embedding_model: Optional[str] = "HuggingFaceEmbeddings"
|
59
|
+
embedding_model_params: Optional[Dict[str, Any]] = {"model_name": "sentence-transformers/all-MiniLM-L6-v2"}
|
60
|
+
vectorstore_type: Optional[str] = "PGVector"
|
61
|
+
|
43
62
|
@model_validator(mode='before')
|
44
63
|
@classmethod
|
45
64
|
def validate_toolkit(cls, values):
|
@@ -111,7 +130,8 @@ class SharepointApiWrapper(BaseToolApiWrapper):
|
|
111
130
|
'Path': file.properties['ServerRelativeUrl'],
|
112
131
|
'Created': file.properties['TimeCreated'],
|
113
132
|
'Modified': file.properties['TimeLastModified'],
|
114
|
-
'Link': file.properties['LinkingUrl']
|
133
|
+
'Link': file.properties['LinkingUrl'],
|
134
|
+
'id': file.properties['UniqueId']
|
115
135
|
}
|
116
136
|
result.append(temp_props)
|
117
137
|
return result if result else ToolException("Can not get files or folder is empty. Please, double check folder name and read permissions.")
|
@@ -132,6 +152,36 @@ class SharepointApiWrapper(BaseToolApiWrapper):
|
|
132
152
|
return ToolException("File not found. Please, check file name and path.")
|
133
153
|
return parse_file_content(file.name, file_content, is_capture_image, page_number)
|
134
154
|
|
155
|
+
def _base_loader(self) -> List[Document]:
|
156
|
+
try:
|
157
|
+
all_files = self.get_files_list()
|
158
|
+
except Exception as e:
|
159
|
+
raise ToolException(f"Unable to extract files: {e}")
|
160
|
+
|
161
|
+
docs: List[Document] = []
|
162
|
+
for file in all_files:
|
163
|
+
metadata = {
|
164
|
+
("updated_at" if k == "Modified" else k): str(v)
|
165
|
+
for k, v in file.items()
|
166
|
+
}
|
167
|
+
docs.append(Document(page_content="", metadata=metadata))
|
168
|
+
return docs
|
169
|
+
|
170
|
+
def index_data(self,
|
171
|
+
collection_suffix: str = '',
|
172
|
+
progress_step: int = None,
|
173
|
+
clean_index: bool = False):
|
174
|
+
docs = self._base_loader()
|
175
|
+
embedding = get_embeddings(self.embedding_model, self.embedding_model_params)
|
176
|
+
vs = self._init_vector_store(collection_suffix, embeddings=embedding)
|
177
|
+
return vs.index_documents(docs, progress_step=progress_step, clean_index=clean_index)
|
178
|
+
|
179
|
+
def _process_document(self, document: Document) -> Document:
|
180
|
+
page_content = self.read_file(document.metadata['Path'], is_capture_image=True)
|
181
|
+
|
182
|
+
document.page_content = json.dumps(str(page_content))
|
183
|
+
return document
|
184
|
+
|
135
185
|
def get_available_tools(self):
|
136
186
|
return [
|
137
187
|
{
|
@@ -151,5 +201,11 @@ class SharepointApiWrapper(BaseToolApiWrapper):
|
|
151
201
|
"description": self.read_file.__doc__,
|
152
202
|
"args_schema": ReadDocument,
|
153
203
|
"ref": self.read_file
|
204
|
+
},
|
205
|
+
{
|
206
|
+
"name": "index_data",
|
207
|
+
"ref": self.index_data,
|
208
|
+
"description": self.index_data.__doc__,
|
209
|
+
"args_schema": indexData,
|
154
210
|
}
|
155
211
|
]
|
@@ -16,7 +16,15 @@ def get_tools(tool):
|
|
16
16
|
url=tool['settings']['url'],
|
17
17
|
password=tool['settings'].get('password', None),
|
18
18
|
email=tool['settings'].get('email', None),
|
19
|
-
toolkit_name=tool.get('toolkit_name')
|
19
|
+
toolkit_name=tool.get('toolkit_name'),
|
20
|
+
llm=tool['settings'].get('llm', None),
|
21
|
+
|
22
|
+
# indexer settings
|
23
|
+
connection_string=tool['settings'].get('connection_string', None),
|
24
|
+
collection_name=f"{tool.get('toolkit_name')}_{str(tool['id'])}",
|
25
|
+
embedding_model="HuggingFaceEmbeddings",
|
26
|
+
embedding_model_params={"model_name": "sentence-transformers/all-MiniLM-L6-v2"},
|
27
|
+
vectorstore_type="PGVector"
|
20
28
|
).get_tools()
|
21
29
|
|
22
30
|
|
@@ -1,14 +1,18 @@
|
|
1
1
|
import json
|
2
2
|
import logging
|
3
|
-
from typing import Dict, List, Optional, Union
|
3
|
+
from typing import Dict, List, Optional, Union, Any
|
4
4
|
|
5
5
|
import pandas as pd
|
6
6
|
from langchain_core.tools import ToolException
|
7
7
|
from pydantic import SecretStr, create_model, model_validator
|
8
8
|
from pydantic.fields import Field, PrivateAttr
|
9
9
|
from testrail_api import StatusCodeError, TestRailAPI
|
10
|
-
|
11
|
-
from
|
10
|
+
from ..elitea_base import BaseVectorStoreToolApiWrapper, BaseIndexParams
|
11
|
+
from langchain_core.documents import Document
|
12
|
+
try:
|
13
|
+
from alita_sdk.runtime.langchain.interfaces.llm_processor import get_embeddings
|
14
|
+
except ImportError:
|
15
|
+
from alita_sdk.langchain.interfaces.llm_processor import get_embeddings
|
12
16
|
|
13
17
|
logger = logging.getLogger(__name__)
|
14
18
|
|
@@ -281,6 +285,19 @@ updateCase = create_model(
|
|
281
285
|
),
|
282
286
|
)
|
283
287
|
|
288
|
+
# Schema for indexing TestRail data into vector store
|
289
|
+
indexData = create_model(
|
290
|
+
"indexData",
|
291
|
+
__base__=BaseIndexParams,
|
292
|
+
project_id=(str, Field(description="TestRail project ID to index data from")),
|
293
|
+
suite_id=(Optional[str], Field(default=None, description="Optional TestRail suite ID to filter test cases")),
|
294
|
+
section_id=(Optional[int], Field(default=None, description="Optional section ID to filter test cases")),
|
295
|
+
title_keyword=(Optional[str], Field(default=None, description="Optional keyword to filter test cases by title")),
|
296
|
+
progress_step=(Optional[int],
|
297
|
+
Field(default=None, ge=0, le=100, description="Optional step size for progress reporting during indexing")),
|
298
|
+
clean_index=(Optional[bool],
|
299
|
+
Field(default=False, description="Optional flag to enforce clean existing index before indexing new data")),
|
300
|
+
)
|
284
301
|
|
285
302
|
SUPPORTED_KEYS = {
|
286
303
|
"id", "title", "section_id", "template_id", "type_id", "priority_id", "milestone_id",
|
@@ -291,11 +308,19 @@ SUPPORTED_KEYS = {
|
|
291
308
|
}
|
292
309
|
|
293
310
|
|
294
|
-
class TestrailAPIWrapper(
|
311
|
+
class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
|
295
312
|
url: str
|
296
313
|
password: Optional[SecretStr] = None,
|
297
314
|
email: Optional[str] = None,
|
298
315
|
_client: Optional[TestRailAPI] = PrivateAttr() # Private attribute for the TestRail client
|
316
|
+
llm: Any = None
|
317
|
+
|
318
|
+
connection_string: Optional[SecretStr] = None
|
319
|
+
collection_name: Optional[str] = None
|
320
|
+
embedding_model: Optional[str] = "HuggingFaceEmbeddings"
|
321
|
+
embedding_model_params: Optional[Dict[str, Any]] = {"model_name": "sentence-transformers/all-MiniLM-L6-v2"}
|
322
|
+
vectorstore_type: Optional[str] = "PGVector"
|
323
|
+
|
299
324
|
|
300
325
|
@model_validator(mode="before")
|
301
326
|
@classmethod
|
@@ -492,7 +517,7 @@ class TestrailAPIWrapper(BaseToolApiWrapper):
|
|
492
517
|
you can submit and update specific fields only).
|
493
518
|
|
494
519
|
:param case_id: T
|
495
|
-
|
520
|
+
He ID of the test case
|
496
521
|
:param kwargs:
|
497
522
|
:key title: str
|
498
523
|
The title of the test case
|
@@ -522,6 +547,98 @@ class TestrailAPIWrapper(BaseToolApiWrapper):
|
|
522
547
|
f"Test case #{case_id} has been updated at '{updated_case['updated_on']}')"
|
523
548
|
)
|
524
549
|
|
550
|
+
def _base_loader(self, project_id: str,
|
551
|
+
suite_id: Optional[str] = None,
|
552
|
+
section_id: Optional[int] = None,
|
553
|
+
title_keyword: Optional[str] = None
|
554
|
+
) -> List[Document]:
|
555
|
+
try:
|
556
|
+
if suite_id:
|
557
|
+
resp = self._client.cases.get_cases(project_id=project_id, suite_id=int(suite_id))
|
558
|
+
cases = resp.get('cases', [])
|
559
|
+
else:
|
560
|
+
resp = self._client.cases.get_cases(project_id=project_id)
|
561
|
+
cases = resp.get('cases', [])
|
562
|
+
except StatusCodeError as e:
|
563
|
+
raise ToolException(f"Unable to extract test cases: {e}")
|
564
|
+
# Apply filters
|
565
|
+
if section_id is not None:
|
566
|
+
cases = [case for case in cases if case.get('section_id') == section_id]
|
567
|
+
if title_keyword is not None:
|
568
|
+
cases = [case for case in cases if title_keyword.lower() in case.get('title', '').lower()]
|
569
|
+
|
570
|
+
docs: List[Document] = []
|
571
|
+
for case in cases:
|
572
|
+
docs.append(Document(page_content=json.dumps(case), metadata={
|
573
|
+
'project_id': project_id,
|
574
|
+
'title': case.get('title', ''),
|
575
|
+
'suite_id': suite_id or case.get('suite_id', ''),
|
576
|
+
'id': str(case.get('id', '')),
|
577
|
+
'updated_on': case.get('updated_on', ''),
|
578
|
+
}))
|
579
|
+
return docs
|
580
|
+
|
581
|
+
def index_data(
|
582
|
+
self,
|
583
|
+
project_id: str,
|
584
|
+
suite_id: Optional[str] = None,
|
585
|
+
collection_suffix: str = "",
|
586
|
+
section_id: Optional[int] = None,
|
587
|
+
title_keyword: Optional[str] = None,
|
588
|
+
progress_step: Optional[int] = None,
|
589
|
+
clean_index: Optional[bool] = False
|
590
|
+
):
|
591
|
+
"""Load TestRail test cases into the vector store."""
|
592
|
+
docs = self._base_loader(project_id, suite_id, section_id, title_keyword)
|
593
|
+
embedding = get_embeddings(self.embedding_model, self.embedding_model_params)
|
594
|
+
vs = self._init_vector_store(collection_suffix, embeddings=embedding)
|
595
|
+
return vs.index_documents(docs, progress_step=progress_step, clean_index=clean_index)
|
596
|
+
|
597
|
+
def _process_document(self, document: Document) -> Document:
|
598
|
+
"""
|
599
|
+
Process an existing base document to extract relevant metadata for full document preparation.
|
600
|
+
Used for late processing of documents after we ensure that the document has to be indexed to avoid
|
601
|
+
time-consuming operations for documents which might be useless.
|
602
|
+
|
603
|
+
Args:
|
604
|
+
document (Document): The base document to process.
|
605
|
+
|
606
|
+
Returns:
|
607
|
+
Document: The processed document with metadata.
|
608
|
+
"""
|
609
|
+
try:
|
610
|
+
# get base data from the document required to extract attachments and other metadata
|
611
|
+
base_data = json.loads(document.page_content)
|
612
|
+
case_id = base_data.get("id")
|
613
|
+
|
614
|
+
# get a list of attachments for the case
|
615
|
+
attachments = self._client.attachments.get_attachments_for_case_bulk(case_id=case_id)
|
616
|
+
attachments_data = {}
|
617
|
+
|
618
|
+
# process each attachment to extract its content
|
619
|
+
for attachment in attachments:
|
620
|
+
attachments_data[attachment['filename']] = self._process_attachment(attachment)
|
621
|
+
base_data['attachments'] = attachments_data
|
622
|
+
document.page_content = json.dumps(base_data)
|
623
|
+
return document
|
624
|
+
except json.JSONDecodeError as e:
|
625
|
+
raise ToolException(f"Failed to decode JSON from document: {e}")
|
626
|
+
|
627
|
+
def _process_attachment(self, attachment: Dict[str, Any]) -> str:
|
628
|
+
"""
|
629
|
+
Processes an attachment to extract its content.
|
630
|
+
|
631
|
+
Args:
|
632
|
+
attachment (Dict[str, Any]): The attachment data.
|
633
|
+
|
634
|
+
Returns:
|
635
|
+
str: string description of the attachment.
|
636
|
+
"""
|
637
|
+
if attachment['filetype'] == 'txt' :
|
638
|
+
return self._client.get(endpoint=f"get_attachment/{attachment['id']}")
|
639
|
+
# TODO: add support for other file types
|
640
|
+
return "This filetype is not supported."
|
641
|
+
|
525
642
|
def _to_markup(self, data: List[Dict], output_format: str) -> str:
|
526
643
|
"""
|
527
644
|
Converts the given data into the specified format: 'json', 'csv', or 'markdown'.
|
@@ -550,7 +667,7 @@ class TestrailAPIWrapper(BaseToolApiWrapper):
|
|
550
667
|
return df.to_markdown(index=False)
|
551
668
|
|
552
669
|
def get_available_tools(self):
|
553
|
-
|
670
|
+
tools = [
|
554
671
|
{
|
555
672
|
"name": "get_case",
|
556
673
|
"ref": self.get_case,
|
@@ -587,4 +704,13 @@ class TestrailAPIWrapper(BaseToolApiWrapper):
|
|
587
704
|
"description": self.update_case.__doc__,
|
588
705
|
"args_schema": updateCase,
|
589
706
|
},
|
707
|
+
{
|
708
|
+
"name": "index_data",
|
709
|
+
"ref": self.index_data,
|
710
|
+
"description": self.index_data.__doc__,
|
711
|
+
"args_schema": indexData,
|
712
|
+
}
|
590
713
|
]
|
714
|
+
# Add vector search from base
|
715
|
+
tools.extend(self._get_vector_search_tools())
|
716
|
+
return tools
|