alita-sdk 0.3.157__py3-none-any.whl → 0.3.159__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.
@@ -24,7 +24,7 @@ class Artifact:
24
24
  logger.error(f"Error: {e}")
25
25
  return f"Error: {e}"
26
26
 
27
- def get(self, artifact_name: str, bucket_name: str = None, is_capture_image: bool = False, page_number: int = None):
27
+ def get(self, artifact_name: str, bucket_name: str = None, is_capture_image: bool = False, page_number: int = None, sheet_name: str = None):
28
28
  if not bucket_name:
29
29
  bucket_name = self.bucket_name
30
30
  data = self.client.download_artifact(bucket_name, artifact_name)
@@ -37,7 +37,7 @@ class Artifact:
37
37
  if detected['encoding'] is not None:
38
38
  return data.decode(detected['encoding'])
39
39
  else:
40
- return parse_file_content(artifact_name, data, is_capture_image, page_number)
40
+ return parse_file_content(artifact_name, data, is_capture_image, page_number, sheet_name)
41
41
 
42
42
  def delete(self, artifact_name: str, bucket_name = None):
43
43
  if not bucket_name:
@@ -61,8 +61,8 @@ class Assistant:
61
61
  "Review toolkits configuration or use pipeline as master agent.")
62
62
 
63
63
  # configure memory store if memory tool is defined
64
- # memory_tool = next((tool for tool in data['tools'] if tool['type'] == 'memory'), None)
65
- # self._configure_store(memory_tool)
64
+ memory_tool = next((tool for tool in data['tools'] if tool['type'] == 'memory'), None)
65
+ self._configure_store(memory_tool)
66
66
 
67
67
  # Lazy import to avoid circular dependency
68
68
  from ..toolkits.tools import get_tools
@@ -94,7 +94,7 @@ def get_tools(tools_list: list, alita_client, llm, memory_store: BaseStore = Non
94
94
  # Add community tools
95
95
  tools += community_tools(tools_list, alita_client, llm)
96
96
  # Add alita tools
97
- tools += alita_tools(tools_list, alita_client, llm)
97
+ tools += alita_tools(tools_list, alita_client, llm, memory_store)
98
98
  # Add MCP tools
99
99
  tools += _mcp_tools(tools_list, alita_client)
100
100
 
@@ -23,8 +23,8 @@ class ArtifactWrapper(BaseToolApiWrapper):
23
23
  def create_file(self, filename: str, filedata: str, bucket_name = None):
24
24
  return self.artifact.create(filename, filedata, bucket_name)
25
25
 
26
- def read_file(self, filename: str, bucket_name = None, is_capture_image: bool = False, page_number: int = None):
27
- return self.artifact.get(filename, bucket_name, is_capture_image, page_number)
26
+ def read_file(self, filename: str, bucket_name = None, is_capture_image: bool = False, page_number: int = None, sheet_name: str = None):
27
+ return self.artifact.get(filename, bucket_name, is_capture_image, page_number, sheet_name)
28
28
 
29
29
  def delete_file(self, filename: str, bucket_name = None):
30
30
  return self.artifact.delete(filename, bucket_name)
@@ -75,6 +75,9 @@ class ArtifactWrapper(BaseToolApiWrapper):
75
75
  default=False)),
76
76
  page_number=(Optional[int], Field(
77
77
  description="Specifies which page to read. If it is None, then full document will be read.",
78
+ default=None)),
79
+ sheet_name=(Optional[str], Field(
80
+ description="Specifies which sheet to read. If it is None, then full document will be read.",
78
81
  default=None))
79
82
  )
80
83
  },
@@ -1,5 +1,8 @@
1
1
  import logging
2
2
  from importlib import import_module
3
+ from typing import Optional
4
+
5
+ from langgraph.store.base import BaseStore
3
6
 
4
7
  logger = logging.getLogger(__name__)
5
8
 
@@ -74,13 +77,14 @@ _safe_import_tool('carrier', 'carrier', 'get_tools', 'AlitaCarrierToolkit')
74
77
  _safe_import_tool('ocr', 'ocr', 'get_tools', 'OCRToolkit')
75
78
  _safe_import_tool('pptx', 'pptx', 'get_tools', 'PPTXToolkit')
76
79
  _safe_import_tool('postman', 'postman', 'get_tools', 'PostmanToolkit')
80
+ _safe_import_tool('memory', 'memory', 'get_tools', 'MemoryToolkit')
77
81
 
78
82
  # Log import summary
79
83
  available_count = len(AVAILABLE_TOOLS)
80
84
  total_attempted = len(AVAILABLE_TOOLS) + len(FAILED_IMPORTS)
81
85
  logger.info(f"Tool imports completed: {available_count}/{total_attempted} successful")
82
86
 
83
- def get_tools(tools_list, alita, llm, *args, **kwargs):
87
+ def get_tools(tools_list, alita, llm, store: Optional[BaseStore] = None, *args, **kwargs):
84
88
  tools = []
85
89
  for tool in tools_list:
86
90
  # validate tool name syntax - it cannot be started with _
@@ -90,6 +94,7 @@ def get_tools(tools_list, alita, llm, *args, **kwargs):
90
94
 
91
95
  tool['settings']['alita'] = alita
92
96
  tool['settings']['llm'] = llm
97
+ tool['settings']['store'] = store
93
98
  tool_type = tool['type']
94
99
 
95
100
  # Check if tool is available and has get_tools function
@@ -15,6 +15,13 @@ from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
15
15
 
16
16
  name = "memory"
17
17
 
18
+ def get_tools(tool):
19
+ return MemoryToolkit().get_toolkit(
20
+ namespace=tool['settings'].get('namespace', str(tool['id'])),
21
+ store=tool['settings'].get('store', None),
22
+ toolkit_name=tool.get('toolkit_name', '')
23
+ ).get_tools()
24
+
18
25
  class MemoryToolkit(BaseToolkit):
19
26
  tools: List[BaseTool] = []
20
27
 
@@ -23,6 +23,16 @@ PostmanGetCollection = create_model(
23
23
  "PostmanGetCollection"
24
24
  )
25
25
 
26
+ PostmanGetCollectionFlat = create_model(
27
+ "PostmanGetCollectionFlat"
28
+ )
29
+
30
+ PostmanGetFolderFlat = create_model(
31
+ "PostmanGetFolderFlat",
32
+ folder_path=(str, Field(
33
+ description="The path to the folder to parse (e.g., 'API/Users' for nested folders)"))
34
+ )
35
+
26
36
  PostmanGetFolder = create_model(
27
37
  "PostmanGetFolder",
28
38
  folder_path=(str, Field(
@@ -45,24 +55,17 @@ PostmanSearchRequests = create_model(
45
55
  description="Optional HTTP method filter", default=None))
46
56
  )
47
57
 
48
- PostmanAnalyzeCollection = create_model(
49
- "PostmanAnalyzeCollection",
50
- include_improvements=(bool, Field(
51
- description="Include improvement suggestions in the analysis", default=False))
52
- )
53
-
54
- PostmanAnalyzeFolder = create_model(
55
- "PostmanAnalyzeFolder",
56
- folder_path=(str, Field(description="The path to the folder to analyze")),
57
- include_improvements=(bool, Field(
58
- description="Include improvement suggestions in the analysis", default=False))
59
- )
60
-
61
- PostmanAnalyzeRequest = create_model(
62
- "PostmanAnalyzeRequest",
63
- request_path=(str, Field(description="The path to the request to analyze")),
58
+ PostmanAnalyze = create_model(
59
+ "PostmanAnalyze",
60
+ scope=(str, Field(
61
+ description="The scope of analysis: 'collection', 'folder', or 'request'",
62
+ default="collection")),
63
+ target_path=(Optional[str], Field(
64
+ description="The path to the folder or request to analyze (required for folder/request scope)",
65
+ default=None)),
64
66
  include_improvements=(bool, Field(
65
- description="Include improvement suggestions in the analysis", default=False))
67
+ description="Include improvement suggestions in the analysis",
68
+ default=False))
66
69
  )
67
70
 
68
71
  PostmanCreateCollection = create_model(
@@ -235,12 +238,18 @@ PostmanMoveRequest = create_model(
235
238
  description="New folder path", default=None))
236
239
  )
237
240
 
