alita-sdk 0.3.532__py3-none-any.whl → 0.3.602__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.

Files changed (137) hide show
  1. alita_sdk/cli/agent_executor.py +2 -1
  2. alita_sdk/cli/agent_loader.py +34 -4
  3. alita_sdk/cli/agents.py +433 -203
  4. alita_sdk/community/__init__.py +8 -4
  5. alita_sdk/configurations/__init__.py +1 -0
  6. alita_sdk/configurations/openapi.py +323 -0
  7. alita_sdk/runtime/clients/client.py +165 -7
  8. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  9. alita_sdk/runtime/langchain/assistant.py +61 -11
  10. alita_sdk/runtime/langchain/constants.py +419 -171
  11. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +4 -2
  12. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  13. alita_sdk/runtime/langchain/langraph_agent.py +108 -23
  14. alita_sdk/runtime/langchain/utils.py +76 -14
  15. alita_sdk/runtime/skills/__init__.py +91 -0
  16. alita_sdk/runtime/skills/callbacks.py +498 -0
  17. alita_sdk/runtime/skills/discovery.py +540 -0
  18. alita_sdk/runtime/skills/executor.py +610 -0
  19. alita_sdk/runtime/skills/input_builder.py +371 -0
  20. alita_sdk/runtime/skills/models.py +330 -0
  21. alita_sdk/runtime/skills/registry.py +355 -0
  22. alita_sdk/runtime/skills/skill_runner.py +330 -0
  23. alita_sdk/runtime/toolkits/__init__.py +5 -0
  24. alita_sdk/runtime/toolkits/artifact.py +2 -1
  25. alita_sdk/runtime/toolkits/mcp.py +6 -3
  26. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  27. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  28. alita_sdk/runtime/toolkits/tools.py +139 -10
  29. alita_sdk/runtime/toolkits/vectorstore.py +1 -1
  30. alita_sdk/runtime/tools/__init__.py +3 -1
  31. alita_sdk/runtime/tools/artifact.py +15 -0
  32. alita_sdk/runtime/tools/data_analysis.py +183 -0
  33. alita_sdk/runtime/tools/llm.py +260 -73
  34. alita_sdk/runtime/tools/loop.py +3 -1
  35. alita_sdk/runtime/tools/loop_output.py +3 -1
  36. alita_sdk/runtime/tools/mcp_server_tool.py +6 -3
  37. alita_sdk/runtime/tools/router.py +2 -4
  38. alita_sdk/runtime/tools/sandbox.py +9 -6
  39. alita_sdk/runtime/tools/skill_router.py +776 -0
  40. alita_sdk/runtime/tools/tool.py +3 -1
  41. alita_sdk/runtime/tools/vectorstore.py +7 -2
  42. alita_sdk/runtime/tools/vectorstore_base.py +7 -2
  43. alita_sdk/runtime/utils/constants.py +5 -1
  44. alita_sdk/runtime/utils/mcp_client.py +1 -1
  45. alita_sdk/runtime/utils/mcp_sse_client.py +1 -1
  46. alita_sdk/runtime/utils/toolkit_utils.py +2 -0
  47. alita_sdk/tools/__init__.py +44 -2
  48. alita_sdk/tools/ado/repos/__init__.py +26 -8
  49. alita_sdk/tools/ado/repos/repos_wrapper.py +78 -52
  50. alita_sdk/tools/ado/test_plan/__init__.py +3 -2
  51. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  52. alita_sdk/tools/ado/utils.py +1 -18
  53. alita_sdk/tools/ado/wiki/__init__.py +2 -1
  54. alita_sdk/tools/ado/wiki/ado_wrapper.py +23 -1
  55. alita_sdk/tools/ado/work_item/__init__.py +3 -2
  56. alita_sdk/tools/ado/work_item/ado_wrapper.py +56 -3
  57. alita_sdk/tools/advanced_jira_mining/__init__.py +2 -1
  58. alita_sdk/tools/aws/delta_lake/__init__.py +2 -1
  59. alita_sdk/tools/azure_ai/search/__init__.py +2 -1
  60. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  61. alita_sdk/tools/base_indexer_toolkit.py +51 -30
  62. alita_sdk/tools/bitbucket/__init__.py +2 -1
  63. alita_sdk/tools/bitbucket/api_wrapper.py +1 -1
  64. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +3 -3
  65. alita_sdk/tools/browser/__init__.py +1 -1
  66. alita_sdk/tools/carrier/__init__.py +1 -1
  67. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  68. alita_sdk/tools/cloud/aws/__init__.py +2 -1
  69. alita_sdk/tools/cloud/azure/__init__.py +2 -1
  70. alita_sdk/tools/cloud/gcp/__init__.py +2 -1
  71. alita_sdk/tools/cloud/k8s/__init__.py +2 -1
  72. alita_sdk/tools/code/linter/__init__.py +2 -1
  73. alita_sdk/tools/code/sonar/__init__.py +2 -1
  74. alita_sdk/tools/code_indexer_toolkit.py +19 -2
  75. alita_sdk/tools/confluence/__init__.py +7 -6
  76. alita_sdk/tools/confluence/api_wrapper.py +7 -8
  77. alita_sdk/tools/confluence/loader.py +4 -2
  78. alita_sdk/tools/custom_open_api/__init__.py +2 -1
  79. alita_sdk/tools/elastic/__init__.py +2 -1
  80. alita_sdk/tools/elitea_base.py +28 -9
  81. alita_sdk/tools/figma/__init__.py +52 -6
  82. alita_sdk/tools/figma/api_wrapper.py +1158 -123
  83. alita_sdk/tools/figma/figma_client.py +73 -0
  84. alita_sdk/tools/figma/toon_tools.py +2748 -0
  85. alita_sdk/tools/github/__init__.py +2 -1
  86. alita_sdk/tools/github/github_client.py +56 -92
  87. alita_sdk/tools/github/schemas.py +4 -4
  88. alita_sdk/tools/gitlab/__init__.py +2 -1
  89. alita_sdk/tools/gitlab/api_wrapper.py +118 -38
  90. alita_sdk/tools/gitlab_org/__init__.py +2 -1
  91. alita_sdk/tools/gitlab_org/api_wrapper.py +60 -62
  92. alita_sdk/tools/google/bigquery/__init__.py +2 -1
  93. alita_sdk/tools/google_places/__init__.py +2 -1
  94. alita_sdk/tools/jira/__init__.py +2 -1
  95. alita_sdk/tools/keycloak/__init__.py +2 -1
  96. alita_sdk/tools/localgit/__init__.py +2 -1
  97. alita_sdk/tools/memory/__init__.py +1 -1
  98. alita_sdk/tools/ocr/__init__.py +2 -1
  99. alita_sdk/tools/openapi/__init__.py +490 -118
  100. alita_sdk/tools/openapi/api_wrapper.py +1368 -0
  101. alita_sdk/tools/openapi/tool.py +20 -0
  102. alita_sdk/tools/pandas/__init__.py +11 -5
  103. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  104. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  105. alita_sdk/tools/postman/__init__.py +2 -1
  106. alita_sdk/tools/pptx/__init__.py +2 -1
  107. alita_sdk/tools/qtest/__init__.py +21 -2
  108. alita_sdk/tools/qtest/api_wrapper.py +430 -13
  109. alita_sdk/tools/rally/__init__.py +2 -1
  110. alita_sdk/tools/rally/api_wrapper.py +1 -1
  111. alita_sdk/tools/report_portal/__init__.py +2 -1
  112. alita_sdk/tools/salesforce/__init__.py +2 -1
  113. alita_sdk/tools/servicenow/__init__.py +11 -10
  114. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  115. alita_sdk/tools/sharepoint/__init__.py +2 -1
  116. alita_sdk/tools/sharepoint/api_wrapper.py +2 -2
  117. alita_sdk/tools/slack/__init__.py +3 -2
  118. alita_sdk/tools/slack/api_wrapper.py +2 -2
  119. alita_sdk/tools/sql/__init__.py +3 -2
  120. alita_sdk/tools/testio/__init__.py +2 -1
  121. alita_sdk/tools/testrail/__init__.py +2 -1
  122. alita_sdk/tools/utils/content_parser.py +77 -3
  123. alita_sdk/tools/utils/text_operations.py +163 -71
  124. alita_sdk/tools/xray/__init__.py +3 -2
  125. alita_sdk/tools/yagmail/__init__.py +2 -1
  126. alita_sdk/tools/zephyr/__init__.py +2 -1
  127. alita_sdk/tools/zephyr_enterprise/__init__.py +2 -1
  128. alita_sdk/tools/zephyr_essential/__init__.py +2 -1
  129. alita_sdk/tools/zephyr_scale/__init__.py +3 -2
  130. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  131. alita_sdk/tools/zephyr_squad/__init__.py +2 -1
  132. {alita_sdk-0.3.532.dist-info → alita_sdk-0.3.602.dist-info}/METADATA +7 -6
  133. {alita_sdk-0.3.532.dist-info → alita_sdk-0.3.602.dist-info}/RECORD +137 -119
  134. {alita_sdk-0.3.532.dist-info → alita_sdk-0.3.602.dist-info}/WHEEL +0 -0
  135. {alita_sdk-0.3.532.dist-info → alita_sdk-0.3.602.dist-info}/entry_points.txt +0 -0
  136. {alita_sdk-0.3.532.dist-info → alita_sdk-0.3.602.dist-info}/licenses/LICENSE +0 -0
  137. {alita_sdk-0.3.532.dist-info → alita_sdk-0.3.602.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@ from langchain_core.tools import ToolException
8
8
  from pydantic import model_validator, PrivateAttr, create_model, SecretStr
9
9
  from pydantic.fields import Field
10
10
 
11
- from ..elitea_base import BaseToolApiWrapper
11
+ from ..elitea_base import BaseToolApiWrapper, BaseCodeToolApiWrapper
12
12
  from ..gitlab.utils import get_diff_w_position, get_position
13
13
 
14
14
  logger = logging.getLogger(__name__)
@@ -24,7 +24,7 @@ GitLabCreateBranch = create_model(
24
24
  GitLabListBranches = create_model(
25
25
  "GitLabListBranchesModel",
26
26
  repository=(Optional[str], Field(description="Name of the repository", default=None)),
27
- limit=(Optional[int], Field(description="Maximum number of branches to return. If not provided, all branches will be returned.", default=20)),
27
+ limit=(Optional[int], Field(description="Maximum number of branches to return. If not provided, all branches will be returned.", default=20, gt=0)),
28
28
  branch_wildcard=(Optional[str], Field(description="Wildcard pattern to filter branches by name. If not provided, all branches will be returned.", default=None))
29
29
  )
30
30
 
@@ -159,6 +159,9 @@ class GitLabWorkspaceAPIWrapper(BaseToolApiWrapper):
159
159
  repo_instances: Dict[str, Any] = {}
160
160
  _active_branch: Optional[str] = PrivateAttr(default='main')
161
161
 
162
+ # Reuse common file helpers from BaseCodeToolApiWrapper where applicable
163
+ edit_file = BaseCodeToolApiWrapper.edit_file
164
+
162
165
  class Config:
163
166
  arbitrary_types_allowed = True
164
167
 
@@ -371,51 +374,76 @@ class GitLabWorkspaceAPIWrapper(BaseToolApiWrapper):
371
374
  except Exception as e:
372
375
  return ToolException(e)
373
376
 
374
- def update_file(self, file_path: str, update_query: str, branch: str, repository: Optional[str] = None) -> str:
375
- """Updates a file with new content.
376
- Parameters:
377
- branch (str): The name of the branch where update the file.
378
- update_query(str): Contains file contents.
379
- The old file contents is wrapped in OLD <<<< and >>>> OLD
380
- The new file contents is wrapped in NEW <<<< and >>>> NEW
381
- For example:
382
- /test/hello.txt
383
- OLD <<<<
384
- Hello Earth!
385
- >>>> OLD
386
- NEW <<<<
387
- Hello Mars!
388
- >>>> NEW
377
+ def _read_file(self, file_path: str, branch: str, **kwargs) -> str:
378
+ """
379
+ Internal read_file used by BaseCodeToolApiWrapper.edit_file.
380
+ Delegates to the public `read_file` implementation which supports an optional repository argument.
381
+ The repository may be passed via kwargs or provided earlier through `update_file` which sets
382
+ a temporary attribute `_tmp_repository_for_edit`.
383
+ """
384
+ # Repository from temporary context, then None
385
+ repository = getattr(self, "_tmp_repository_for_edit", None)
386
+ try:
387
+ # Public read_file signature: read_file(file_path, branch, repository=None)
388
+ return self.read_file(file_path, branch, repository)
389
+ except Exception as e:
390
+ raise ToolException(f"Can't extract file content (`{file_path}`) due to error:\n{str(e)}")
391
+
392
+ def _write_file(self, file_path: str, content: str, branch: str = None, commit_message: str = None) -> str:
393
+ """
394
+ Write content to a file (update only) in the specified GitLab repository.
395
+
396
+ This implementation follows the same commit flow as the previous `update_file`:
397
+ it does not attempt to create the file when it is missing — it will always
398
+ create a commit with a single `update` action. If the file does not exist on
399
+ the target branch, the underlying GitLab API will typically return an error.
389
400
  """
390
401
  try:
402
+ branch = branch if branch else (self._active_branch if self._active_branch else self.branch)
403
+ # pick repository from temporary edit context
404
+ repository = getattr(self, "_tmp_repository_for_edit", None)
391
405
  repo_instance = self._get_repo(repository)
392
- file_content = self.read_file(file_path, branch, repository)
393
- updated_file_content = file_content
394
- for old, new in self.extract_old_new_pairs(update_query):
395
- if not old.strip():
396
- continue
397
- updated_file_content = updated_file_content.replace(old, new)
398
- if file_content == updated_file_content:
399
- return (
400
- "File content was not updated because old content was not found or empty."
401
- "It may be helpful to use the read_file action to get "
402
- "the current file contents."
403
- )
406
+
407
+ # Always perform an 'update' action commit (do not create file when missing)
404
408
  commit = {
405
409
  "branch": branch,
406
- "commit_message": "Update " + file_path,
410
+ "commit_message": commit_message or f"Update {file_path}",
407
411
  "actions": [
408
412
  {
409
413
  "action": "update",
410
414
  "file_path": file_path,
411
- "content": updated_file_content,
415
+ "content": content,
412
416
  }
413
417
  ],
414
418
  }
415
419
  repo_instance.commits.create(commit)
416
- return "Updated file " + file_path
420
+ return f"Updated file {file_path}"
421
+ except ToolException:
422
+ raise
423
+ except Exception as e:
424
+ return ToolException(f"Unable to write file due to error: {str(e)}")
425
+
426
+ def update_file(self, file_path: str, update_query: str, branch: str, repository: Optional[str] = None) -> str:
427
+ """Updates a file with new content using OLD/NEW markers by delegating to `edit_file`.
428
+
429
+ The method sets a temporary repository context so that `edit_file`'s internal
430
+ calls to `_read_file` and `_write_file` operate on the requested repository.
431
+ """
432
+ # Set temporary repository context used by _read_file/_write_file
433
+ self._tmp_repository_for_edit = repository
434
+ try:
435
+ commit_message = f"Update {file_path}"
436
+ return self.edit_file(file_path=file_path, file_query=update_query, branch=branch, commit_message=commit_message)
437
+ except ToolException as e:
438
+ return str(e)
417
439
  except Exception as e:
418
440
  return ToolException(f"Unable to update file due to error: {str(e)}")
441
+ finally:
442
+ # Clear temporary context
443
+ try:
444
+ delattr(self, "_tmp_repository_for_edit")
445
+ except Exception:
446
+ self._tmp_repository_for_edit = None
419
447
 
420
448
  def delete_file(self, file_path: str, branch: str, repository: Optional[str] = None) -> str:
421
449
  """Deletes a file from the repo."""
@@ -428,36 +456,6 @@ class GitLabWorkspaceAPIWrapper(BaseToolApiWrapper):
428
456
  except Exception as e:
429
457
  return ToolException(f"Unable to delete file due to error: {str(e)}")
430
458
 
431
- def extract_old_new_pairs(self, file_query):
432
- """Extract old and new content pairs from the file query."""
433
- code_lines = file_query.split("\n")
434
- old_contents = []
435
- new_contents = []
436
- in_old_section = False
437
- in_new_section = False
438
- current_section_content = []
439
- for line in code_lines:
440
- if "OLD <<<" in line:
441
- in_old_section = True
442
- current_section_content = []
443
- continue
444
- if ">>>> OLD" in line:
445
- in_old_section = False
446
- old_contents.append("\n".join(current_section_content).strip())
447
- current_section_content = []
448
- continue
449
- if "NEW <<<" in line:
450
- in_new_section = True
451
- current_section_content = []
452
- continue
453
- if ">>>> NEW" in line:
454
- in_new_section = False
455
- new_contents.append("\n".join(current_section_content).strip())
456
- current_section_content = []
457
- continue
458
- if in_old_section or in_new_section:
459
- current_section_content.append(line)
460
- return list(zip(old_contents, new_contents))
461
459
 
462
460
  def append_file(self, file_path: str, content: str, branch: str, repository: Optional[str] = None) -> str:
463
461
  """
@@ -8,6 +8,7 @@ from ....configurations.bigquery import BigQueryConfiguration
8
8
  from ...utils import clean_string, get_max_toolkit_length
9
9
  from .api_wrapper import BigQueryApiWrapper
10
10
  from .tool import BigQueryAction
11
+ from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
11
12
 
12
13
  name = "bigquery"
13
14
 
@@ -129,7 +130,7 @@ class BigQueryToolkit(BaseToolkit):
129
130
  name=t["name"],
130
131
  description=description,
131
132
  args_schema=t["args_schema"],
132
- metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
133
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: t["name"]} if toolkit_name else {TOOL_NAME_META: t["name"]}
133
134
  )
