alita-sdk 0.3.263__py3-none-any.whl → 0.3.499__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.
Files changed (248) hide show
  1. alita_sdk/cli/__init__.py +10 -0
  2. alita_sdk/cli/__main__.py +17 -0
  3. alita_sdk/cli/agent/__init__.py +5 -0
  4. alita_sdk/cli/agent/default.py +258 -0
  5. alita_sdk/cli/agent_executor.py +155 -0
  6. alita_sdk/cli/agent_loader.py +215 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3601 -0
  9. alita_sdk/cli/callbacks.py +647 -0
  10. alita_sdk/cli/cli.py +168 -0
  11. alita_sdk/cli/config.py +306 -0
  12. alita_sdk/cli/context/__init__.py +30 -0
  13. alita_sdk/cli/context/cleanup.py +198 -0
  14. alita_sdk/cli/context/manager.py +731 -0
  15. alita_sdk/cli/context/message.py +285 -0
  16. alita_sdk/cli/context/strategies.py +289 -0
  17. alita_sdk/cli/context/token_estimation.py +127 -0
  18. alita_sdk/cli/formatting.py +182 -0
  19. alita_sdk/cli/input_handler.py +419 -0
  20. alita_sdk/cli/inventory.py +1256 -0
  21. alita_sdk/cli/mcp_loader.py +315 -0
  22. alita_sdk/cli/toolkit.py +327 -0
  23. alita_sdk/cli/toolkit_loader.py +85 -0
  24. alita_sdk/cli/tools/__init__.py +43 -0
  25. alita_sdk/cli/tools/approval.py +224 -0
  26. alita_sdk/cli/tools/filesystem.py +1751 -0
  27. alita_sdk/cli/tools/planning.py +389 -0
  28. alita_sdk/cli/tools/terminal.py +414 -0
  29. alita_sdk/community/__init__.py +64 -8
  30. alita_sdk/community/inventory/__init__.py +224 -0
  31. alita_sdk/community/inventory/config.py +257 -0
  32. alita_sdk/community/inventory/enrichment.py +2137 -0
  33. alita_sdk/community/inventory/extractors.py +1469 -0
  34. alita_sdk/community/inventory/ingestion.py +3172 -0
  35. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  36. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  37. alita_sdk/community/inventory/parsers/base.py +295 -0
  38. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  39. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  40. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  41. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  42. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  43. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  44. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  45. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  46. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  47. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  48. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  49. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  50. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  51. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  52. alita_sdk/community/inventory/patterns/loader.py +348 -0
  53. alita_sdk/community/inventory/patterns/registry.py +198 -0
  54. alita_sdk/community/inventory/presets.py +535 -0
  55. alita_sdk/community/inventory/retrieval.py +1403 -0
  56. alita_sdk/community/inventory/toolkit.py +173 -0
  57. alita_sdk/community/inventory/visualize.py +1370 -0
  58. alita_sdk/configurations/__init__.py +10 -0
  59. alita_sdk/configurations/ado.py +4 -2
  60. alita_sdk/configurations/azure_search.py +1 -1
  61. alita_sdk/configurations/bigquery.py +1 -1
  62. alita_sdk/configurations/bitbucket.py +94 -2
  63. alita_sdk/configurations/browser.py +18 -0
  64. alita_sdk/configurations/carrier.py +19 -0
  65. alita_sdk/configurations/confluence.py +96 -1
  66. alita_sdk/configurations/delta_lake.py +1 -1
  67. alita_sdk/configurations/figma.py +0 -5
  68. alita_sdk/configurations/github.py +65 -1
  69. alita_sdk/configurations/gitlab.py +79 -0
  70. alita_sdk/configurations/google_places.py +17 -0
  71. alita_sdk/configurations/jira.py +103 -0
  72. alita_sdk/configurations/postman.py +1 -1
  73. alita_sdk/configurations/qtest.py +1 -3
  74. alita_sdk/configurations/report_portal.py +19 -0
  75. alita_sdk/configurations/salesforce.py +19 -0
  76. alita_sdk/configurations/service_now.py +1 -12
  77. alita_sdk/configurations/sharepoint.py +19 -0
  78. alita_sdk/configurations/sonar.py +18 -0
  79. alita_sdk/configurations/sql.py +20 -0
  80. alita_sdk/configurations/testio.py +18 -0
  81. alita_sdk/configurations/testrail.py +88 -0
  82. alita_sdk/configurations/xray.py +94 -1
  83. alita_sdk/configurations/zephyr_enterprise.py +94 -1
  84. alita_sdk/configurations/zephyr_essential.py +95 -0
  85. alita_sdk/runtime/clients/artifact.py +12 -2
  86. alita_sdk/runtime/clients/client.py +235 -66
  87. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  88. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  89. alita_sdk/runtime/clients/sandbox_client.py +373 -0
  90. alita_sdk/runtime/langchain/assistant.py +123 -17
  91. alita_sdk/runtime/langchain/constants.py +8 -1
  92. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  93. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
  94. alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
  95. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +8 -2
  96. alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
  97. alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
  98. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
  99. alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
  100. alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
  101. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
  102. alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
  103. alita_sdk/runtime/langchain/document_loaders/constants.py +187 -40
  104. alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
  105. alita_sdk/runtime/langchain/langraph_agent.py +406 -91
  106. alita_sdk/runtime/langchain/utils.py +51 -8
  107. alita_sdk/runtime/llms/preloaded.py +2 -6
  108. alita_sdk/runtime/models/mcp_models.py +61 -0
  109. alita_sdk/runtime/toolkits/__init__.py +26 -0
  110. alita_sdk/runtime/toolkits/application.py +9 -2
  111. alita_sdk/runtime/toolkits/artifact.py +19 -7
  112. alita_sdk/runtime/toolkits/datasource.py +13 -6
  113. alita_sdk/runtime/toolkits/mcp.py +780 -0
  114. alita_sdk/runtime/toolkits/planning.py +178 -0
  115. alita_sdk/runtime/toolkits/subgraph.py +11 -6
  116. alita_sdk/runtime/toolkits/tools.py +214 -60
  117. alita_sdk/runtime/toolkits/vectorstore.py +9 -4
  118. alita_sdk/runtime/tools/__init__.py +22 -0
  119. alita_sdk/runtime/tools/application.py +16 -4
  120. alita_sdk/runtime/tools/artifact.py +312 -19
  121. alita_sdk/runtime/tools/function.py +100 -4
  122. alita_sdk/runtime/tools/graph.py +81 -0
  123. alita_sdk/runtime/tools/image_generation.py +212 -0
  124. alita_sdk/runtime/tools/llm.py +539 -180
  125. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  126. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  127. alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
  128. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  129. alita_sdk/runtime/tools/planning/models.py +246 -0
  130. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  131. alita_sdk/runtime/tools/router.py +2 -1
  132. alita_sdk/runtime/tools/sandbox.py +375 -0
  133. alita_sdk/runtime/tools/vectorstore.py +62 -63
  134. alita_sdk/runtime/tools/vectorstore_base.py +156 -85
  135. alita_sdk/runtime/utils/AlitaCallback.py +106 -20
  136. alita_sdk/runtime/utils/mcp_client.py +465 -0
  137. alita_sdk/runtime/utils/mcp_oauth.py +244 -0
  138. alita_sdk/runtime/utils/mcp_sse_client.py +405 -0
  139. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  140. alita_sdk/runtime/utils/streamlit.py +41 -14
  141. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  142. alita_sdk/runtime/utils/utils.py +14 -0
  143. alita_sdk/tools/__init__.py +78 -35
  144. alita_sdk/tools/ado/__init__.py +0 -1
  145. alita_sdk/tools/ado/repos/__init__.py +10 -6
  146. alita_sdk/tools/ado/repos/repos_wrapper.py +12 -11
  147. alita_sdk/tools/ado/test_plan/__init__.py +10 -7
  148. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -23
  149. alita_sdk/tools/ado/wiki/__init__.py +10 -11
  150. alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -28
  151. alita_sdk/tools/ado/work_item/__init__.py +10 -11
  152. alita_sdk/tools/ado/work_item/ado_wrapper.py +63 -10
  153. alita_sdk/tools/advanced_jira_mining/__init__.py +10 -7
  154. alita_sdk/tools/aws/delta_lake/__init__.py +13 -11
  155. alita_sdk/tools/azure_ai/search/__init__.py +11 -7
  156. alita_sdk/tools/base_indexer_toolkit.py +392 -86
  157. alita_sdk/tools/bitbucket/__init__.py +18 -11
  158. alita_sdk/tools/bitbucket/api_wrapper.py +52 -9
  159. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
  160. alita_sdk/tools/browser/__init__.py +40 -16
  161. alita_sdk/tools/browser/crawler.py +3 -1
  162. alita_sdk/tools/browser/utils.py +15 -6
  163. alita_sdk/tools/carrier/__init__.py +17 -17
  164. alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
  165. alita_sdk/tools/carrier/excel_reporter.py +8 -4
  166. alita_sdk/tools/chunkers/__init__.py +3 -1
  167. alita_sdk/tools/chunkers/code/codeparser.py +1 -1
  168. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  169. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  170. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  171. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  172. alita_sdk/tools/cloud/aws/__init__.py +9 -6
  173. alita_sdk/tools/cloud/azure/__init__.py +9 -6
  174. alita_sdk/tools/cloud/gcp/__init__.py +9 -6
  175. alita_sdk/tools/cloud/k8s/__init__.py +9 -6
  176. alita_sdk/tools/code/linter/__init__.py +7 -7
  177. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  178. alita_sdk/tools/code/sonar/__init__.py +18 -12
  179. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  180. alita_sdk/tools/confluence/__init__.py +14 -11
  181. alita_sdk/tools/confluence/api_wrapper.py +198 -58
  182. alita_sdk/tools/confluence/loader.py +10 -0
  183. alita_sdk/tools/custom_open_api/__init__.py +9 -4
  184. alita_sdk/tools/elastic/__init__.py +8 -7
  185. alita_sdk/tools/elitea_base.py +543 -64
  186. alita_sdk/tools/figma/__init__.py +10 -8
  187. alita_sdk/tools/figma/api_wrapper.py +352 -153
  188. alita_sdk/tools/github/__init__.py +13 -11
  189. alita_sdk/tools/github/api_wrapper.py +9 -26
  190. alita_sdk/tools/github/github_client.py +75 -12
  191. alita_sdk/tools/github/schemas.py +2 -1
  192. alita_sdk/tools/gitlab/__init__.py +11 -10
  193. alita_sdk/tools/gitlab/api_wrapper.py +135 -45
  194. alita_sdk/tools/gitlab_org/__init__.py +11 -9
  195. alita_sdk/tools/google/bigquery/__init__.py +12 -13
  196. alita_sdk/tools/google_places/__init__.py +18 -10
  197. alita_sdk/tools/jira/__init__.py +14 -8
  198. alita_sdk/tools/jira/api_wrapper.py +315 -168
  199. alita_sdk/tools/keycloak/__init__.py +8 -7
  200. alita_sdk/tools/localgit/local_git.py +56 -54
  201. alita_sdk/tools/memory/__init__.py +27 -11
  202. alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
  203. alita_sdk/tools/ocr/__init__.py +8 -7
  204. alita_sdk/tools/openapi/__init__.py +10 -1
  205. alita_sdk/tools/pandas/__init__.py +8 -7
  206. alita_sdk/tools/pandas/api_wrapper.py +7 -25
  207. alita_sdk/tools/postman/__init__.py +8 -10
  208. alita_sdk/tools/postman/api_wrapper.py +19 -8
  209. alita_sdk/tools/postman/postman_analysis.py +8 -1
  210. alita_sdk/tools/pptx/__init__.py +8 -9
  211. alita_sdk/tools/qtest/__init__.py +19 -13
  212. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  213. alita_sdk/tools/rally/__init__.py +10 -9
  214. alita_sdk/tools/report_portal/__init__.py +20 -15
  215. alita_sdk/tools/salesforce/__init__.py +19 -15
  216. alita_sdk/tools/servicenow/__init__.py +14 -11
  217. alita_sdk/tools/sharepoint/__init__.py +14 -13
  218. alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
  219. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  220. alita_sdk/tools/sharepoint/utils.py +8 -2
  221. alita_sdk/tools/slack/__init__.py +10 -7
  222. alita_sdk/tools/sql/__init__.py +19 -18
  223. alita_sdk/tools/sql/api_wrapper.py +71 -23
  224. alita_sdk/tools/testio/__init__.py +18 -12
  225. alita_sdk/tools/testrail/__init__.py +10 -10
  226. alita_sdk/tools/testrail/api_wrapper.py +213 -45
  227. alita_sdk/tools/utils/__init__.py +28 -4
  228. alita_sdk/tools/utils/content_parser.py +181 -61
  229. alita_sdk/tools/utils/text_operations.py +254 -0
  230. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
  231. alita_sdk/tools/xray/__init__.py +12 -7
  232. alita_sdk/tools/xray/api_wrapper.py +58 -113
  233. alita_sdk/tools/zephyr/__init__.py +9 -6
  234. alita_sdk/tools/zephyr_enterprise/__init__.py +13 -8
  235. alita_sdk/tools/zephyr_enterprise/api_wrapper.py +17 -7
  236. alita_sdk/tools/zephyr_essential/__init__.py +13 -9
  237. alita_sdk/tools/zephyr_essential/api_wrapper.py +289 -47
  238. alita_sdk/tools/zephyr_essential/client.py +6 -4
  239. alita_sdk/tools/zephyr_scale/__init__.py +10 -7
  240. alita_sdk/tools/zephyr_scale/api_wrapper.py +6 -2
  241. alita_sdk/tools/zephyr_squad/__init__.py +9 -6
  242. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/METADATA +180 -33
  243. alita_sdk-0.3.499.dist-info/RECORD +433 -0
  244. alita_sdk-0.3.499.dist-info/entry_points.txt +2 -0
  245. alita_sdk-0.3.263.dist-info/RECORD +0 -342
  246. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/WHEEL +0 -0
  247. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/licenses/LICENSE +0 -0
  248. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,7 @@ from pydantic import BaseModel, ConfigDict, create_model, Field, SecretStr