238
- PostmanGetRequest = create_model(
239
- "PostmanGetRequest",
241
+ PostmanGetRequestByPath = create_model(
242
+ "PostmanGetRequestByPath",
240
243
  request_path=(str, Field(
241
244
  description="The path to the request (e.g., 'API/Users/Get User' or 'applications/recommendations')"))
242
245
  )
243
246
 
247
+ PostmanGetRequestById = create_model(
248
+ "PostmanGetRequestById",
249
+ request_id=(str, Field(
250
+ description="The unique ID of the request"))
251
+ )
252
+
244
253
  PostmanGetRequestScript = create_model(
245
254
  "PostmanGetRequestScript",
246
255
  request_path=(str, Field(description="Path to the request (folder/requestName)")),
@@ -319,33 +328,54 @@ class PostmanApiWrapper(BaseToolApiWrapper):
319
328
  "args_schema": PostmanGetCollections,
320
329
  "ref": self.get_collections
321
330
  },
331
+ # {
332
+ # "name": "get_collection",
333
+ # "mode": "get_collection",
334
+ # "description": "Get a specific Postman collection by ID",
335
+ # "args_schema": PostmanGetCollection,
336
+ # "ref": self.get_collection
337
+ # },
322
338
  {
323
339
  "name": "get_collection",
324
340
  "mode": "get_collection",
325
- "description": "Get a specific Postman collection by ID",
326
- "args_schema": PostmanGetCollection,
327
- "ref": self.get_collection
341
+ "description": "Get a specific Postman collection in flattened format with path-based structure",
342
+ "args_schema": PostmanGetCollectionFlat,
343
+ "ref": self.get_collection_flat
328
344
  },
329
345
  {
330
346
  "name": "get_folder",
331
347
  "mode": "get_folder",
332
- "description": "Get folders from a collection by path (supports nested paths like 'API/Users')",
333
- "args_schema": PostmanGetFolder,
334
- "ref": self.get_folder
348
+ "description": "Get a specific folder in flattened format with path-based structure",
349
+ "args_schema": PostmanGetFolderFlat,
350
+ "ref": self.get_folder_flat
335
351
  },
352
+ # {
353
+ # "name": "get_folder",
354
+ # "mode": "get_folder",
355
+ # "description": "Get folders from a collection by path (supports nested paths like 'API/Users')",
356
+ # "args_schema": PostmanGetFolder,
357
+ # "ref": self.get_folder
358
+ # },
359
+ # {
360
+ # "name": "get_folder_requests",
361
+ # "mode": "get_folder_requests",
362
+ # "description": "Get detailed information about all requests in a folder",
363
+ # "args_schema": PostmanGetFolderRequests,
364
+ # "ref": self.get_folder_requests
365
+ # },
336
366
  {
337
- "name": "get_folder_requests",
338
- "mode": "get_folder_requests",
339
- "description": "Get detailed information about all requests in a folder",
340
- "args_schema": PostmanGetFolderRequests,
341
- "ref": self.get_folder_requests
367
+ "name": "get_request_by_path",
368
+ "mode": "get_request_by_path",
369
+ "description": "Get a specific request by path",
370
+ "args_schema": PostmanGetRequestByPath,
371
+ "ref": self.get_request_by_path
342
372
  },
343
373
  {
344
- "name": "get_request",
345
- "mode": "get_request",
346
- "description": "Get a specific request by path",
347
- "args_schema": PostmanGetRequest,
348
- "ref": self.get_request
374
+ "name": "get_request_by_id",
375
+ "mode": "get_request_by_id",
376
+ "description": "Get a specific request by ID",
377
+ "args_schema": PostmanGetRequestById,
378
+ "ref": self.get_request_by_id
349
379
  },
350
380
  {
351
381
  "name": "get_request_script",
@@ -362,40 +392,26 @@ class PostmanApiWrapper(BaseToolApiWrapper):
362
392
  "ref": self.search_requests
363
393
  },
364
394
  {
365
- "name": "analyze_collection",
366
- "mode": "analyze_collection",
367
- "description": "Analyze a collection for API quality, best practices, and issues",
368
- "args_schema": PostmanAnalyzeCollection,
369
- "ref": self.analyze_collection
370
- },
371
- {
372
- "name": "analyze_folder",
373
- "mode": "analyze_folder",
374
- "description": "Analyze a specific folder within a collection",
375
- "args_schema": PostmanAnalyzeFolder,
376
- "ref": self.analyze_folder
377
- },
378
- {
379
- "name": "analyze_request",
380
- "mode": "analyze_request",
381
- "description": "Analyze a specific request within a collection",
382
- "args_schema": PostmanAnalyzeRequest,
383
- "ref": self.analyze_request
384
- },
385
- {
386
- "name": "create_collection",
387
- "mode": "create_collection",
388
- "description": "Create a new Postman collection",
389
- "args_schema": PostmanCreateCollection,
390
- "ref": self.create_collection
391
- },
392
- {
393
- "name": "update_collection_name",
394
- "mode": "update_collection_name",
395
- "description": "Update collection name",
396
- "args_schema": PostmanUpdateCollectionName,
397
- "ref": self.update_collection_name
395
+ "name": "analyze",
396
+ "mode": "analyze",
397
+ "description": "Analyze collection, folder, or request for API quality, best practices, and issues",
398
+ "args_schema": PostmanAnalyze,
399
+ "ref": self.analyze
398
400
  },
401
+ # {
402
+ # "name": "create_collection",
403
+ # "mode": "create_collection",
404
+ # "description": "Create a new Postman collection",
405
+ # "args_schema": PostmanCreateCollection,
406
+ # "ref": self.create_collection
407
+ # },
408
+ # {
409
+ # "name": "update_collection_name",
410
+ # "mode": "update_collection_name",
411
+ # "description": "Update collection name",
412
+ # "args_schema": PostmanUpdateCollectionName,
413
+ # "ref": self.update_collection_name
414
+ # },
399
415
  {
400
416
  "name": "update_collection_description",
401
417
  "mode": "update_collection_description",
@@ -570,8 +586,7 @@ class PostmanApiWrapper(BaseToolApiWrapper):
570
586
  def get_collection(self, **kwargs) -> str:
571
587
  """Get a specific collection by ID."""
572
588
  try:
573
- response = self._make_request(
574
- 'GET', f'/collections/{self.collection_id}')
589
+ response = self._make_request('GET', f'/collections/{self.collection_id}')
575
590
  return json.dumps(response, indent=2)
576
591
  except Exception as e:
577
592
  stacktrace = format_exc()
@@ -580,6 +595,19 @@ class PostmanApiWrapper(BaseToolApiWrapper):
580
595
  raise ToolException(
581
596
  f"Unable to get collection {self.collection_id}: {str(e)}")
582
597
 
598
+ def get_collection_flat(self, **kwargs) -> str:
599
+ """Get a specific collection by ID in flattened format."""
600
+ try:
601
+ response = self._make_request('GET', f'/collections/{self.collection_id}')
602
+ flattened = self.parse_collection_to_flat_structure(response)
603
+ return json.dumps(flattened, indent=2)
604
+ except Exception as e:
605
+ stacktrace = format_exc()
606
+ logger.error(
607
+ f"Exception when getting flattened collection {self.collection_id}: {stacktrace}")
608
+ raise ToolException(
609
+ f"Unable to get flattened collection {self.collection_id}: {str(e)}")
610
+
583
611
  def get_folder(self, folder_path: str, **kwargs) -> str:
584
612
  """Get folders from a collection by path."""
585
613
  try:
@@ -595,6 +623,19 @@ class PostmanApiWrapper(BaseToolApiWrapper):
595
623
  raise ToolException(
596
624
  f"Unable to get folder {folder_path} from collection {self.collection_id}: {str(e)}")
597
625
 
626
+ def get_folder_flat(self, folder_path: str, **kwargs) -> str:
627
+ """Get a specific folder in flattened format with path-based structure."""
628
+ try:
629
+ response = self._make_request('GET', f'/collections/{self.collection_id}')
630
+ flattened = self.parse_collection_to_flat_structure(response, folder_path)
631
+ return json.dumps(flattened, indent=2)
632
+ except Exception as e:
633
+ stacktrace = format_exc()
634
+ logger.error(
635
+ f"Exception when getting flattened folder {folder_path}: {stacktrace}")
636
+ raise ToolException(
637
+ f"Unable to get flattened folder {folder_path}: {str(e)}")
638
+
598
639
  def get_folder_requests(self, folder_path: str, include_details: bool = False, **kwargs) -> str:
599
640
  """Get detailed information about all requests in a folder."""
600
641
  try:
@@ -626,20 +667,55 @@ class PostmanApiWrapper(BaseToolApiWrapper):
626
667
  f"Unable to get requests from folder {folder_path}: {str(e)}")
627
668
 
628
669
  def search_requests(self, query: str, search_in: str = "all", method: str = None, **kwargs) -> str:
629
- """Search for requests across the collection."""
670
+ """Search for requests across the collection and return results in flattened structure."""
630
671
  try:
631
- collection = self._make_request(
672
+ collection_response = self._make_request(
632
673
  'GET', f'/collections/{self.collection_id}')
633
- requests = self.analyzer.search_requests_in_items(
634
- collection['collection']['item'], query, search_in, method)
635
-
674
+
675
+ # Get the collection in flattened structure
676
+ flattened = self.parse_collection_to_flat_structure(collection_response)
677
+
678
+ # Filter only requests that match the search criteria
679
+ matching_requests = {}
680
+
681
+ for path, item in flattened['items'].items():
682
+ if item.get('type') != 'request':
683
+ continue
684
+
685
+ # Apply method filter if specified
686
+ if method and item.get('method', '').upper() != method.upper():
687
+ continue
688
+
689
+ # Apply search criteria
690
+ match_found = False
691
+ query_lower = query.lower()
692
+
693
+ if search_in == "all" or search_in == "name":
694
+ # Extract request name from path (last part after /)
695
+ request_name = path.split('/')[-1] if '/' in path else path
696
+ if query_lower in request_name.lower():
697
+ match_found = True
698
+
699
+ if not match_found and (search_in == "all" or search_in == "url"):
700
+ url = item.get('request_url', '') or item.get('url', '')
701
+ if query_lower in url.lower():
702
+ match_found = True
703
+
704
+ if not match_found and (search_in == "all" or search_in == "description"):
705
+ description = item.get('description', '')
706
+ if isinstance(description, str) and query_lower in description.lower():
707
+ match_found = True
708
+
709
+ if match_found:
710
+ matching_requests[path] = item
711
+
712
+ # Create result structure similar to flattened format
636
713
  result = {
637
- "collection_id": self.collection_id,
638
714
  "query": query,
639
715
  "search_in": search_in,
640
716
  "method_filter": method,
641
- "results_count": len(requests),
642
- "results": requests
717
+ "results_count": len(matching_requests),
718
+ "items": matching_requests
643
719
  }
644
720
 
645
721
  return json.dumps(result, indent=2)
@@ -649,83 +725,85 @@ class PostmanApiWrapper(BaseToolApiWrapper):
649
725
  raise ToolException(
650
726
  f"Unable to search requests in collection {self.collection_id}: {str(e)}")
651
727
 
652
- def analyze_collection(self, include_improvements: bool = False, **kwargs) -> str:
653
- """Analyze a collection for API quality and best practices."""
728
+ def analyze(self, scope: str = "collection", target_path: str = None, include_improvements: bool = False, **kwargs) -> str:
729
+ """Unified analysis method for collection, folder, or request analysis.
730
+
731
+ Args:
732
+ scope: The scope of analysis ('collection', 'folder', or 'request')
733
+ target_path: The path to the folder or request (required for folder/request scope)
734
+ include_improvements: Whether to include improvement suggestions
735
+ """
654
736
  try:
655
- collection = self._make_request(
656
- 'GET', f'/collections/{self.collection_id}')
657
- analysis = self.analyzer.perform_collection_analysis(collection)
737
+ # Validate parameters
738
+ if scope not in ["collection", "folder", "request"]:
739
+ raise ToolException(f"Invalid scope '{scope}'. Must be 'collection', 'folder', or 'request'")
658
740
 
659
- if include_improvements:
660
- improvements = self.analyzer.generate_improvements(analysis)
661
- analysis["improvements"] = improvements
662
- analysis["improvement_count"] = len(improvements)
741
+ if scope in ["folder", "request"] and not target_path:
742
+ raise ToolException(f"target_path is required when scope is '{scope}'")
663
743
 
664
- return json.dumps(analysis, indent=2)
665
- except Exception as e:
666
- stacktrace = format_exc()
667
- logger.error(f"Exception when analyzing collection: {stacktrace}")
668
- raise ToolException(
669
- f"Unable to analyze collection {self.collection_id}: {str(e)}")
670
-
671
- def analyze_folder(self, folder_path: str, include_improvements: bool = False, **kwargs) -> str:
672
- """Analyze a specific folder within a collection."""
673
- try:
744
+ # Get collection data
674
745
  collection = self._make_request(
675
746
  'GET', f'/collections/{self.collection_id}')
676
- folders = self.analyzer.find_folders_by_path(
677
- collection['collection']['item'], folder_path)
678
-
679
- if not folders:
680
- return json.dumps({"error": f"Folder '{folder_path}' not found"}, indent=2)
681
-
682
- folder_analyses = []
683
- for folder in folders:
684
- analysis = self.analyzer.perform_folder_analysis(folder, folder_path)
747
+
748
+ if scope == "collection":
749
+ # Analyze entire collection
750
+ analysis = self.analyzer.perform_collection_analysis(collection)
685
751
 
686
752
  if include_improvements:
687
- improvements = self.analyzer.generate_folder_improvements(analysis)
753
+ improvements = self.analyzer.generate_improvements(analysis)
688
754
  analysis["improvements"] = improvements
689
755
  analysis["improvement_count"] = len(improvements)
690
756
 
691
- folder_analyses.append(analysis)
692
-
693
- return json.dumps(folder_analyses, indent=2)
694
- except Exception as e:
695
- stacktrace = format_exc()
696
- logger.error(f"Exception when analyzing folder: {stacktrace}")
697
- raise ToolException(
698
- f"Unable to analyze folder {folder_path}: {str(e)}")
757
+ return json.dumps(analysis, indent=2)
758
+
759
+ elif scope == "folder":
760
+ # Analyze specific folder
761
+ folders = self.analyzer.find_folders_by_path(
762
+ collection['collection']['item'], target_path)
699
763
 
700
- def analyze_request(self, request_path: str, include_improvements: bool = False, **kwargs) -> str:
701
- """Analyze a specific request within a collection."""
702
- try:
703
- collection = self._make_request(
704
- 'GET', f'/collections/{self.collection_id}')
705
- collection_data = collection["collection"]
764
+ if not folders:
765
+ return json.dumps({"error": f"Folder '{target_path}' not found"}, indent=2)
766
+
767
+ folder_analyses = []
768
+ for folder in folders:
769
+ analysis = self.analyzer.perform_folder_analysis(folder, target_path)
770
+
771
+ if include_improvements:
772
+ improvements = self.analyzer.generate_folder_improvements(analysis)
773
+ analysis["improvements"] = improvements
774
+ analysis["improvement_count"] = len(improvements)
775
+
776
+ folder_analyses.append(analysis)
777
+
778
+ return json.dumps(folder_analyses, indent=2)
779
+
780
+ elif scope == "request":
781
+ # Analyze specific request
782
+ collection_data = collection["collection"]
706
783
 
707
- # Find the request
708
- request_item = self.analyzer.find_request_by_path(
709
- collection_data["item"], request_path)
710
- if not request_item:
711
- raise ToolException(f"Request '{request_path}' not found")
784
+ # Find the request
785
+ request_item = self.analyzer.find_request_by_path(
786
+ collection_data["item"], target_path)
787
+ if not request_item:
788
+ raise ToolException(f"Request '{target_path}' not found")
712
789
 
713
- # Perform request analysis
714
- analysis = self.analyzer.perform_request_analysis(request_item)
715
- analysis["request_path"] = request_path
716
- analysis["collection_id"] = self.collection_id
717
-
718
- if include_improvements:
719
- improvements = self.analyzer.generate_request_improvements(analysis)
720
- analysis["improvements"] = improvements
721
- analysis["improvement_count"] = len(improvements)
790
+ # Perform request analysis
791
+ analysis = self.analyzer.perform_request_analysis(request_item)
792
+ analysis["request_path"] = target_path
793
+ analysis["collection_id"] = self.collection_id
794
+
795
+ if include_improvements:
796
+ improvements = self.analyzer.generate_request_improvements(analysis)
797
+ analysis["improvements"] = improvements
798
+ analysis["improvement_count"] = len(improvements)
722
799
 
723
- return json.dumps(analysis, indent=2)
800
+ return json.dumps(analysis, indent=2)
801
+
724
802
  except Exception as e:
725
803
  stacktrace = format_exc()
726
- logger.error(f"Exception when analyzing request: {stacktrace}")
804
+ logger.error(f"Exception when analyzing {scope}: {stacktrace}")
727
805
  raise ToolException(
728
- f"Unable to analyze request {request_path}: {str(e)}")
806
+ f"Unable to analyze {scope} {target_path or self.collection_id}: {str(e)}")
729
807
 
730
808
  # =================================================================
731
809
  # COLLECTION MANAGEMENT METHODS
@@ -1482,7 +1560,7 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1482
1560
  # HELPER METHODS
1483
1561
  # =================================================================
1484
1562
 
1485
- def get_request(self, request_path: str, **kwargs) -> str:
1563
+ def get_request_by_path(self, request_path: str, **kwargs) -> str:
1486
1564
  """Get a specific request by path.
1487
1565
 
1488
1566
  Uses the _get_request_item_and_id helper to find the request and then fetches complete
@@ -1500,10 +1578,28 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1500
1578
  return json.dumps(response, indent=2)
1501
1579
  except Exception as e:
1502
1580
  stacktrace = format_exc()
1503
- logger.error(f"Exception when getting request: {stacktrace}")
1581
+ logger.error(f"Exception when getting request by path: {stacktrace}")
1504
1582
  raise ToolException(
1505
1583
  f"Unable to get request '{request_path}': {str(e)}")
1506
1584
 
1585
+ def get_request_by_id(self, request_id: str, **kwargs) -> str:
1586
+ """Get a specific request by ID.
1587
+
1588
+ Directly fetches the request using its unique ID from the Postman API.
1589
+ """
1590
+ try:
1591
+ # Fetch the complete request information from the API using the ID
1592
+ response = self._make_request(
1593
+ 'GET', f'/collections/{self.collection_id}/requests/{request_id}'
1594
+ )
1595
+
1596
+ return json.dumps(response, indent=2)
1597
+ except Exception as e:
1598
+ stacktrace = format_exc()
1599
+ logger.error(f"Exception when getting request by ID: {stacktrace}")
1600
+ raise ToolException(
1601
+ f"Unable to get request with ID '{request_id}': {str(e)}")
1602
+
1507
1603
  def get_request_script(self, request_path: str, script_type: str = "prerequest", **kwargs) -> str:
1508
1604
  """
1509
1605
  Get the script (pre-request or test) for a request by path.
@@ -1549,3 +1645,143 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1549
1645
  stacktrace = format_exc()
1550
1646
  logger.error(f"Exception when getting request {script_type} script: {stacktrace}")
1551
1647
  raise ToolException(f"Unable to get {script_type} script for request '{request_path}': {str(e)}")
1648
+
1649
+ def parse_collection_to_flat_structure(self, collection_response: Dict[str, Any], folder_path: str = None) -> Dict[str, Any]:
1650
+ """Parse collection response into a flattened structure with path-based keys.
1651
+
1652
+ Args:
1653
+ collection_response: The Postman collection response JSON
1654
+ folder_path: Optional folder path to filter results. If provided, only items
1655
+ within this folder will be included in the output, and collection
1656
+ metadata will be excluded.
1657
+ """
1658
+ collection = collection_response.get('collection', {})
1659
+ info = collection.get('info', {})
1660
+
1661
+ # If folder_path is specified, return minimal structure focused on the folder
1662
+ if folder_path is not None:
1663
+ result = {
1664
+ "folder_path": folder_path,
1665
+ "items": {}
1666
+ }
1667
+ else:
1668
+ # Full collection structure with metadata
1669
+ result = {
1670
+ "collection_postman_id": info.get('_postman_id'),
1671
+ "name": info.get('name'),
1672
+ "updatedAt": info.get('updatedAt'),
1673
+ "createdAt": info.get('createdAt'),
1674
+ "lastUpdatedBy": info.get('lastUpdatedBy'),
1675
+ "uid": info.get('uid'),
1676
+ "items": {}
1677
+ }
1678
+
1679
+ def parse_items(items, parent_path=""):
1680
+ """Recursively parse items into flat structure."""
1681
+ for item in items:
1682
+ item_name = item.get('name', '')
1683
+ current_path = f"{parent_path}/{item_name}" if parent_path else item_name
1684
+
1685
+ # If folder_path is specified, check if we should include this item
1686
+ if folder_path is not None:
1687
+ # Check if current path is within the specified folder
1688
+ if not (current_path == folder_path or current_path.startswith(folder_path + "/")):
1689
+ # If this is a folder, we need to check if it contains the target folder
1690
+ if 'item' in item and folder_path.startswith(current_path + "/"):
1691
+ # This folder is an ancestor of the target folder, continue traversing
1692
+ parse_items(item['item'], current_path)
1693
+ continue
1694
+
1695
+ # Check if this is a folder (has 'item' property) or a request
1696
+ if 'item' in item:
1697
+ # This is a folder
1698
+ result['items'][current_path] = {
1699
+ "type": "folder",
1700
+ "id": item.get('id'),
1701
+ "uid": item.get('uid')
1702
+ }
1703
+ # Recursively parse nested items
1704
+ parse_items(item['item'], current_path)
1705
+ else:
1706
+ # This is a request
1707
+ request_info = item.get('request', {})
1708
+
1709
+ # Parse URL
1710
+ url_info = request_info.get('url', {})
1711
+ if isinstance(url_info, str):
1712
+ url = url_info
1713
+ else:
1714
+ # URL is an object with raw property
1715
+ url = url_info.get('raw', '')
1716
+
1717
+ # Parse headers
1718
+ headers = request_info.get('header', [])
1719
+
1720
+ # Parse body
1721
+ body_info = None
1722
+ body = request_info.get('body', {})
1723
+ if body:
1724
+ body_mode = body.get('mode', '')
1725
+ if body_mode == 'raw':
1726
+ try:
1727
+ raw_data = body.get('raw', '')
1728
+ if raw_data:
1729
+ body_info = {
1730
+ "type": "json",
1731
+ "data": json.loads(raw_data) if raw_data.strip() else {}
1732
+ }
1733
+ except json.JSONDecodeError:
1734
+ body_info = {
1735
+ "type": "raw",
1736
+ "data": body.get('raw', '')
1737
+ }
1738
+ elif body_mode == 'formdata':
1739
+ body_info = {
1740
+ "type": "formdata",
1741
+ "data": body.get('formdata', [])
1742
+ }
1743
+ elif body_mode == 'urlencoded':
1744
+ body_info = {
1745
+ "type": "urlencoded",
1746
+ "data": body.get('urlencoded', [])
1747
+ }
1748
+
1749
+ # Parse URL parameters
1750
+ params = []
1751
+ if isinstance(url_info, dict):
1752
+ query = url_info.get('query', [])
1753
+ for param in query:
1754
+ if isinstance(param, dict):
1755
+ params.append({
1756
+ "key": param.get('key', ''),
1757
+ "value": param.get('value', ''),
1758
+ "disabled": param.get('disabled', False)
1759
+ })
1760
+
1761
+ request_data = {
1762
+ "id": item.get('id'),
1763
+ "uid": item.get('uid'),
1764
+ "full_postman_path": current_path,
1765
+ "type": "request",
1766
+ "method": request_info.get('method', 'GET'),
1767
+ "request_url": url,
1768
+ "headers": headers,
1769
+ "params": params
1770
+ }
1771
+
1772
+ # Add body if present
1773
+ if body_info:
1774
+ request_data["body"] = body_info
1775
+
1776
+ # Add description if present
1777
+ description = request_info.get('description')
1778
+ if description:
1779
+ request_data["description"] = description
1780
+
1781
+ result['items'][current_path] = request_data
1782
+
1783
+ # Parse the top-level items
1784
+ items = collection.get('item', [])
1785
+ parse_items(items)
1786
+
1787
+ return result
@@ -9,13 +9,13 @@ import pymupdf
9
9
  from langchain_core.tools import ToolException
10
10
  from transformers import BlipProcessor, BlipForConditionalGeneration
11
11
 
12
- def parse_file_content(file_name, file_content, is_capture_image: bool = False, page_number: int = None):
12
+ def parse_file_content(file_name, file_content, is_capture_image: bool = False, page_number: int = None, sheet_name: str = None):
13
13
  if file_name.endswith('.txt'):
14
14
  return parse_txt(file_content)
15
15
  elif file_name.endswith('.docx'):
16
16
  return read_docx_from_bytes(file_content)
17
17
  elif file_name.endswith('.xlsx') or file_name.endswith('.xls'):
18
- return parse_excel(file_content)
18
+ return parse_excel(file_content, sheet_name)
19
19
  elif file_name.endswith('.pdf'):
20
20
  return parse_pdf(file_content, page_number, is_capture_image)
21
21
  elif file_name.endswith('.pptx'):
@@ -30,15 +30,25 @@ def parse_txt(file_content):
30
30
  except Exception as e:
31
31
  return ToolException(f"Error decoding file content: {e}")
32
32
 
33
- def parse_excel(file_content):
33
+ def parse_excel(file_content, sheet_name = None):
34
34
  try:
35
35
  excel_file = io.BytesIO(file_content)
36
- df = pd.read_excel(excel_file)
37
- df.fillna('', inplace=True)
38
- return df.to_string()
36
+ if sheet_name:
37
+ return parse_sheet(excel_file, sheet_name)
38
+ dfs = pd.read_excel(excel_file, sheet_name=sheet_name)
39
+ result = []
40
+ for sheet_name, df in dfs.items():
41
+ df.fillna('', inplace=True)
42
+ result.append(f"=== Sheet: {sheet_name} ===\n{df.to_string(index=False)}")
43
+ return "\n\n".join(result)
39
44
  except Exception as e:
40
45
  return ToolException(f"Error reading Excel file: {e}")
41
46
 
47
+ def parse_sheet(excel_file, sheet_name):
48
+ df = pd.read_excel(excel_file, sheet_name=sheet_name)
49
+ df.fillna('', inplace=True)
50
+ return df.to_string()
51
+
42
52
  def parse_pdf(file_content, page_number, is_capture_image):
43
53
  with pymupdf.open(stream=file_content, filetype="pdf") as report:
44
54
  text_content = ''
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alita_sdk
3
- Version: 0.3.157
3
+ Version: 0.3.159
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>
6
6
  License-Expression: Apache-2.0
@@ -31,8 +31,8 @@ Requires-Dist: langchain_community~=0.3.7; extra == "runtime"
31
31
  Requires-Dist: langchain-openai~=0.3.0; extra == "runtime"
32
32
  Requires-Dist: langgraph-checkpoint-sqlite~=2.0.0; extra == "runtime"
33
33
  Requires-Dist: langgraph-checkpoint-postgres~=2.0.1; extra == "runtime"
34
- Requires-Dist: langsmith==0.1.144; extra == "runtime"
35
- Requires-Dist: langgraph~=0.2.53; extra == "runtime"
34
+ Requires-Dist: langsmith>=0.3.45; extra == "runtime"
35
+ Requires-Dist: langgraph>=0.4.8; extra == "runtime"
36
36
  Requires-Dist: langchain_chroma~=0.2.2; extra == "runtime"
37
37
  Requires-Dist: langchain-unstructured~=0.1.6; extra == "runtime"
38
38
  Requires-Dist: langchain-postgres~=0.0.13; extra == "runtime"
@@ -122,6 +122,7 @@ Requires-Dist: yagmail==0.15.293; extra == "tools"
122
122
  Requires-Dist: pysnc==1.1.10; extra == "tools"
123
123
  Requires-Dist: shortuuid==1.0.13; extra == "tools"
124
124
  Requires-Dist: yarl==1.17.1; extra == "tools"
125
+ Requires-Dist: langmem==0.0.27; extra == "tools"
125
126
  Provides-Extra: community
126
127
  Requires-Dist: retry-extended==0.2.3; extra == "community"
127
128
  Requires-Dist: browser-use==0.1.43; extra == "community"
@@ -43,12 +43,12 @@ alita_sdk/community/deep_researcher/utils/md_to_pdf.py,sha256=EgCaUGLsP5-5F301aB
43
43
  alita_sdk/community/deep_researcher/utils/os.py,sha256=Q1xX7c7_p7EmuzzXIAY9TDmraDNvU0GGcpfgIfWKQ2A,793
44
44
  alita_sdk/runtime/__init__.py,sha256=4W0UF-nl3QF2bvET5lnah4o24CoTwSoKXhuN0YnwvEE,828
45
45
  alita_sdk/runtime/clients/__init__.py,sha256=BdehU5GBztN1Qi1Wul0cqlU46FxUfMnI6Vq2Zd_oq1M,296
46
- alita_sdk/runtime/clients/artifact.py,sha256=33prjst8z3Wn3SZ8Xl2gJIgQmKzaJPDyaVzay87mDes,2643
46
+ alita_sdk/runtime/clients/artifact.py,sha256=4N2t5x3GibyXLq3Fvrv2o_VA7Z000yNfc-UN4eGsHZg,2679
47
47
  alita_sdk/runtime/clients/client.py,sha256=jbC_M72CybwZgFfMRL6paj-NmICrSuk1vVnVTm_u-kc,19734
48
48
  alita_sdk/runtime/clients/datasource.py,sha256=HAZovoQN9jBg0_-lIlGBQzb4FJdczPhkHehAiVG3Wx0,1020
49
49
  alita_sdk/runtime/clients/prompt.py,sha256=li1RG9eBwgNK_Qf0qUaZ8QNTmsncFrAL2pv3kbxZRZg,1447
50
50
  alita_sdk/runtime/langchain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
- alita_sdk/runtime/langchain/assistant.py,sha256=1G4yBhBc-tXgqerujUVu5Z8T49m_7ov-7zYYsm-jGb4,7511
51
+ alita_sdk/runtime/langchain/assistant.py,sha256=QJEMiEOrFMJ4GpnK24U2pKFblrvdQpKFdfhZsI2wAUI,7507
52
52
  alita_sdk/runtime/langchain/chat_message_template.py,sha256=kPz8W2BG6IMyITFDA5oeb5BxVRkHEVZhuiGl4MBZKdc,2176
53
53
  alita_sdk/runtime/langchain/constants.py,sha256=eHVJ_beJNTf1WJo4yq7KMK64fxsRvs3lKc34QCXSbpk,3319
54
54
  alita_sdk/runtime/langchain/indexer.py,sha256=0ENHy5EOhThnAiYFc7QAsaTNp9rr8hDV_hTK8ahbatk,37592
@@ -102,12 +102,12 @@ alita_sdk/runtime/toolkits/artifact.py,sha256=7fTr9VpGd2zwCB3EwW4aqWa5jVKRTunqV3
102
102
  alita_sdk/runtime/toolkits/datasource.py,sha256=qk78OdPoReYPCWwahfkKLbKc4pfsu-061oXRryFLP6I,2498
103
103
  alita_sdk/runtime/toolkits/prompt.py,sha256=WIpTkkVYWqIqOWR_LlSWz3ug8uO9tm5jJ7aZYdiGRn0,1192
104
104
  alita_sdk/runtime/toolkits/subgraph.py,sha256=ZYqI4yVLbEPAjCR8dpXbjbL2ipX598Hk3fL6AgaqFD4,1758
105
- alita_sdk/runtime/toolkits/tools.py,sha256=jGjavYf6tugvLJOU-Vc1L9bDtN295dVK9aqUk8dy490,6114
105
+ alita_sdk/runtime/toolkits/tools.py,sha256=iDWnGpEnmiD226osSlMy7l5suUxOl6vDo8BTtzb8oCw,6128
106
106
  alita_sdk/runtime/toolkits/vectorstore.py,sha256=BGppQADa1ZiLO17fC0uCACTTEvPHlodEDYEzUcBRbAA,2901
107
107
  alita_sdk/runtime/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
108
  alita_sdk/runtime/tools/agent.py,sha256=m98QxOHwnCRTT9j18Olbb5UPS8-ZGeQaGiUyZJSyFck,3162
109
109
  alita_sdk/runtime/tools/application.py,sha256=7XSqwASZGihvQ9uZxnQp61ypFT6twrzBH_BIFC1cX0w,2785
110
- alita_sdk/runtime/tools/artifact.py,sha256=-5c4vAh1EC-brLwjVC2Q2yR8CkHc-A5Pyc-pQgKaOsU,6164
110
+ alita_sdk/runtime/tools/artifact.py,sha256=ILpRclmyHCqz1Byb0zV9LBzI6r9YooNq7-2EYQZCTxU,6412
111
111
  alita_sdk/runtime/tools/datasource.py,sha256=pvbaSfI-ThQQnjHG-QhYNSTYRnZB0rYtZFpjCfpzxYI,2443
112
112
  alita_sdk/runtime/tools/echo.py,sha256=spw9eCweXzixJqHnZofHE1yWiSUa04L4VKycf3KCEaM,486
113
113
  alita_sdk/runtime/tools/function.py,sha256=ZFpd7TGwIawze2e7BHlKwP0NHwNw42wwrmmnXyJQJhk,2600
@@ -129,7 +129,7 @@ alita_sdk/runtime/utils/logging.py,sha256=svPyiW8ztDfhqHFITv5FBCj8UhLxz6hWcqGIY6
129
129
  alita_sdk/runtime/utils/save_dataframe.py,sha256=i-E1wp-t4wb17Zq3nA3xYwgSILjoXNizaQAA9opWvxY,1576
130
130
  alita_sdk/runtime/utils/streamlit.py,sha256=z4J_bdxkA0zMROkvTB4u379YBRFCkKh-h7PD8RlnZWQ,85644
131
131
  alita_sdk/runtime/utils/utils.py,sha256=dM8whOJAuFJFe19qJ69-FLzrUp6d2G-G6L7d4ss2XqM,346
132
- alita_sdk/tools/__init__.py,sha256=qsF21SiBa7P5gcWybCLP4K3xqA34LW9TVuM1QKaU-xc,9716
132
+ alita_sdk/tools/__init__.py,sha256=F1Mrl8jEUUnmfg_VwrNcVSwJKPBp-xmh9vv9goPRA0o,9933
133
133
  alita_sdk/tools/elitea_base.py,sha256=NQaIxPX6DVIerHCb18jwUR6maZxxk73NZaTsFHkBQWE,21119
134
134
  alita_sdk/tools/ado/__init__.py,sha256=mD6GHcYMTtffPJkJvFPe2rzvye_IRmXmWfI7xYuZhO4,912
135
135
  alita_sdk/tools/ado/utils.py,sha256=PTCludvaQmPLakF2EbCGy66Mro4-rjDtavVP-xcB2Wc,1252
@@ -248,7 +248,7 @@ alita_sdk/tools/llm/llm_utils.py,sha256=v3_lWP_Nk6tJLkj0BYohOun0OWNfvzqLjPdPAMl-
248
248
  alita_sdk/tools/localgit/__init__.py,sha256=NScO0Eu-wl-rc63jjD5Qv1RXXB1qukSIJXx-yS_JQLI,2529
249
249
  alita_sdk/tools/localgit/local_git.py,sha256=gsAftNcK7nMCd8VsIkwDLs2SoG0MgpYdkQG5tmoynkA,18074
250
250
  alita_sdk/tools/localgit/tool.py,sha256=It_B24rMvFPurB355Oy5IShg2BsZTASsEoSS8hu2SXw,998
251
- alita_sdk/tools/memory/__init__.py,sha256=QBzuOQapovmbcFS4nG39p3g-fUPp3kQrjh8EGk6VmBs,1901
251
+ alita_sdk/tools/memory/__init__.py,sha256=SOB5Lhf8v8v0-IDUXUgb1KNdv5je-ooi6oGor8iYPpI,2148
252
252
  alita_sdk/tools/ocr/__init__.py,sha256=pvslKVXyJmK0q23FFDNieuc7RBIuzNXTjTNj-GqhGb0,3335
253
253
  alita_sdk/tools/ocr/api_wrapper.py,sha256=08UF8wj1sR8DcW0z16pw19bgLatLkBF8dySW-Ds8iRk,29649
254
254
  alita_sdk/tools/ocr/text_detection.py,sha256=1DBxt54r3_HdEi93QynSIVta3rH3UpIvy799TPtDTtk,23825
@@ -273,7 +273,7 @@ alita_sdk/tools/pandas/statsmodels/descriptive.py,sha256=APdofBnEiRhMrn6tLKwH076
273
273
  alita_sdk/tools/pandas/statsmodels/hypothesis_testing.py,sha256=fdNAayMB3W7avMfKJCcbf2_P54vUXbq8KVebOB48348,10508
274
274
  alita_sdk/tools/pandas/statsmodels/regression.py,sha256=Y1pWK4u_qzrfA740K-FX0nZ5FREGGPk8mfvykPIYoiI,9164
275
275
  alita_sdk/tools/postman/__init__.py,sha256=W0HdtACnTZw6tnzj7_qY_X5RoRyX3czcUSVaZJjBW-Y,4236
276
- alita_sdk/tools/postman/api_wrapper.py,sha256=G6pfCzZUok25qHkiZQvte7lrPYF3gbZX01jx0Si9MN8,66869
276
+ alita_sdk/tools/postman/api_wrapper.py,sha256=4Hf_aOvUB1G_BdlKvNaAqnQaoCnKDph-E7v4VEwaw5Y,77933
277
277
  alita_sdk/tools/postman/postman_analysis.py,sha256=2d-Oi2UORosIePIUyncSONw9hY7dw8Zc7BQvCd4aqpg,45115
278
278
  alita_sdk/tools/pptx/__init__.py,sha256=LNSTQk0BncfdWLXAOGX2WXezG3D4qSEuYwLpokmF9iM,3438
279
279
  alita_sdk/tools/pptx/pptx_wrapper.py,sha256=yyCYcTlIY976kJ4VfPo4dyxj4yeii9j9TWP6W8ZIpN8,29195
@@ -303,7 +303,7 @@ alita_sdk/tools/testio/api_wrapper.py,sha256=BvmL5h634BzG6p7ajnQLmj-uoAw1gjWnd4F
303
303
  alita_sdk/tools/testrail/__init__.py,sha256=83G9oS2fSiATLnW9783LqdoDyubgnmABEk-1hQcsTGE,3805
304
304
  alita_sdk/tools/testrail/api_wrapper.py,sha256=QkF1j2QIdtqeWUZB0GYtdmKATu0gPTQVmM5faK-ASaI,24495
305
305
  alita_sdk/tools/utils/__init__.py,sha256=155xepXPr4OEzs2Mz5YnjXcBpxSv1X2eznRUVoPtyK0,3268
306
- alita_sdk/tools/utils/content_parser.py,sha256=-87Lrl3_50YFFxf6pcIWwyMwEC8tomgcg2qk15urem4,4262
306
+ alita_sdk/tools/utils/content_parser.py,sha256=cdAENBS2-KPBAVbUczsuT-YJEdouKQ0SxCU6bWFfgak,4736
307
307
  alita_sdk/tools/xray/__init__.py,sha256=dn-Ine9mHF8c_yZ-pWkn-gvSvSmGwdrqxPJOz6Cmqc4,3297
308
308
  alita_sdk/tools/xray/api_wrapper.py,sha256=l7Cwvh_5bEaH0IM3yLo1PSClqV1E20wH_sEHaJntM3s,8517
309
309
  alita_sdk/tools/yagmail/__init__.py,sha256=c4Qn3em0tLxzRmFKpzbBgY9W2EnOoKf0azoDJHng5CY,2208
@@ -317,8 +317,8 @@ alita_sdk/tools/zephyr_enterprise/api_wrapper.py,sha256=Ir3zHljhbZQJRJJQOBzS_GL5
317
317
  alita_sdk/tools/zephyr_enterprise/zephyr_enterprise.py,sha256=hV9LIrYfJT6oYp-ZfQR0YHflqBFPsUw2Oc55HwK0H48,6809
318
318
  alita_sdk/tools/zephyr_scale/__init__.py,sha256=2NTcdrfkx4GSegqyXhsPLsEpc4FlACuDy85b0fk6cAo,4572
319
319
  alita_sdk/tools/zephyr_scale/api_wrapper.py,sha256=UHVQUVqcBc3SZvDfO78HSuBzwAsRw2cCDQa-xMOzndE,68663
320
- alita_sdk-0.3.157.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
321
- alita_sdk-0.3.157.dist-info/METADATA,sha256=IO3yoKg0xK703bmf11i1s-ifXSEuaWvZ6al0kqK6r9U,18667
322
- alita_sdk-0.3.157.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
323
- alita_sdk-0.3.157.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
324
- alita_sdk-0.3.157.dist-info/RECORD,,
320
+ alita_sdk-0.3.159.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
321
+ alita_sdk-0.3.159.dist-info/METADATA,sha256=C4uRfDlRMstPa_EMg1hsUN1lHDkB80Xnme6REnRJ_Yk,18714
322
+ alita_sdk-0.3.159.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
323
+ alita_sdk-0.3.159.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
324
+ alita_sdk-0.3.159.dist-info/RECORD,,