alita-sdk 0.3.284__py3-none-any.whl → 0.3.286__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.
@@ -44,7 +44,7 @@ class AlitaGitlabToolkit(BaseToolkit):
44
44
  return create_model(
45
45
  name,
46
46
  repository=(str, Field(description="GitLab repository", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AlitaGitlabToolkit.toolkit_max_length})),
47
- gitlab_configuration=(Optional[GitlabConfiguration], Field(description="GitLab configuration", json_schema_extra={'configuration_types': ['gitlab']})),
47
+ gitlab_configuration=(GitlabConfiguration, Field(description="GitLab configuration", json_schema_extra={'configuration_types': ['gitlab']})),
48
48
  branch=(str, Field(description="Main branch", default="main")),
49
49
  # indexer settings
50
50
  pgvector_configuration=(Optional[PgVectorConfiguration], Field(default = None,
@@ -1,8 +1,9 @@
1
1
  import json
2
2
  import logging
3
- from typing import Dict, List, Optional, Union, Any, Generator
3
+ from typing import Dict, List, Literal, Optional, Union, Any, Generator
4
4
 
5
5
  import pandas as pd
6
+ from langchain_core.documents import Document
6
7
  from langchain_core.tools import ToolException
7
8
  from openai import BadRequestError
8
9
  from pydantic import SecretStr, create_model, model_validator
@@ -10,11 +11,9 @@ from pydantic.fields import Field, PrivateAttr
10
11
  from testrail_api import StatusCodeError, TestRailAPI
11
12
 
12
13
  from ..chunkers.code.constants import get_file_extension
13
- from ..elitea_base import BaseVectorStoreToolApiWrapper, extend_with_vector_tools
14
- from langchain_core.documents import Document
15
-
14
+ from ..non_code_indexer_toolkit import NonCodeIndexerToolkit
15
+ from ..utils.available_tools_decorator import extend_with_parent_available_tools
16
16
  from ...runtime.utils.utils import IndexerKeywords
17
- from ..utils.content_parser import parse_file_content
18
17
 
19
18
  try:
20
19
  from alita_sdk.runtime.langchain.interfaces.llm_processor import get_embeddings
@@ -301,7 +300,7 @@ SUPPORTED_KEYS = {
301
300
  }
302
301
 
303
302
 
304
- class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
303
+ class TestrailAPIWrapper(NonCodeIndexerToolkit):
305
304
  url: str
306
305
  password: Optional[SecretStr] = None,
307
306
  email: Optional[str] = None,
@@ -322,7 +321,7 @@ class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
322
321
  password = values.get("password")
323
322
  email = values.get("email")
324
323
  cls._client = TestRailAPI(url, email, password)
325
- return values
324
+ return super().validate_toolkit(values)
326
325
 
327
326
  def add_cases(self, add_test_cases_data: str):
328
327
  """Adds new test cases into Testrail per defined parameters.
@@ -537,6 +536,7 @@ class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
537
536
  suite_id: Optional[str] = None,
538
537
  section_id: Optional[int] = None,
539
538
  title_keyword: Optional[str] = None,
539
+ chunking_tool: str = None,
540
540
  **kwargs: Any
541
541
  ) -> Generator[Document, None, None]:
542
542
  self._include_attachments = kwargs.get('include_attachments', False)
@@ -558,7 +558,7 @@ class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
558
558
  cases = [case for case in cases if title_keyword.lower() in case.get('title', '').lower()]
559
559
 
560
560
  for case in cases:
561
- yield Document(page_content=json.dumps(case), metadata={
561
+ metadata = {
562
562
  'project_id': project_id,
563
563
  'title': case.get('title', ''),
564
564
  'suite_id': suite_id or case.get('suite_id', ''),
@@ -572,7 +572,14 @@ class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
572
572
  'automation_type': case.get('custom_automation_type') or -1,
573
573
  'section_id': case.get('section_id') or -1,
574
574
  'entity_type': 'test_case',
575
- })
575
+ }
576
+ if chunking_tool:
577
+ # content is in metadata for chunking tool post-processing
578
+ metadata[IndexerKeywords.CONTENT_IN_BYTES.value] = json.dumps(case).encode("utf-8")
579
+ page_content = ""
580
+ else:
581
+ page_content = json.dumps(case)
582
+ yield Document(page_content=page_content, metadata=metadata)
576
583
 
577
584
  def _process_document(self, document: Document) -> Generator[Document, None, None]:
578
585
  """
@@ -592,7 +599,7 @@ class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
592
599
  return
593
600
 
594
601
  # get base data from the document required to extract attachments and other metadata
595
- base_data = json.loads(document.page_content)
602
+ base_data = document.metadata
596
603
  case_id = base_data.get("id")
597
604
 
598
605
  # get a list of attachments for the case
@@ -605,24 +612,24 @@ class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
605
612
  continue
606
613
 
607
614
  attachment_id = f"attach_{attachment['id']}"
608
- # add attachment id to metadata of parent
609
- document.metadata.setdefault(IndexerKeywords.DEPENDENT_DOCS.value, []).append(attachment_id)
610
- # TODO: pass it to chunkers
611
- yield Document(page_content=self._process_attachment(attachment),
615
+ file_name, content_bytes = self._process_attachment(attachment)
616
+ yield Document(page_content="",
612
617
  metadata={
613
618
  'project_id': base_data.get('project_id', ''),
619
+ 'updated_on': base_data.get('updated_on', ''),
614
620
  'id': str(attachment_id),
615
- IndexerKeywords.PARENT.value: str(case_id),
616
621
  'filename': attachment['filename'],
617
622
  'filetype': attachment['filetype'],
618
623
  'created_on': attachment['created_on'],
619
624
  'entity_type': 'test_case_attachment',
620
625
  'is_image': attachment['is_image'],
626
+ IndexerKeywords.CONTENT_FILE_NAME.value: file_name,
627
+ IndexerKeywords.CONTENT_IN_BYTES.value: content_bytes
621
628
  })
622
629
  except json.JSONDecodeError as e:
623
630
  raise ToolException(f"Failed to decode JSON from document: {e}")
624
631
 
625
- def _process_attachment(self, attachment: Dict[str, Any]) -> str:
632
+ def _process_attachment(self, attachment: Dict[str, Any]) -> tuple[Any, bytes]:
626
633
  """
627
634
  Processes an attachment to extract its content.
628
635
 
@@ -633,18 +640,21 @@ class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
633
640
  str: string description of the attachment.
634
641
  """
635
642
 
636
- page_content = "This filetype is not supported."
637
643
  if attachment['filetype'] == 'txt' :
638
- page_content = self._client.get(endpoint=f"get_attachment/{attachment['id']}")
644
+ response = self._client.get(endpoint=f"get_attachment/{attachment['id']}")
645
+ if hasattr(response, "content"):
646
+ return attachment['filename'], response.content
647
+ elif isinstance(response, str):
648
+ return attachment['filename'], response.encode("utf-8")
639
649
  else:
640
650
  try:
641
651
  attachment_path = self._client.attachments.get_attachment(attachment_id=attachment['id'], path=f"./{attachment['filename']}")
642
- page_content = parse_file_content(file_name=attachment['filename'], file_content=attachment_path.read_bytes(), llm=self.llm, is_capture_image=True)
652
+ return attachment['filename'], attachment_path.read_bytes()
643
653
  except BadRequestError as ai_e:
644
654
  logger.error(f"Unable to parse page's content with type: {attachment['filetype']} due to AI service issues: {ai_e}")
645
655
  except Exception as e:
646
656
  logger.error(f"Unable to parse page's content with type: {attachment['filetype']}: {e}")
647
- return page_content
657
+ return attachment['filename'], b"This filetype is not supported."
648
658
 
649
659
  def _index_tool_params(self):
650
660
  return {
@@ -658,6 +668,7 @@ class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
658
668
  'skip_attachment_extensions': (Optional[List[str]], Field(
659
669
  description="List of file extensions to skip when processing attachments: i.e. ['.png', '.jpg']",
660
670
  default=[])),
671
+ 'chunking_tool':(Literal['json'], Field(description="Name of chunking tool", default='json'))
661
672
  }
662
673
 
663
674
  def _to_markup(self, data: List[Dict], output_format: str) -> str:
@@ -687,7 +698,7 @@ class TestrailAPIWrapper(BaseVectorStoreToolApiWrapper):
687
698
  if output_format == "markdown":
688
699
  return df.to_markdown(index=False)
689
700
 
690
- @extend_with_vector_tools
701
+ @extend_with_parent_available_tools
691
702
  def get_available_tools(self):
692
703
  tools = [
693
704
  {
@@ -43,7 +43,8 @@ class ZephyrEnterpriseToolkit(BaseToolkit):
43
43
  default=None)),
44
44
  # embedder settings
45
45
  embedding_model=(Optional[str], Field(default=None, description="Embedding configuration.", json_schema_extra={'configuration_model': 'embedding'})),
46
- selected_tools=(List[Literal[tuple(selected_tools)]], []),
46
+ selected_tools=(List[Literal[tuple(selected_tools)]],
47
+ Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
47
48
  __config__=ConfigDict(json_schema_extra={
48
49
  'metadata': {
49
50
  "label": "Zephyr Enterprise", "icon_url": "zephyr.svg",
@@ -179,12 +179,43 @@ class ZephyrEssentialApiWrapper(NonCodeIndexerToolkit):
179
179
  def create_folder(self, json: str):
180
180
  """Create a new folder."""
181
181
  folder_data = self._parse_json(json)
182
+ if 'parentId' not in folder_data:
183
+ if 'parentName' in folder_data:
184
+ parent_folder_name = folder_data['parentName']
185
+
186
+ parent_folder = self.find_folder_by_name(parent_folder_name)
187
+
188
+ if isinstance(parent_folder, ToolException):
189
+ return ToolException(f"Folder with name '{parent_folder_name}' not found.")
190
+ else:
191
+ folder_data['parentId'] = parent_folder['id']
192
+
182
193
  return self._client.create_folder(folder_data)
183
194
 
184
195
  def get_folder(self, folder_id: str):
185
196
  """Retrieve details of a specific folder."""
186
197
  return self._client.get_folder(folder_id)
187
198
 
199
+ def find_folder_by_name(self, name: str, project_key: Optional[str] = None, folder_type: Optional[str] = None):
200
+ """
201
+ Find a folder by its name, ignoring case.
202
+
203
+ :param name: The name of the folder to search for.
204
+ :param project_key: Optional filter by project key.
205
+ :param folder_type: Optional filter by folder type.
206
+ :return: The folder details if found, otherwise None.
207
+ """
208
+ # Fetch all folders with optional filters
209
+ folders = self.list_folders(project_key=project_key, folder_type=folder_type)
210
+
211
+ # Iterate through the folders and search for the matching name
212
+ for folder in folders['values']:
213
+ if folder.get('name', '').lower() == name.lower():
214
+ return folder
215
+
216
+ # Return None if no folder is found
217
+ return ToolException(f"Folder with name {name} was not found")
218
+
188
219
  def delete_link(self, link_id: str):
189
220
  """Delete a specific link."""
190
221
  return self._client.delete_link(link_id)
@@ -482,6 +513,12 @@ class ZephyrEssentialApiWrapper(NonCodeIndexerToolkit):
482
513
  "args_schema": GetFolder,
483
514
  "ref": self.get_folder,
484
515
  },
516
+ {
517
+ "name": "find_folder_by_name",
518
+ "description": self.find_folder_by_name.__doc__,
519
+ "args_schema": FindFolderByName,
520
+ "ref": self.find_folder_by_name,
521
+ },
485
522
  {
486
523
  "name": "delete_link",
487
524
  "description": self.delete_link.__doc__,
@@ -879,11 +916,12 @@ CreateFolder = create_model(
879
916
  JSON body to create a folder. Example:
880
917
  {
881
918
  "parentId": 123456,
919
+ "parentName": "parentFolder",
882
920
  "name": "ZephyrEssential_test",
883
921
  "projectKey": "PRJ",
884
922
  "folderType": "TEST_CASE"
885
923
  }
886
- Possible folder types: "TEST_CASE", "TEST_PLAN", "TEST_CYCLE"
924
+ Possible folder types: "TEST_CASE", "TEST_PLAN", "TEST_CYCLE",
887
925
  """
888
926
  )))
889
927
  )
@@ -893,6 +931,14 @@ GetFolder = create_model(
893
931
  folder_id=(str, Field(description="ID of the folder to retrieve."))
894
932
  )
895
933
 
934
+ FindFolderByName = create_model(
935
+ "GetFolder",
936
+ name=(str, Field(description="Name of the folder to retrieve.")),
937
+ project_key=(Optional[str], Field(description="Project key", default=None)),
938
+ folder_type=(Optional[str], Field(description="""Folder type. Possible values: "TEST_CASE", "TEST_PLAN", "TEST_CYCLE" """,
939
+ default=None)),
940
+ )
941
+
896
942
  DeleteLink = create_model(
897
943
  "DeleteLink",
898
944
  link_id=(str, Field(description="ID of the link to delete."))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alita_sdk
3
- Version: 0.3.284
3
+ Version: 0.3.286
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 <lifedjik@gmail.com>, Artem Dubrovskiy <ad13box@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -241,7 +241,7 @@ alita_sdk/tools/github/graphql_client_wrapper.py,sha256=d3AGjzLGH_hdQV2V8HeAX92d
241
241
  alita_sdk/tools/github/schemas.py,sha256=yFsqivfjCPRk9GxFJrL8sTz6nnjFCZ0j5DIfPtGSsvA,13852
242
242
  alita_sdk/tools/github/tool.py,sha256=Jnnv5lenV5ds8AAdyo2m8hSzyJ117HZBjzHC6T1ck-M,1037
243
243
  alita_sdk/tools/github/tool_prompts.py,sha256=y6ZW_FpUCE87Uop3WuQAZVRnzxO5t7xjBOI5bCqiluw,30194
244
- alita_sdk/tools/gitlab/__init__.py,sha256=wLTgBEeX44A3YkPkyUoPe4GVkDwHgLUpNEIBwJvaGUY,4488
244
+ alita_sdk/tools/gitlab/__init__.py,sha256=_ZmoZaaxJ9qmkTSZ8xsAiXMqR075GHvW1w6USvEyKf8,4478
245
245
  alita_sdk/tools/gitlab/api_wrapper.py,sha256=x2AdR4CkqnMPxVOWwIAgxXuQuzel58drz8m2jyf2R0M,21833
246
246
  alita_sdk/tools/gitlab/tools.py,sha256=vOGTlSaGaFmWn6LS6YFP-FuTqUPun9vnv1VrUcUHAZQ,16500
247
247
  alita_sdk/tools/gitlab/utils.py,sha256=Z2XiqIg54ouqqt1to-geFybmkCb1I6bpE91wfnINH1I,2320
@@ -322,7 +322,7 @@ alita_sdk/tools/sql/models.py,sha256=AKJgSl_kEEz4fZfw3kbvdGHXaRZ-yiaqfJOB6YOj3i0
322
322
  alita_sdk/tools/testio/__init__.py,sha256=-4pEdEv0xfXml5bn8Nij28D5o9Y8hDllYoXfPeJ6os4,2699
323
323
  alita_sdk/tools/testio/api_wrapper.py,sha256=BvmL5h634BzG6p7ajnQLmj-uoAw1gjWnd4FHHu1h--Q,21638
324
324
  alita_sdk/tools/testrail/__init__.py,sha256=0kETjWKLU7R6mugBWsjwEUsh10pipbAeNSGJAO0FBh0,4634
325
- alita_sdk/tools/testrail/api_wrapper.py,sha256=5T-QyTzt-J0rI32xc_E684lCdgyWeHSyeTYiwQwtGyg,32275
325
+ alita_sdk/tools/testrail/api_wrapper.py,sha256=xKQbjwL602J55KZiAdMcMtsuzK2jky0DUcrrdsazj0A,32981
326
326
  alita_sdk/tools/utils/__init__.py,sha256=155xepXPr4OEzs2Mz5YnjXcBpxSv1X2eznRUVoPtyK0,3268
327
327
  alita_sdk/tools/utils/available_tools_decorator.py,sha256=IbrdfeQkswxUFgvvN7-dyLMZMyXLiwvX7kgi3phciCk,273
328
328
  alita_sdk/tools/utils/content_parser.py,sha256=KM6K37VLAAvzwvoHFpJBoRB5d7w6z3mZC-u01n0IpDQ,12036
@@ -336,19 +336,19 @@ alita_sdk/tools/zephyr/Zephyr.py,sha256=ODZbg9Aw0H0Rbv-HcDXLI4KHbPiLDHoteDofshw9
336
336
  alita_sdk/tools/zephyr/__init__.py,sha256=8B2Ibz5QTmB5WkV0q8Sq4kuj92FFaFWZLrT877zRRLg,2897
337
337
  alita_sdk/tools/zephyr/api_wrapper.py,sha256=lJCYPG03ej0qgdpLflnS7LFB4HSAfGzIvTjAJt07CQs,6244
338
338
  alita_sdk/tools/zephyr/rest_client.py,sha256=7vSD3oYIX-3KbAFed-mphSQif_VRuXrq5O07ryNQ7Pk,6208
339
- alita_sdk/tools/zephyr_enterprise/__init__.py,sha256=SaLLkrD_m4we9dmNKP0Hj6skMiz2kDp4MuKr09ddX_s,4077
339
+ alita_sdk/tools/zephyr_enterprise/__init__.py,sha256=IoWQPH2lf2Yuj2ejZ74818JTXdrMoh_ZxAJORukYODU,4172
340
340
  alita_sdk/tools/zephyr_enterprise/api_wrapper.py,sha256=km2TYNu5ppRkspN1PyYetu6iBGj-xKVIwGHty1r_wAw,11552
341
341
  alita_sdk/tools/zephyr_enterprise/zephyr_enterprise.py,sha256=hV9LIrYfJT6oYp-ZfQR0YHflqBFPsUw2Oc55HwK0H48,6809
342
342
  alita_sdk/tools/zephyr_essential/__init__.py,sha256=2SymL6TIF1ad52w5DTtWaYvNygEvE1ssNNeKx3Y-_Zg,3980
343
- alita_sdk/tools/zephyr_essential/api_wrapper.py,sha256=241d5FJkaANzn01HKD-Zyfhk-R4ETkvYNhIc2gNU4oU,38653
343
+ alita_sdk/tools/zephyr_essential/api_wrapper.py,sha256=PN8KbNBzbsjgkdpyogav1cmwIwu_6dGh0EA2mAm2U68,40685
344
344
  alita_sdk/tools/zephyr_essential/client.py,sha256=bfNcUKNqj9MFWTludGbbqD4qZlxrBaC2JtWsCfZMqSY,9722
345
345
  alita_sdk/tools/zephyr_scale/__init__.py,sha256=eetAVRclO1j_N0T3mRnWeLfi3BS98i5FwhNReXO0PlE,4289
346
346
  alita_sdk/tools/zephyr_scale/api_wrapper.py,sha256=HOt9ShtJI_1tVPcwd3Rwk-VS0SMLqcPNYbN1wqfeuhc,78330
347
347
  alita_sdk/tools/zephyr_squad/__init__.py,sha256=0AI_j27xVO5Gk5HQMFrqPTd4uvuVTpiZUicBrdfEpKg,2796
348
348
  alita_sdk/tools/zephyr_squad/api_wrapper.py,sha256=kmw_xol8YIYFplBLWTqP_VKPRhL_1ItDD0_vXTe_UuI,14906
349
349
  alita_sdk/tools/zephyr_squad/zephyr_squad_cloud_client.py,sha256=R371waHsms4sllHCbijKYs90C-9Yu0sSR3N4SUfQOgU,5066
350
- alita_sdk-0.3.284.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
351
- alita_sdk-0.3.284.dist-info/METADATA,sha256=4u6303venAnCzBB7n0UveeWvynBpZTAACQENr6amRqo,18897
352
- alita_sdk-0.3.284.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
353
- alita_sdk-0.3.284.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
354
- alita_sdk-0.3.284.dist-info/RECORD,,
350
+ alita_sdk-0.3.286.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
351
+ alita_sdk-0.3.286.dist-info/METADATA,sha256=b__uoODWVeCvH86q0fXrT3qQpp8Q5-xisgU7QaxEXW4,18897
352
+ alita_sdk-0.3.286.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
353
+ alita_sdk-0.3.286.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
354
+ alita_sdk-0.3.286.dist-info/RECORD,,