134
135
  )
135
136
  return instance
@@ -8,6 +8,7 @@ from ..base.tool import BaseAction
8
8
  from ..elitea_base import filter_missconfigured_index_tools
9
9
  from ..utils import clean_string, get_max_toolkit_length
10
10
  from ...configurations.google_places import GooglePlacesConfiguration
11
+ from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
11
12
 
12
13
  name = "google_places"
13
14
 
@@ -67,7 +68,7 @@ class GooglePlacesToolkit(BaseToolkit):
67
68
  name=tool["name"],
68
69
  description=description,
69
70
  args_schema=tool["args_schema"],
70
- metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
71
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
71
72
  ))
72
73
  return cls(tools=tools)
73
74
 
@@ -9,6 +9,7 @@ from ..elitea_base import filter_missconfigured_index_tools
9
9
  from ..utils import clean_string, get_max_toolkit_length, parse_list, check_connection_response
10
10
  from ...configurations.jira import JiraConfiguration
11
11
  from ...configurations.pgvector import PgVectorConfiguration
12
+ from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOLKIT_TYPE_META, TOOL_NAME_META
12
13
 
13
14
  name = "jira"
14
15
 
@@ -126,7 +127,7 @@ class JiraToolkit(BaseToolkit):
126
127
  name=tool["name"],
127
128
  description=description,
128
129
  args_schema=tool["args_schema"],
129
- metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
130
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
130
131
  ))
131
132
  return cls(tools=tools)
132
133
 
@@ -6,6 +6,7 @@ from pydantic import BaseModel, ConfigDict, create_model, Field, SecretStr
6
6
  from .api_wrapper import KeycloakApiWrapper
7
7
  from ..base.tool import BaseAction
8
8
  from ..utils import clean_string, get_max_toolkit_length
9
+ from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
9
10
 
10
11
  name = "keycloak"
11
12
 
@@ -54,7 +55,7 @@ class KeycloakToolkit(BaseToolkit):
54
55
  name=tool["name"],
55
56
  description=description,
56
57
  args_schema=tool["args_schema"],
57
- metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
58
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
58
59
  ))
59
60
  return cls(tools=tools)
60
61
 
@@ -5,6 +5,7 @@ from pydantic import BaseModel, ConfigDict, create_model, Field
5
5
 
6
6
  from .local_git import LocalGit
7
7
  from .tool import LocalGitAction
8
+ from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
8
9
 
9
10
  name = "localgit"
10
11
 
@@ -55,7 +56,7 @@ class AlitaLocalGitToolkit(BaseToolkit):
55
56
  mode=tool["mode"],
56
57
  description=description,
57
58
  args_schema=tool["args_schema"],
58
- metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
59
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
59
60
  ))
60
61
  return cls(tools=tools)
61
62
 
@@ -118,7 +118,7 @@ class MemoryToolkit(BaseToolkit):
118
118
  # Add metadata to tools if toolkit_name is provided
119
119
  if toolkit_name:
120
120
  for tool in tools:
121
- tool.metadata = {"toolkit_name": toolkit_name}
121
+ tool.metadata = {"toolkit_name": toolkit_name, "toolkit_type": name}
122
122
 
123
123
  return cls(tools=tools)
124
124
 
@@ -6,6 +6,7 @@ from pydantic import create_model, BaseModel, ConfigDict, Field
6
6
  from .api_wrapper import OCRApiWrapper
7
7
  from ..base.tool import BaseAction
8
8
  from ..utils import clean_string, get_max_toolkit_length
9
+ from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
9
10
 
10
11
  name = "ocr"
11
12
 
@@ -59,7 +60,7 @@ class OCRToolkit(BaseToolkit):
59
60
  name=tool["name"],
60
61
  description=description,
61
62
  args_schema=tool["args_schema"],
62
- metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
63
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
63
64
  ))
64
65
  return cls(tools=tools)
65
66