5
5
 
6
6
  from .api_wrapper import KeycloakApiWrapper
7
7
  from ..base.tool import BaseAction
8
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
8
+ from ..utils import clean_string, get_max_toolkit_length
9
9
 
10
10
  name = "keycloak"
11
11
 
@@ -21,15 +21,13 @@ def get_tools(tool):
21
21
 
22
22
  class KeycloakToolkit(BaseToolkit):
23
23
  tools: list[BaseTool] = []
24
- toolkit_max_length: int = 0
25
24
 
26
25
  @staticmethod
27
26
  def toolkit_config_schema() -> BaseModel:
28
27
  selected_tools = {x['name']: x['args_schema'].schema() for x in KeycloakApiWrapper.model_construct().get_available_tools()}
29
- KeycloakToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
30
28
  return create_model(
31
29
  name,
32
- base_url=(str, Field(default="", title="Server URL", description="Keycloak server URL", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': KeycloakToolkit.toolkit_max_length})),
30
+ base_url=(str, Field(default="", title="Server URL", description="Keycloak server URL", json_schema_extra={'toolkit_name': True})),
33
31
  realm=(str, Field(default="", title="Realm", description="Keycloak realm")),
34
32
  client_id=(str, Field(default="", title="Client ID", description="Keycloak client ID")),
35
33
  client_secret=(SecretStr, Field(default="", title="Client sercet", description="Keycloak client secret", json_schema_extra={'secret': True})),
@@ -42,16 +40,19 @@ class KeycloakToolkit(BaseToolkit):
42
40
  if selected_tools is None:
43
41
  selected_tools = []
44
42
  keycloak_api_wrapper = KeycloakApiWrapper(**kwargs)
45
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
46
43
  available_tools = keycloak_api_wrapper.get_available_tools()
47
44
  tools = []
48
45
  for tool in available_tools:
49
46
  if selected_tools and tool["name"] not in selected_tools:
50
47
  continue
48
+ description = f"{tool['description']}\nUrl: {keycloak_api_wrapper.base_url}"
49
+ if toolkit_name:
50
+ description = f"{description}\nToolkit: {toolkit_name}"
51
+ description = description[:1000]
51
52
  tools.append(BaseAction(
52
53
  api_wrapper=keycloak_api_wrapper,
53
- name=prefix + tool["name"],
54
- description=f"{tool['description']}\nUrl: {keycloak_api_wrapper.base_url}",
54
+ name=tool["name"],
55
+ description=description,
55
56
  args_schema=tool["args_schema"]
56
57
  ))
57
58
  return cls(tools=tools)
@@ -8,7 +8,8 @@ from git import Repo
8
8
  from pydantic import BaseModel, Field, create_model, model_validator
9
9
  from langchain_core.tools import ToolException
10
10
 
11
- from ..elitea_base import BaseToolApiWrapper
11
+ from ..elitea_base import BaseToolApiWrapper, extend_with_file_operations
12
+ from ..utils.text_operations import parse_old_new_markers
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
  CREATE_FILE_PROMPT = """Create new file in your local repository."""
@@ -128,58 +129,6 @@ class LocalGit(BaseToolApiWrapper):
128
129
  repo.head.reset(commit=commit_sha, working_tree=True)
129
130
  return values
130
131
 
131
- def extract_old_new_pairs(self, file_query):
132
- # Split the file content by lines
133
- code_lines = file_query.split("\n")
134
-
135
- # Initialize lists to hold the contents of OLD and NEW sections
136
- old_contents = []
137
- new_contents = []
138
-
139
- # Initialize variables to track whether the current line is within an OLD or NEW section
140
- in_old_section = False
141
- in_new_section = False
142
-
143
- # Temporary storage for the current section's content
144
- current_section_content = []
145
-
146
- # Iterate through each line in the file content
147
- for line in code_lines:
148
- # Check for OLD section start
149
- if "OLD <<<" in line:
150
- in_old_section = True
151
- current_section_content = [] # Reset current section content
152
- continue # Skip the line with the marker
153
-
154
- # Check for OLD section end
155
- if ">>>> OLD" in line:
156
- in_old_section = False
157
- old_contents.append("\n".join(current_section_content).strip()) # Add the captured content
158
- current_section_content = [] # Reset current section content
159
- continue # Skip the line with the marker
160
-
161
- # Check for NEW section start
162
- if "NEW <<<" in line:
163
- in_new_section = True
164
- current_section_content = [] # Reset current section content
165
- continue # Skip the line with the marker
166
-
167
- # Check for NEW section end
168
- if ">>>> NEW" in line:
169
- in_new_section = False
170
- new_contents.append("\n".join(current_section_content).strip()) # Add the captured content
171
- current_section_content = [] # Reset current section content
172
- continue # Skip the line with the marker
173
-
174
- # If currently in an OLD or NEW section, add the line to the current section content
175
- if in_old_section or in_new_section:
176
- current_section_content.append(line)
177
-
178
- # Pair the OLD and NEW contents
179
- paired_contents = list(zip(old_contents, new_contents))
180
-
181
- return paired_contents
182
-
183
132
  def checkout_commit(self, commit_sha: str) -> str:
184
133
  """ Checkout specific commit from repository """
185
134
  try:
@@ -233,6 +182,58 @@ class LocalGit(BaseToolApiWrapper):
233
182
  return f.read()
234
183
  else:
235
184
  return "File '{}' cannot be read because it is not existed".format(file_path)
185
+
186
+ def _read_file(self, file_path: str, branch: str = None, **kwargs) -> str:
187
+ """
188
+ Read a file from the repository with optional partial read support.
189
+
190
+ Parameters:
191
+ file_path: the file path (relative to repo root)
192
+ branch: branch name (not used for local git, always reads from working dir)
193
+ **kwargs: Additional parameters (offset, limit, head, tail) - currently ignored,
194
+ partial read handled client-side by base class methods
195
+
196
+ Returns:
197
+ File content as string
198
+ """
199
+ return self.read_file(file_path)
200
+
201
+ def _write_file(
202
+ self,
203
+ file_path: str,
204
+ content: str,
205
+ branch: str = None,
206
+ commit_message: str = None
207
+ ) -> str:
208
+ """
209
+ Write content to a file (create or update).
210
+
211
+ Parameters:
212
+ file_path: Path to the file (relative to repo root)
213
+ content: New file content
214
+ branch: Branch name (not used for local git)
215
+ commit_message: Commit message (not used - files are written without commit)
216
+
217
+ Returns:
218
+ Success message
219
+ """
220
+ try:
221
+ full_path = os.path.normpath(os.path.join(self.repo.working_dir, file_path))
222
+
223
+ # Ensure directory exists
224
+ os.makedirs(os.path.dirname(full_path), exist_ok=True)
225
+
226
+ # Write the file
227
+ with open(full_path, 'w') as f:
228
+ f.write(content)
229
+
230
+ # Determine if file was created or updated
231
+ if os.path.exists(full_path):
232
+ return f"Updated file {file_path}"
233
+ else:
234
+ return f"Created file {file_path}"
235
+ except Exception as e:
236
+ raise ToolException(f"Unable to write file {file_path}: {str(e)}")
236
237
 
237
238
  def update_file_content_by_lines(self, file_path: str, start_line_index: int, end_line_index: int,
238
239
  new_content: str) -> str:
@@ -314,7 +315,7 @@ class LocalGit(BaseToolApiWrapper):
314
315
  file_path = os.path.normpath(os.path.join(self.repo.working_dir, file_path))
315
316
  file_content = self.read_file(file_path)
316
317
  updated_file_content = file_content
317
- for old, new in self.extract_old_new_pairs(file_query):
318
+ for old, new in parse_old_new_markers(file_query): # Use shared utility
318
319
  if not old.strip():
319
320
  continue
320
321
  updated_file_content = updated_file_content.replace(old, new)
@@ -332,6 +333,7 @@ class LocalGit(BaseToolApiWrapper):
332
333
  except Exception as e:
333
334
  return "Unable to update file due to error:\n" + str(e)
334
335
 
336
+ @extend_with_file_operations
335
337
  def get_available_tools(self):
336
338
  return [
337
339
  {
@@ -1,6 +1,8 @@
1
- from typing import Optional, List
1
+ from typing import List, Literal
2
2
 
3
- from langchain_core.tools import BaseToolkit, BaseTool
3
+ from langchain_core.tools import BaseToolkit, BaseTool, ToolException
4
+
5
+ from alita_sdk.configurations.pgvector import PgVectorConfiguration
4
6
 
5
7
  try:
6
8
  from langmem import create_manage_memory_tool, create_search_memory_tool
@@ -15,7 +17,7 @@ from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
15
17
 
16
18
  name = "memory"
17
19
 
18
- def get_tools(tools_list: list, alita_client, llm, memory_store=None):
20
+ def get_tools(tools_list: list, memory_store=None):
19
21
  """
20
22
  Get memory tools for the provided tool configurations.
21
23
 
@@ -35,8 +37,9 @@ def get_tools(tools_list: list, alita_client, llm, memory_store=None):
35
37
  try:
36
38
  toolkit_instance = MemoryToolkit().get_toolkit(
37
39
  namespace=tool['settings'].get('namespace', str(tool['id'])),
38
- username=tool['settings'].get('username', ''),
40
+ # username=tool['settings'].get('username', ''),
39
41
  store=tool['settings'].get('store', memory_store),
42
+ pgvector_configuration=tool['settings'].get('pgvector_configuration', {}),
40
43
  toolkit_name=tool.get('toolkit_name', '')
41
44
  )
42
45
  all_tools.extend(toolkit_instance.get_tools())
@@ -53,18 +56,23 @@ class MemoryToolkit(BaseToolkit):
53
56
 
54
57
  @staticmethod
55
58
  def toolkit_config_schema() -> BaseModel:
59
+ memory_tools = [create_manage_memory_tool('test'), create_search_memory_tool('test')]
60
+ selected_tools = {x.name: x.args_schema.schema() for x in memory_tools}
61
+
56
62
  return create_model(
57
- 'MemoryConfig',
58
- namespace=(str, Field(description="Memory namespace", json_schema_extra={'toolkit_name': True})),
59
- username=(Optional[str], Field(description="Username", default='Tester', json_schema_extra={'hidden': True})),
60
- connection_string=(Optional[SecretStr], Field(description="Connection string for vectorstore",
61
- default=None,
62
- json_schema_extra={'secret': True})),
63
+ 'memory',
64
+ namespace=(str, Field(description="Memory namespace")),
65
+ pgvector_configuration=(PgVectorConfiguration, Field(description="PgVector Configuration",
66
+ json_schema_extra={
67
+ 'configuration_types': ['pgvector']})),
68
+ selected_tools=(List[Literal[tuple(selected_tools)]],
69
+ Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
70
+
63
71
  __config__=ConfigDict(json_schema_extra={
64
72
  'metadata': {
65
73
  "label": "Memory",
66
74
  "icon_url": "memory.svg",
67
- "hidden": True,
75
+ "hidden": False,
68
76
  "categories": ["other"],
69
77
  "extra_categories": ["long-term memory", "langmem"],
70
78
  }
@@ -88,6 +96,14 @@ class MemoryToolkit(BaseToolkit):
88
96
  "PostgreSQL dependencies (psycopg) are required for MemoryToolkit. "
89
97
  "Install with: pip install psycopg[binary]"
90
98
  )
99
+
100
+ if store is None:
101
+ # The store is not provided, attempt to create it from configuration
102
+ from ...runtime.langchain.store_manager import get_manager
103
+ conn_str = (kwargs.get('pgvector_configuration') or {}).get('connection_string', '')
104
+ if not conn_str:
105
+ raise ToolException("Connection string is required to create PostgresStore for memory toolkit.")
106
+ store = get_manager().get_store(conn_str)
91
107
 
92
108
  # Validate store type
93
109
  if store is not None and not isinstance(store, PostgresStore):
@@ -1,12 +1,17 @@
1
1
  from langchain_core.documents import Document
2
+ from langchain_core.tools import ToolException
2
3
 
3
4
  from alita_sdk.runtime.utils.utils import IndexerKeywords
4
5
  from alita_sdk.tools.base_indexer_toolkit import BaseIndexerToolkit
5
6
 
6
7
 
7
8
  class NonCodeIndexerToolkit(BaseIndexerToolkit):
8
- def _get_indexed_data(self, collection_suffix: str):
9
- return self.vector_adapter.get_indexed_data(self, collection_suffix)
9
+ def _get_indexed_data(self, index_name: str):
10
+ self._ensure_vectorstore_initialized()
11
+ if not self.vector_adapter:
12
+ raise ToolException("Vector adapter is not initialized. "
13
+ "Check your configuration: embedding_model and vectorstore_type.")
14
+ return self.vector_adapter.get_indexed_data(self, index_name)
10
15
 
11
16
  def key_fn(self, document: Document):
12
17
  return document.metadata.get('id')
@@ -5,7 +5,7 @@ from pydantic import create_model, BaseModel, ConfigDict, Field
5
5
 
6
6
  from .api_wrapper import OCRApiWrapper
7
7
  from ..base.tool import BaseAction
8
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
8
+ from ..utils import clean_string, get_max_toolkit_length
9
9
 
10
10
  name = "ocr"
11
11
 
@@ -23,15 +23,13 @@ def get_tools(tool):
23
23
 
24
24
  class OCRToolkit(BaseToolkit):
25
25
  tools: list[BaseTool] = []
26
- toolkit_max_length: int = 0
27
26
 
28
27
  @staticmethod
29
28
  def toolkit_config_schema() -> BaseModel:
30
29
  selected_tools = {x['name']: x['args_schema'].schema() for x in OCRApiWrapper.model_construct().get_available_tools()}
31
- OCRToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
32
30
  return create_model(
33
31
  name,
34
- artifacts_folder=(str, Field(description="Folder path containing artifacts to process", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': OCRToolkit.toolkit_max_length})),
32
+ artifacts_folder=(str, Field(description="Folder path containing artifacts to process", json_schema_extra={'toolkit_name': True})),
35
33
  tesseract_settings=(dict, Field(description="Settings for Tesseract OCR processing", default={})),
36
34
  structured_output=(bool, Field(description="Whether to return structured JSON output", default=False)),
37
35
  expected_fields=(dict, Field(description="Expected fields for structured output", default={})),
@@ -47,16 +45,19 @@ class OCRToolkit(BaseToolkit):
47
45
  if selected_tools is None:
48
46
  selected_tools = []
49
47
  ocr_api_wrapper = OCRApiWrapper(**kwargs)
50
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
51
48
  available_tools = ocr_api_wrapper.get_available_tools()
52
49
  tools = []
53
50
  for tool in available_tools:
54
51
  if selected_tools and tool["name"] not in selected_tools:
55
52
  continue
53
+ description = tool["description"]
54
+ if toolkit_name:
55
+ description = f"Toolkit: {toolkit_name}\n{description}"
56
+ description = description[:1000]
56
57
  tools.append(BaseAction(
57
58
  api_wrapper=ocr_api_wrapper,
58
- name=prefix + tool["name"],
59
- description=tool["description"],
59
+ name=tool["name"],
60
+ description=description,
60
61
  args_schema=tool["args_schema"]
61
62
  ))
62
63
  return cls(tools=tools)
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import re
3
3
  import logging
4
+ import yaml
4
5
  from typing import List, Any, Optional, Dict
5
6
  from langchain_core.tools import BaseTool, BaseToolkit, ToolException
6
7
  from requests_openapi import Operation, Client, Server
@@ -101,7 +102,15 @@ class AlitaOpenAPIToolkit(BaseToolkit):
101
102
  else:
102
103
  tools_set = {}
103
104
  if isinstance(openapi_spec, str):
104
- openapi_spec = json.loads(openapi_spec)
105
+ # Try to detect if it's YAML or JSON by attempting to parse as JSON first
106
+ try:
107
+ openapi_spec = json.loads(openapi_spec)
108
+ except json.JSONDecodeError:
109
+ # If JSON parsing fails, try YAML
110
+ try:
111
+ openapi_spec = yaml.safe_load(openapi_spec)
112
+ except yaml.YAMLError as e:
113
+ raise ToolException(f"Failed to parse OpenAPI spec as JSON or YAML: {e}")
105
114
  c = Client()
106
115
  c.load_spec(openapi_spec)
107
116
  if headers:
@@ -5,7 +5,7 @@ from pydantic import BaseModel, ConfigDict, create_model, Field
5
5
 
6
6
  from .api_wrapper import PandasWrapper
7
7
  from ..base.tool import BaseAction
8
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
8
+ from ..utils import clean_string, get_max_toolkit_length
9
9
 
10
10
  name = "pandas"
11
11
 
@@ -21,15 +21,13 @@ def get_tools(tool):
21
21
 
22
22
  class PandasToolkit(BaseToolkit):
23
23
  tools: List[BaseTool] = []
24
- toolkit_max_length: int = 0
25
24
 
26
25
  @staticmethod
27
26
  def toolkit_config_schema() -> BaseModel:
28
27
  selected_tools = {x['name']: x['args_schema'].schema() for x in PandasWrapper.model_construct().get_available_tools()}
29
- PandasToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
30
28
  return create_model(
31
29
  name,
32
- bucket_name=(str, Field(default=None, title="Bucket name", description="Bucket where the content file is stored", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': PandasToolkit.toolkit_max_length})),
30
+ bucket_name=(str, Field(default=None, title="Bucket name", description="Bucket where the content file is stored")),
33
31
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
34
32
  __config__=ConfigDict(json_schema_extra={'metadata': {"label": "Pandas", "icon_url": "pandas-icon.svg",
35
33
  "categories": ["analysis"],
@@ -41,16 +39,19 @@ class PandasToolkit(BaseToolkit):
41
39
  if selected_tools is None:
42
40
  selected_tools = []
43
41
  csv_tool_api_wrapper = PandasWrapper(**kwargs)
44
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
45
42
  available_tools = csv_tool_api_wrapper.get_available_tools()
46
43
  tools = []
47
44
  for tool in available_tools:
48
45
  if selected_tools and tool["name"] not in selected_tools:
49
46
  continue
47
+ description = tool["description"]
48
+ if toolkit_name:
49
+ description = f"Toolkit: {toolkit_name}\n{description}"
50
+ description = description[:1000]
50
51
  tools.append(BaseAction(
51
52
  api_wrapper=csv_tool_api_wrapper,
52
- name=prefix + tool["name"],
53
- description=tool["description"],
53
+ name=tool["name"],
54
+ description=description,
54
55
  args_schema=tool["args_schema"]
55
56
  ))
56
57
  return cls(tools=tools)
@@ -93,7 +93,7 @@ class PandasWrapper(BaseToolApiWrapper):
93
93
  if file_extension in ['csv', 'txt']:
94
94
  df = pd.read_csv(file_obj)
95
95
  elif file_extension in ['xlsx', 'xls']:
96
- df = pd.read_excel(file_obj)
96
+ df = pd.read_excel(file_obj, engine='calamine')
97
97
  elif file_extension == 'parquet':
98
98
  df = pd.read_parquet(file_obj)
99
99
  elif file_extension == 'json':
@@ -162,35 +162,17 @@ class PandasWrapper(BaseToolApiWrapper):
162
162
  """Analyze and process using query on dataset"""
163
163
  df = self._get_dataframe(filename)
164
164
  code = self.generate_code_with_retries(df, query)
165
- dispatch_custom_event(
166
- name="thinking_step",
167
- data={
168
- "message": f"Executing generated code... \n\n```python\n{code}\n```",
169
- "tool_name": "process_query",
170
- "toolkit": "pandas"
171
- }
172
- )
165
+ self._log_tool_event(tool_name="process_query",
166
+ message=f"Executing generated code... \n\n```python\n{code}\n```")
173
167
  try:
174
168
  result = self.execute_code(df, code)
175
169
  except Exception as e:
176
170
  logger.error(f"Code execution failed: {format_exc()}")
177
- dispatch_custom_event(
178
- name="thinking_step",
179
- data={
180
- "message": f"Code execution failed: {format_exc()}",
181
- "tool_name": "process_query",
182
- "toolkit": "pandas"
183
- }
184
- )
171
+ self._log_tool_event(tool_name="process_query",
172
+ message=f"Executing generated code... \n\n```python\n{code}\n```")
185
173
  raise
186
- dispatch_custom_event(
187
- name="thinking_step",
188
- data={
189
- "message": f"Result of code execution... \n\n```\n{result['result']}\n```",
190
- "tool_name": "process_query",
191
- "toolkit": "pandas"
192
- }
193
- )
174
+ self._log_tool_event(tool_name="process_query",
175
+ message=f"Executing generated code... \n\n```python\n{code}\n```")
194
176
  if result.get("df") is not None:
195
177
  df = result.pop("df")
196
178
  # Not saving dataframe to artifact repo for now
@@ -6,7 +6,7 @@ from pydantic import create_model, BaseModel, ConfigDict, Field, field_validator
6
6
  from ..base.tool import BaseAction
7
7
 
8
8
  from .api_wrapper import PostmanApiWrapper
9
- from ..utils import clean_string, get_max_toolkit_length, TOOLKIT_SPLITTER, check_connection_response
9
+ from ..utils import clean_string, get_max_toolkit_length, check_connection_response
10
10
  from ...configurations.postman import PostmanConfiguration
11
11
 
12
12
  name = "postman"
@@ -43,20 +43,16 @@ def get_tools(tool):
43
43
 
44
44
  class PostmanToolkit(BaseToolkit):
45
45
  tools: List[BaseTool] = []
46
- toolkit_max_length: int = 0
47
46
 
48
47
  @staticmethod
49
48
  def toolkit_config_schema() -> BaseModel:
50
49
  selected_tools = {x['name']: x['args_schema'].schema(
51
50
  ) for x in PostmanApiWrapper.model_construct().get_available_tools()}
52
- PostmanToolkit.toolkit_max_length = get_max_toolkit_length(
53
- selected_tools)
54
51
  m = create_model(
55
52
  name,
56
53
  postman_configuration=(Optional[PostmanConfiguration], Field(description="Postman Configuration",
57
54
  json_schema_extra={'configuration_types': ['postman']})),
58
- collection_id=(str, Field(description="Default collection ID", json_schema_extra={
59
- 'toolkit_name': True, 'max_toolkit_length': PostmanToolkit.toolkit_max_length})),
55
+ collection_id=(str, Field(description="Default collection ID")),
60
56
  environment_config=(dict, Field(
61
57
  description="JSON configuration for request execution (auth headers, project IDs, base URLs, etc.)",
62
58
  default={})),
@@ -90,19 +86,21 @@ class PostmanToolkit(BaseToolkit):
90
86
  **kwargs['postman_configuration'],
91
87
  }
92
88
  postman_api_wrapper = PostmanApiWrapper(**wrapper_payload)
93
- prefix = clean_string(str(toolkit_name), cls.toolkit_max_length) + \
94
- TOOLKIT_SPLITTER if toolkit_name else ''
95
89
  available_tools = postman_api_wrapper.get_available_tools()
96
90
  tools = []
97
91
  for tool in available_tools:
98
92
  if selected_tools:
99
93
  if tool["name"] not in selected_tools:
100
94
  continue
95
+ description = f"{tool['description']}\nAPI URL: {postman_api_wrapper.base_url}"
96
+ if toolkit_name:
97
+ description = f"{description}\nToolkit: {toolkit_name}"
98
+ description = description[:1000]
101
99
  tools.append(PostmanAction(
102
100
  api_wrapper=postman_api_wrapper,
103
- name=prefix + tool["name"],
101
+ name=tool["name"],
104
102
  mode=tool["mode"],
105
- description=f"{tool['description']}\nAPI URL: {postman_api_wrapper.base_url}",
103
+ description=description,
106
104
  args_schema=tool["args_schema"]
107
105
  ))
108
106
  return cls(tools=tools)
@@ -340,7 +340,7 @@ class PostmanApiWrapper(BaseToolApiWrapper):
340
340
  raise ToolException(
341
341
  f"Invalid JSON response from Postman API: {str(e)}")
342
342
 
343
- def _apply_authentication(self, headers, params, all_variables, resolve_variables):
343
+ def _apply_authentication(self, headers, params, all_variables, native_auth, resolve_variables):
344
344
  """Apply authentication based on environment_config auth settings.
345
345
 
346
346
  Supports multiple authentication types:
@@ -363,14 +363,15 @@ class PostmanApiWrapper(BaseToolApiWrapper):
363
363
  import base64
364
364
 
365
365
  # Handle structured auth configuration only - no backward compatibility
366
- auth_config = self.environment_config.get('auth')
366
+ auth_config = self.environment_config.get('auth', native_auth)
367
367
  if auth_config and isinstance(auth_config, dict):
368
368
  auth_type = auth_config.get('type', '').lower()
369
369
  auth_params = auth_config.get('params', {})
370
370
 
371
371
  if auth_type == 'bearer':
372
372
  # Bearer token authentication
373
- token = resolve_variables(str(auth_params.get('token', '')))
373
+ tokent_raw = auth_config.get('bearer', [{}])[0].get('value', '')
374
+ token = resolve_variables(str(tokent_raw))
374
375
  if token:
375
376
  headers['Authorization'] = f'Bearer {token}'
376
377
 
@@ -739,7 +740,7 @@ class PostmanApiWrapper(BaseToolApiWrapper):
739
740
  all_variables = {}
740
741
 
741
742
  # 1. Start with environment_config variables (lowest priority)
742
- all_variables.update(self.environment_config)
743
+ all_variables.update(self._get_variables_from_env_config())
743
744
 
744
745
  # 2. Add collection variables
745
746
  collection_variables = collection_data.get('variable', [])
@@ -760,8 +761,9 @@ class PostmanApiWrapper(BaseToolApiWrapper):
760
761
  import re
761
762
  def replace_var(match):
762
763
  var_name = match.group(1)
763
- return str(all_variables.get(var_name, match.group(0)))
764
-
764
+ value = all_variables.get(var_name, None)
765
+ return resolve_variables(str(value)) if value else match.group(0)
766
+
765
767
  return re.sub(r'\{\{([^}]+)\}\}', replace_var, text)
766
768
 
767
769
  # Prepare the request
@@ -791,7 +793,7 @@ class PostmanApiWrapper(BaseToolApiWrapper):
791
793
  headers = {}
792
794
 
793
795
  # Handle authentication from environment_config
794
- self._apply_authentication(headers, params, all_variables, resolve_variables)
796
+ self._apply_authentication(headers, params, all_variables, request_data.get('auth', None), resolve_variables)
795
797
 
796
798
  # Add headers from request
797
799
  request_headers = request_data.get('header', [])
@@ -1640,7 +1642,7 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1640
1642
 
1641
1643
  # Find the request
1642
1644
  request_item = self.analyzer.find_request_by_path(
1643
- collection_data["item"], request_path)
1645
+ collection_data["item"], request_path, collection_data.get("auth", None))
1644
1646
  if not request_item:
1645
1647
  raise ToolException(f"Request '{request_path}' not found")
1646
1648
 
@@ -2161,3 +2163,12 @@ class PostmanApiWrapper(BaseToolApiWrapper):
2161
2163
  parse_items(items)
2162
2164
 
2163
2165
  return result
2166
+
2167
+ def _get_variables_from_env_config(self):
2168
+ """Extracts all enabled variables from the 'values' field in environment_config."""
2169
+ result = {}
2170
+ values = self.environment_config.get("values", [])
2171
+ for var in values:
2172
+ if var.get("enabled", True) and "key" in var and "value" in var:
2173
+ result[var["key"]] = var["value"]
2174
+ return result
@@ -1049,13 +1049,14 @@ class PostmanAnalyzer:
1049
1049
  find_in_items(items, path_parts)
1050
1050
  return results
1051
1051
 
1052
- def find_request_by_path(self, items: List[Dict], request_path: str) -> Optional[Dict]:
1052
+ def find_request_by_path(self, items: List[Dict], request_path: str, auth = None) -> Optional[Dict]:
1053
1053
  """Find a request by its path."""
1054
1054
  path_parts = [part.strip() for part in request_path.split('/') if part.strip()]
1055
1055
  if not path_parts:
1056
1056
  return None
1057
1057
 
1058
1058
  current_items = items
1059
+ current_auth = auth
1059
1060
 
1060
1061
  # Navigate through folders to the request
1061
1062
  for i, part in enumerate(path_parts):
@@ -1065,6 +1066,9 @@ class PostmanAnalyzer:
1065
1066
  if i == len(path_parts) - 1:
1066
1067
  # This should be the request
1067
1068
  if item.get('request'):
1069
+ # if request has no auth, inherit from parent
1070
+ if not item['request'].get('auth') and current_auth:
1071
+ item['request']['auth'] = current_auth
1068
1072
  return item
1069
1073
  else:
1070
1074
  return None
@@ -1072,6 +1076,9 @@ class PostmanAnalyzer:
1072
1076
  # This should be a folder
1073
1077
  if item.get('item'):
1074
1078
  current_items = item['item']
1079
+ # Update current_auth if folder has auth
1080
+ if item.get('auth'):
1081
+ current_auth = item['auth']
1075
1082
  found = True
1076
1083
  break
1077
1084
  else: