alita-sdk 0.3.257__py3-none-any.whl → 0.3.562__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 (278) 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 +1073 -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 +72 -12
  30. alita_sdk/community/inventory/__init__.py +236 -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/toolkit_utils.py +176 -0
  58. alita_sdk/community/inventory/visualize.py +1370 -0
  59. alita_sdk/configurations/__init__.py +11 -0
  60. alita_sdk/configurations/ado.py +148 -2
  61. alita_sdk/configurations/azure_search.py +1 -1
  62. alita_sdk/configurations/bigquery.py +1 -1
  63. alita_sdk/configurations/bitbucket.py +94 -2
  64. alita_sdk/configurations/browser.py +18 -0
  65. alita_sdk/configurations/carrier.py +19 -0
  66. alita_sdk/configurations/confluence.py +130 -1
  67. alita_sdk/configurations/delta_lake.py +1 -1
  68. alita_sdk/configurations/figma.py +76 -5
  69. alita_sdk/configurations/github.py +65 -1
  70. alita_sdk/configurations/gitlab.py +81 -0
  71. alita_sdk/configurations/google_places.py +17 -0
  72. alita_sdk/configurations/jira.py +103 -0
  73. alita_sdk/configurations/openapi.py +111 -0
  74. alita_sdk/configurations/postman.py +1 -1
  75. alita_sdk/configurations/qtest.py +72 -3
  76. alita_sdk/configurations/report_portal.py +115 -0
  77. alita_sdk/configurations/salesforce.py +19 -0
  78. alita_sdk/configurations/service_now.py +1 -12
  79. alita_sdk/configurations/sharepoint.py +167 -0
  80. alita_sdk/configurations/sonar.py +18 -0
  81. alita_sdk/configurations/sql.py +20 -0
  82. alita_sdk/configurations/testio.py +101 -0
  83. alita_sdk/configurations/testrail.py +88 -0
  84. alita_sdk/configurations/xray.py +94 -1
  85. alita_sdk/configurations/zephyr_enterprise.py +94 -1
  86. alita_sdk/configurations/zephyr_essential.py +95 -0
  87. alita_sdk/runtime/clients/artifact.py +21 -4
  88. alita_sdk/runtime/clients/client.py +458 -67
  89. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  90. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  91. alita_sdk/runtime/clients/sandbox_client.py +352 -0
  92. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  93. alita_sdk/runtime/langchain/assistant.py +183 -43
  94. alita_sdk/runtime/langchain/constants.py +647 -1
  95. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  96. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
  97. alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
  98. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  99. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -3
  100. alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
  101. alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
  102. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
  103. alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
  104. alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
  105. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
  106. alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
  107. alita_sdk/runtime/langchain/document_loaders/constants.py +189 -41
  108. alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
  109. alita_sdk/runtime/langchain/langraph_agent.py +407 -92
  110. alita_sdk/runtime/langchain/utils.py +102 -8
  111. alita_sdk/runtime/llms/preloaded.py +2 -6
  112. alita_sdk/runtime/models/mcp_models.py +61 -0
  113. alita_sdk/runtime/skills/__init__.py +91 -0
  114. alita_sdk/runtime/skills/callbacks.py +498 -0
  115. alita_sdk/runtime/skills/discovery.py +540 -0
  116. alita_sdk/runtime/skills/executor.py +610 -0
  117. alita_sdk/runtime/skills/input_builder.py +371 -0
  118. alita_sdk/runtime/skills/models.py +330 -0
  119. alita_sdk/runtime/skills/registry.py +355 -0
  120. alita_sdk/runtime/skills/skill_runner.py +330 -0
  121. alita_sdk/runtime/toolkits/__init__.py +28 -0
  122. alita_sdk/runtime/toolkits/application.py +14 -4
  123. alita_sdk/runtime/toolkits/artifact.py +24 -9
  124. alita_sdk/runtime/toolkits/datasource.py +13 -6
  125. alita_sdk/runtime/toolkits/mcp.py +780 -0
  126. alita_sdk/runtime/toolkits/planning.py +178 -0
  127. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  128. alita_sdk/runtime/toolkits/subgraph.py +11 -6
  129. alita_sdk/runtime/toolkits/tools.py +314 -70
  130. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  131. alita_sdk/runtime/tools/__init__.py +24 -0
  132. alita_sdk/runtime/tools/application.py +16 -4
  133. alita_sdk/runtime/tools/artifact.py +367 -33
  134. alita_sdk/runtime/tools/data_analysis.py +183 -0
  135. alita_sdk/runtime/tools/function.py +100 -4
  136. alita_sdk/runtime/tools/graph.py +81 -0
  137. alita_sdk/runtime/tools/image_generation.py +218 -0
  138. alita_sdk/runtime/tools/llm.py +1013 -177
  139. alita_sdk/runtime/tools/loop.py +3 -1
  140. alita_sdk/runtime/tools/loop_output.py +3 -1
  141. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  142. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  143. alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
  144. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  145. alita_sdk/runtime/tools/planning/models.py +246 -0
  146. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  147. alita_sdk/runtime/tools/router.py +2 -1
  148. alita_sdk/runtime/tools/sandbox.py +375 -0
  149. alita_sdk/runtime/tools/skill_router.py +776 -0
  150. alita_sdk/runtime/tools/tool.py +3 -1
  151. alita_sdk/runtime/tools/vectorstore.py +69 -65
  152. alita_sdk/runtime/tools/vectorstore_base.py +163 -90
  153. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  154. alita_sdk/runtime/utils/mcp_client.py +492 -0
  155. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  156. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  157. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  158. alita_sdk/runtime/utils/streamlit.py +41 -14
  159. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  160. alita_sdk/runtime/utils/utils.py +48 -0
  161. alita_sdk/tools/__init__.py +135 -37
  162. alita_sdk/tools/ado/__init__.py +2 -2
  163. alita_sdk/tools/ado/repos/__init__.py +15 -19
  164. alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
  165. alita_sdk/tools/ado/test_plan/__init__.py +26 -8
  166. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
  167. alita_sdk/tools/ado/wiki/__init__.py +27 -12
  168. alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
  169. alita_sdk/tools/ado/work_item/__init__.py +27 -12
  170. alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
  171. alita_sdk/tools/advanced_jira_mining/__init__.py +12 -8
  172. alita_sdk/tools/aws/delta_lake/__init__.py +14 -11
  173. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  174. alita_sdk/tools/azure_ai/search/__init__.py +13 -8
  175. alita_sdk/tools/base/tool.py +5 -1
  176. alita_sdk/tools/base_indexer_toolkit.py +454 -110
  177. alita_sdk/tools/bitbucket/__init__.py +27 -19
  178. alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
  179. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
  180. alita_sdk/tools/browser/__init__.py +41 -16
  181. alita_sdk/tools/browser/crawler.py +3 -1
  182. alita_sdk/tools/browser/utils.py +15 -6
  183. alita_sdk/tools/carrier/__init__.py +18 -17
  184. alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
  185. alita_sdk/tools/carrier/excel_reporter.py +8 -4
  186. alita_sdk/tools/chunkers/__init__.py +3 -1
  187. alita_sdk/tools/chunkers/code/codeparser.py +1 -1
  188. alita_sdk/tools/chunkers/sematic/json_chunker.py +2 -1
  189. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  190. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  191. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  192. alita_sdk/tools/cloud/aws/__init__.py +11 -7
  193. alita_sdk/tools/cloud/azure/__init__.py +11 -7
  194. alita_sdk/tools/cloud/gcp/__init__.py +11 -7
  195. alita_sdk/tools/cloud/k8s/__init__.py +11 -7
  196. alita_sdk/tools/code/linter/__init__.py +9 -8
  197. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  198. alita_sdk/tools/code/sonar/__init__.py +20 -13
  199. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  200. alita_sdk/tools/confluence/__init__.py +21 -14
  201. alita_sdk/tools/confluence/api_wrapper.py +197 -58
  202. alita_sdk/tools/confluence/loader.py +14 -2
  203. alita_sdk/tools/custom_open_api/__init__.py +11 -5
  204. alita_sdk/tools/elastic/__init__.py +10 -8
  205. alita_sdk/tools/elitea_base.py +546 -64
  206. alita_sdk/tools/figma/__init__.py +11 -8
  207. alita_sdk/tools/figma/api_wrapper.py +352 -153
  208. alita_sdk/tools/github/__init__.py +17 -17
  209. alita_sdk/tools/github/api_wrapper.py +9 -26
  210. alita_sdk/tools/github/github_client.py +81 -12
  211. alita_sdk/tools/github/schemas.py +2 -1
  212. alita_sdk/tools/github/tool.py +5 -1
  213. alita_sdk/tools/gitlab/__init__.py +18 -13
  214. alita_sdk/tools/gitlab/api_wrapper.py +224 -80
  215. alita_sdk/tools/gitlab_org/__init__.py +13 -10
  216. alita_sdk/tools/google/bigquery/__init__.py +13 -13
  217. alita_sdk/tools/google/bigquery/tool.py +5 -1
  218. alita_sdk/tools/google_places/__init__.py +20 -11
  219. alita_sdk/tools/jira/__init__.py +21 -11
  220. alita_sdk/tools/jira/api_wrapper.py +315 -168
  221. alita_sdk/tools/keycloak/__init__.py +10 -8
  222. alita_sdk/tools/localgit/__init__.py +8 -3
  223. alita_sdk/tools/localgit/local_git.py +62 -54
  224. alita_sdk/tools/localgit/tool.py +5 -1
  225. alita_sdk/tools/memory/__init__.py +38 -14
  226. alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
  227. alita_sdk/tools/ocr/__init__.py +10 -8
  228. alita_sdk/tools/openapi/__init__.py +281 -108
  229. alita_sdk/tools/openapi/api_wrapper.py +883 -0
  230. alita_sdk/tools/openapi/tool.py +20 -0
  231. alita_sdk/tools/pandas/__init__.py +18 -11
  232. alita_sdk/tools/pandas/api_wrapper.py +40 -45
  233. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  234. alita_sdk/tools/postman/__init__.py +10 -11
  235. alita_sdk/tools/postman/api_wrapper.py +19 -8
  236. alita_sdk/tools/postman/postman_analysis.py +8 -1
  237. alita_sdk/tools/pptx/__init__.py +10 -10
  238. alita_sdk/tools/qtest/__init__.py +21 -14
  239. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  240. alita_sdk/tools/rally/__init__.py +12 -10
  241. alita_sdk/tools/report_portal/__init__.py +22 -16
  242. alita_sdk/tools/salesforce/__init__.py +21 -16
  243. alita_sdk/tools/servicenow/__init__.py +20 -16
  244. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  245. alita_sdk/tools/sharepoint/__init__.py +16 -14
  246. alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
  247. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  248. alita_sdk/tools/sharepoint/utils.py +8 -2
  249. alita_sdk/tools/slack/__init__.py +11 -7
  250. alita_sdk/tools/sql/__init__.py +21 -19
  251. alita_sdk/tools/sql/api_wrapper.py +71 -23
  252. alita_sdk/tools/testio/__init__.py +20 -13
  253. alita_sdk/tools/testrail/__init__.py +12 -11
  254. alita_sdk/tools/testrail/api_wrapper.py +214 -46
  255. alita_sdk/tools/utils/__init__.py +28 -4
  256. alita_sdk/tools/utils/content_parser.py +182 -62
  257. alita_sdk/tools/utils/text_operations.py +254 -0
  258. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
  259. alita_sdk/tools/xray/__init__.py +17 -14
  260. alita_sdk/tools/xray/api_wrapper.py +58 -113
  261. alita_sdk/tools/yagmail/__init__.py +8 -3
  262. alita_sdk/tools/zephyr/__init__.py +11 -7
  263. alita_sdk/tools/zephyr_enterprise/__init__.py +15 -9
  264. alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
  265. alita_sdk/tools/zephyr_essential/__init__.py +15 -10
  266. alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
  267. alita_sdk/tools/zephyr_essential/client.py +6 -4
  268. alita_sdk/tools/zephyr_scale/__init__.py +12 -8
  269. alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
  270. alita_sdk/tools/zephyr_squad/__init__.py +11 -7
  271. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/METADATA +184 -37
  272. alita_sdk-0.3.562.dist-info/RECORD +450 -0
  273. alita_sdk-0.3.562.dist-info/entry_points.txt +2 -0
  274. alita_sdk/tools/bitbucket/tools.py +0 -304
  275. alita_sdk-0.3.257.dist-info/RECORD +0 -343
  276. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/WHEEL +0 -0
  277. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/licenses/LICENSE +0 -0
  278. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,10 @@
1
- import base64
2
1
  import functools
3
2
  import json
4
3
  import logging
5
4
  import re
6
5
  from enum import Enum
7
6
  from typing import Dict, List, Generator, Optional, Union
7
+ from urllib.parse import urlparse, parse_qs
8
8
 
9
9
  import requests
10
10
  from FigmaPy import FigmaPy
@@ -12,10 +12,43 @@ from langchain_core.documents import Document
12
12
  from langchain_core.tools import ToolException
13
13
  from pydantic import Field, PrivateAttr, create_model, model_validator, SecretStr
14
14
 
15
- from ..elitea_base import BaseVectorStoreToolApiWrapper, extend_with_vector_tools
15
+ from ..non_code_indexer_toolkit import NonCodeIndexerToolkit
16
+ from ..utils.available_tools_decorator import extend_with_parent_available_tools
16
17
  from ..utils.content_parser import load_content_from_bytes
17
18
 
18
19
  GLOBAL_LIMIT = 10000
20
+ GLOBAL_RETAIN = ['id', 'name', 'type', 'document', 'children']
21
+ GLOBAL_REMOVE = []
22
+ GLOBAL_DEPTH_START = 4
23
+ GLOBAL_DEPTH_END = 6
24
+ EXTRA_PARAMS = (
25
+ Optional[Dict[str, Union[str, int, List, None]]],
26
+ Field(
27
+ description=(
28
+ "Additional parameters for customizing response processing:\n"
29
+ "- `limit`: Maximum size of the output in characters.\n"
30
+ "- `regexp`: Regex pattern to filter or clean the output.\n"
31
+ "- `fields_retain`: List of field names to always keep in the output, on levels starting from `depth_start`.\n"
32
+ "- `fields_remove`: List of field names to exclude from the output, unless also present in `fields_retain`.\n"
33
+ "- `depth_start`: The depth in the object hierarchy at which field filtering begins (fields are retained or removed).\n"
34
+ "- `depth_end`: The depth at which all fields are ignored and recursion stops.\n"
35
+ "Use these parameters to control the granularity and size of the returned data, especially for large or deeply nested objects."
36
+ ),
37
+ default={
38
+ "limit": GLOBAL_LIMIT, "regexp": None,
39
+ "fields_retain": GLOBAL_RETAIN, "fields_remove": GLOBAL_REMOVE,
40
+ "depth_start": GLOBAL_DEPTH_START, "depth_end": GLOBAL_DEPTH_END,
41
+ },
42
+ examples=[
43
+ {
44
+ "limit": "1000",
45
+ "regexp": r'("strokes"|"fills")\s*:\s*("[^"]*"|[^\s,}\[]+)\s*(?=,|\}|\n)',
46
+ "fields_retain": GLOBAL_RETAIN, "fields_remove": GLOBAL_REMOVE,
47
+ "depth_start": GLOBAL_DEPTH_START, "depth_end": GLOBAL_DEPTH_END,
48
+ }
49
+ ],
50
+ ),
51
+ )
19
52
 
20
53
 
21
54
  class ArgsSchema(Enum):
@@ -35,19 +68,7 @@ class ArgsSchema(Enum):
35
68
  examples=["8:6,1:7"],
36
69
  ),
37
70
  ),
38
- extra_params=(
39
- Optional[Dict[str, Union[str, int, None]]],
40
- Field(
41
- description="Additional parameters including limit and regex pattern to be removed from response",
42
- default={"limit": GLOBAL_LIMIT, "regexp": None},
43
- examples=[
44
- {
45
- "limit": "1000",
46
- "regexp": r'("strokes"|"fills")\s*:\s*("[^"]*"|[^\s,}\[]+)\s*(?=,|\}|\n)',
47
- }
48
- ],
49
- ),
50
- ),
71
+ extra_params=EXTRA_PARAMS,
51
72
  )
52
73
  File = create_model(
53
74
  "FileNodes",
@@ -60,25 +81,13 @@ class ArgsSchema(Enum):
60
81
  ),
61
82
  geometry=(
62
83
  Optional[str],
63
- Field(description="Sets to 'paths' to export vector data"),
84
+ Field(description="Sets to 'paths' to export vector data", default=None),
64
85
  ),
65
86
  version=(
66
87
  Optional[str],
67
- Field(description="Sets version of file"),
68
- ),
69
- extra_params=(
70
- Optional[Dict[str, Union[str, int, None]]],
71
- Field(
72
- description="Additional parameters including limit and regex pattern to be removed from response",
73
- default={"limit": GLOBAL_LIMIT, "regexp": None},
74
- examples=[
75
- {
76
- "limit": "1000",
77
- "regexp": r'("strokes"|"fills")\s*:\s*("[^"]*"|[^\s,}\[]+)\s*(?=,|\}|\n)',
78
- }
79
- ],
80
- ),
88
+ Field(description="Sets version of file", default=None),
81
89
  ),
90
+ extra_params=EXTRA_PARAMS,
82
91
  )
83
92
  FileKey = create_model(
84
93
  "FileKey",
@@ -89,19 +98,7 @@ class ArgsSchema(Enum):
89
98
  examples=["Fp24FuzPwH0L74ODSrCnQo"],
90
99
  ),
91
100
  ),
92
- extra_params=(
93
- Optional[Dict[str, Union[str, int, None]]],
94
- Field(
95
- description="Additional parameters including limit and regex pattern to be removed from response",
96
- default={"limit": GLOBAL_LIMIT, "regexp": None},
97
- examples=[
98
- {
99
- "limit": "1000",
100
- "regexp": r'("strokes"|"fills")\s*:\s*("[^"]*"|[^\s,}\[]+)\s*(?=,|\}|\n)',
101
- }
102
- ],
103
- ),
104
- ),
101
+ extra_params=EXTRA_PARAMS,
105
102
  )
106
103
  FileComment = create_model(
107
104
  "FileComment",
@@ -119,22 +116,11 @@ class ArgsSchema(Enum):
119
116
  client_meta=(
120
117
  Optional[dict],
121
118
  Field(
122
- description="Positioning information of the comment (Vector, FrameOffset, Region, FrameOffsetRegion)"
123
- ),
124
- ),
125
- extra_params=(
126
- Optional[Dict[str, Union[str, int, None]]],
127
- Field(
128
- description="Additional parameters including limit and regex pattern to be removed from response",
129
- default={"limit": GLOBAL_LIMIT, "regexp": None},
130
- examples=[
131
- {
132
- "limit": "1000",
133
- "regexp": r'("strokes"|"fills")\s*:\s*("[^"]*"|[^\s,}\[]+)\s*(?=,|\}|\n)',
134
- }
135
- ],
119
+ description="Positioning information of the comment (Vector, FrameOffset, Region, FrameOffsetRegion)",
120
+ default=None,
136
121
  ),
137
122
  ),
123
+ extra_params=EXTRA_PARAMS,
138
124
  )
139
125
  FileImages = create_model(
140
126
  "FileImages",
@@ -146,40 +132,30 @@ class ArgsSchema(Enum):
146
132
  ),
147
133
  ),
148
134
  ids=(
149
- str,
135
+ Optional[str],
150
136
  Field(
151
137
  description="Specifies id of file images separated by comma",
152
138
  examples=["8:6,1:7"],
139
+ default="0:0",
153
140
  ),
154
141
  ),
155
142
  scale=(
156
143
  Optional[str],
157
- Field(description="A number between 0.01 and 4, the image scaling factor"),
144
+ Field(description="A number between 0.01 and 4, the image scaling factor", default=None),
158
145
  ),
159
146
  format=(
160
147
  Optional[str],
161
148
  Field(
162
149
  description="A string enum for the image output format",
163
150
  examples=["jpg", "png", "svg", "pdf"],
151
+ default=None,
164
152
  ),
165
153
  ),
166
154
  version=(
167
155
  Optional[str],
168
- Field(description="A specific version ID to use"),
169
- ),
170
- extra_params=(
171
- Optional[Dict[str, Union[str, int, None]]],
172
- Field(
173
- description="Additional parameters including limit and regex pattern to be removed from response",
174
- default={"limit": GLOBAL_LIMIT, "regexp": None},
175
- examples=[
176
- {
177
- "limit": "1000",
178
- "regexp": r'("strokes"|"fills")\s*:\s*("[^"]*"|[^\s,}\[]+)\s*(?=,|\}|\n)',
179
- }
180
- ],
181
- ),
156
+ Field(description="A specific version ID to use", default=None),
182
157
  ),
158
+ extra_params=EXTRA_PARAMS,
183
159
  )
184
160
  TeamProjects = create_model(
185
161
  "TeamProjects",
@@ -190,19 +166,7 @@ class ArgsSchema(Enum):
190
166
  examples=["1101853299713989222"],
191
167
  ),
192
168
  ),
193
- extra_params=(
194
- Optional[Dict[str, Union[str, int, None]]],
195
- Field(
196
- description="Additional parameters including limit and regex pattern to be removed from response",
197
- default={"limit": GLOBAL_LIMIT, "regexp": None},
198
- examples=[
199
- {
200
- "limit": "1000",
201
- "regexp": r'("strokes"|"fills")\s*:\s*("[^"]*"|[^\s,}\[]+)\s*(?=,|\}|\n)',
202
- }
203
- ],
204
- ),
205
- ),
169
+ extra_params=EXTRA_PARAMS,
206
170
  )
207
171
  ProjectFiles = create_model(
208
172
  "ProjectFiles",
@@ -213,91 +177,253 @@ class ArgsSchema(Enum):
213
177
  examples=["55391681"],
214
178
  ),
215
179
  ),
216
- extra_params=(
217
- Optional[Dict[str, Union[str, int, None]]],
218
- Field(
219
- description="Additional parameters including limit and regex pattern to be removed from response",
220
- default={"limit": GLOBAL_LIMIT, "regexp": None},
221
- examples=[
222
- {
223
- "limit": "1000",
224
- "regexp": r'("strokes"|"fills")\s*:\s*("[^"]*"|[^\s,}\[]+)\s*(?=,|\}|\n)',
225
- }
226
- ],
227
- ),
228
- ),
180
+ extra_params=EXTRA_PARAMS,
229
181
  )
230
182
 
231
183
 
232
- class FigmaApiWrapper(BaseVectorStoreToolApiWrapper):
184
+ class FigmaApiWrapper(NonCodeIndexerToolkit):
233
185
  token: Optional[SecretStr] = Field(default=None)
234
186
  oauth2: Optional[SecretStr] = Field(default=None)
235
187
  global_limit: Optional[int] = Field(default=GLOBAL_LIMIT)
236
188
  global_regexp: Optional[str] = Field(default=None)
189
+ global_fields_retain: Optional[List[str]] = GLOBAL_RETAIN
190
+ global_fields_remove: Optional[List[str]] = GLOBAL_REMOVE
191
+ global_depth_start: Optional[int] = GLOBAL_DEPTH_START
192
+ global_depth_end: Optional[int] = GLOBAL_DEPTH_END
237
193
  _client: Optional[FigmaPy] = PrivateAttr()
238
194
 
239
- def _base_loader(self, project_id: Optional[str] = None, file_keys: Optional[List[str]] = None, **kwargs) -> Generator[Document, None, None]:
240
- files = []
241
- if project_id:
195
+ def _base_loader(
196
+ self,
197
+ file_or_page_url: Optional[str] = None,
198
+ project_id: Optional[str] = None,
199
+ file_keys_include: Optional[List[str]] = None,
200
+ file_keys_exclude: Optional[List[str]] = None,
201
+ node_ids_include: Optional[List[str]] = None,
202
+ node_ids_exclude: Optional[List[str]] = None,
203
+ node_types_include: Optional[List[str]] = None,
204
+ node_types_exclude: Optional[List[str]] = None,
205
+ **kwargs
206
+ ) -> Generator[Document, None, None]:
207
+ if file_or_page_url:
208
+ # If URL is provided and valid, extract and override file_keys_include and node_ids_include
209
+ try:
210
+ parsed = urlparse(file_or_page_url)
211
+ path_parts = parsed.path.strip('/').split('/')
212
+
213
+ # Check if the path matches the expected format
214
+ if len(path_parts) >= 2 and path_parts[0] == 'design':
215
+ file_keys_include = [path_parts[1]]
216
+ if len(path_parts) == 3:
217
+ # To ensure url structure matches Figma's format with 3 path segments
218
+ query_params = parse_qs(parsed.query)
219
+ if "node-id" in query_params:
220
+ node_ids_include = query_params.get('node-id', [])
221
+ except Exception as e:
222
+ raise ToolException(
223
+ f"Unexpected error while processing Figma url {file_or_page_url}: {e}")
224
+
225
+ # If both include and exclude are provided, use only include
226
+ if file_keys_include:
227
+ self._log_tool_event(f"Loading files: {file_keys_include}")
228
+ for file_key in file_keys_include:
229
+ self._log_tool_event(f"Loading file `{file_key}`")
230
+ file = self._client.get_file(file_key, geometry='depth=1') # fetch only top-level structure (only pages without inner components)
231
+ if not file:
232
+ raise ToolException(f"Unexpected error while retrieving file {file_key}. Please try specifying the node-id of an inner page.")
233
+ metadata = {
234
+ 'id': file_key,
235
+ 'file_key': file_key,
236
+ 'name': file.name,
237
+ 'updated_on': file.last_modified,
238
+ 'figma_pages_include': node_ids_include or [],
239
+ 'figma_pages_exclude': node_ids_exclude or [],
240
+ 'figma_nodes_include': node_types_include or [],
241
+ 'figma_nodes_exclude': node_types_exclude or []
242
+ }
243
+ yield Document(page_content=json.dumps(metadata), metadata=metadata)
244
+ elif project_id:
245
+ self._log_tool_event(f"Loading project files from project `{project_id}`")
242
246
  files = json.loads(self.get_project_files(project_id)).get('files', [])
243
247
  for file in files:
248
+ if file_keys_exclude and file.get('key', '') in file_keys_exclude:
249
+ continue
244
250
  yield Document(page_content=json.dumps(file), metadata={
245
251
  'id': file.get('key', ''),
246
252
  'file_key': file.get('key', ''),
247
253
  'name': file.get('name', ''),
248
- 'updated_on': file.get('last_modified', '')
254
+ 'updated_on': file.get('last_modified', ''),
255
+ 'figma_pages_include': node_ids_include or [],
256
+ 'figma_pages_exclude': node_ids_exclude or [],
257
+ 'figma_nodes_include': node_types_include or [],
258
+ 'figma_nodes_exclude': node_types_exclude or []
249
259
  })
250
- elif file_keys:
251
- for file_key in file_keys:
252
- file = self._client.get_file(file_key)
253
- metadata = {
254
- 'id': file_key,
255
- 'file_key': file_key,
256
- 'name': file.name,
257
- 'updated_on': file.last_modified
258
- }
259
- yield Document(page_content=json.dumps(metadata), metadata=metadata)
260
+ elif file_keys_exclude or node_ids_exclude:
261
+ raise ValueError("Excludes without parent (project_id or file_keys_include) do not make sense.")
262
+ else:
263
+ raise ValueError("You must provide at least project_id or file_keys_include.")
264
+
265
+ def has_image_representation(self, node):
266
+ node_type = node.get('type', '').lower()
267
+ default_images_types = [
268
+ 'image', 'canvas', 'frame', 'vector', 'table', 'slice', 'sticky', 'shape_with_text', 'connector'
269
+ ]
270
+ # filter nodes of type which has image representation
271
+ # or rectangles with image as background
272
+ if (node_type in default_images_types
273
+ or (node_type == 'rectangle' and 'fills' in node and any(
274
+ fill.get('type') == 'IMAGE' for fill in node['fills'] if isinstance(fill, dict)))):
275
+ return True
276
+ return False
277
+
278
+ def get_texts_recursive(self, node):
279
+ texts = []
280
+ node_type = node.get('type', '').lower()
281
+ if node_type == 'text':
282
+ texts.append(node.get('characters', ''))
283
+ if 'children' in node:
284
+ for child in node['children']:
285
+ texts.extend(self.get_texts_recursive(child))
286
+ return texts
287
+
288
+ def _load_pages(self, document: Document):
289
+ file_key = document.metadata.get('id', '')
290
+ node_ids_include = document.metadata.pop('figma_pages_include', [])
291
+ node_ids_exclude = document.metadata.pop('figma_pages_exclude', [])
292
+ self._log_tool_event(f"Included pages: {node_ids_include}. Excluded pages: {node_ids_exclude}.")
293
+ if node_ids_include:
294
+ # try to fetch only specified pages/nodes in one request
295
+ file = self._get_file_nodes(file_key,','.join(node_ids_include)) # attempt to fetch only specified pages/nodes in one request
296
+ if file:
297
+ return [node['document'] for node in file.get('nodes', {}).values() if 'document' in node]
298
+ else:
299
+ #
300
+ file = self._client.get_file(file_key)
301
+ if file:
302
+ figma_pages = file.document.get('children', [])
303
+ return [node for node in figma_pages if ('id' in node and node['id'].replace(':', '-') not in node_ids_exclude)]
304
+ # fallback to loading all pages and filtering them one by one
305
+ file = self._client.get_file(file_key, geometry='depth=1')
306
+ if not file:
307
+ raise ToolException(
308
+ f"Unexpected error while retrieving file {file_key}. Please try specifying the node-id of an inner page.")
309
+ figma_pages_raw = file.document.get('children', [])
310
+ # extract pages one by one
311
+ if node_ids_include:
312
+ return [self._get_file_nodes(file_key, node_id) for node_id in node_ids_include]
313
+ else:
314
+ # return [self._get_file_nodes(file_key, page["id"]) for page in figma_pages_raw if ('id' in page and page['id'].replace(':', '-') not in node_ids_exclude)]
315
+ result = []
316
+ for page in figma_pages_raw:
317
+ if 'id' in page and page['id'].replace(':', '-') not in node_ids_exclude:
318
+ page_res = self._get_file_nodes(file_key, page["id"]).get('nodes', {}).get(page["id"], {}).get("document", {})
319
+ result.append(page_res)
320
+ return result
260
321
 
261
322
  def _process_document(self, document: Document) -> Generator[Document, None, None]:
262
323
  file_key = document.metadata.get('id', '')
263
- #
264
- node_ids = []
265
- children = self._client.get_file(file_key).document.get('children', [])
266
- if children:
267
- nodes = children[0].get('children', [])
268
- node_ids = [node['id'] for node in nodes if 'id' in node]
269
- images = self._client.get_file_images(file_key, node_ids).images or {}
270
-
271
- # iterate over images values
272
- for node_id, image_url in images.items():
273
- if not image_url:
274
- logging.warning(f"Image URL not found for node_id {node_id} in file {file_key}. Skipping.")
275
- continue
276
- response = requests.get(image_url)
277
- if response.status_code == 200:
278
- content_type = response.headers.get('Content-Type', '')
279
- if 'text/html' not in content_type.lower():
280
- extension = f".{content_type.split('/')[-1]}" if content_type.startswith('image') else '.txt'
281
- page_content = load_content_from_bytes(
282
- file_content=response.content,
283
- extension=extension, llm = self.llm)
324
+ self._log_tool_event(f"Loading details (images) for `{file_key}`")
325
+ figma_pages = self._load_pages(document)
326
+ node_types_include = [t.strip().lower() for t in document.metadata.pop('figma_nodes_include', [])]
327
+ node_types_exclude = [t.strip().lower() for t in document.metadata.pop('figma_nodes_exclude', [])]
328
+
329
+ image_nodes = []
330
+ text_nodes = {}
331
+ for page in figma_pages:
332
+ for node in page.get('children', []):
333
+ # filter by node_type if specified any include or exclude
334
+ node_type = node.get('type', '').lower()
335
+ include = node_types_include and node_type in node_types_include
336
+ exclude = node_types_exclude and node_type not in node_types_exclude
337
+ no_filter = not node_types_include and not node_types_exclude
338
+
339
+ if include or exclude or no_filter:
340
+ node_id = node.get('id')
341
+ if node_id:
342
+ if self.has_image_representation(node):
343
+ image_nodes.append(node['id'])
344
+ else:
345
+ text_nodes[node['id']] = self.get_texts_recursive(node)
346
+ # process image nodes
347
+ if image_nodes:
348
+ file_images = self._client.get_file_images(file_key, image_nodes)
349
+ images = self._client.get_file_images(file_key, image_nodes).images or {} if file_images else {}
350
+ total_images = len(images)
351
+ if total_images == 0:
352
+ logging.info(f"No images found for file {file_key}.")
353
+ return
354
+ progress_step = max(1, total_images // 10)
355
+ for idx, (node_id, image_url) in enumerate(images.items(), 1):
356
+ if not image_url:
357
+ logging.warning(f"Image URL not found for node_id {node_id} in file {file_key}. Skipping.")
358
+ continue
359
+ response = requests.get(image_url)
360
+ if response.status_code == 200:
361
+ content_type = response.headers.get('Content-Type', '')
362
+ if 'text/html' not in content_type.lower():
363
+ extension = f".{content_type.split('/')[-1]}" if content_type.startswith('image') else '.txt'
364
+ page_content = load_content_from_bytes(
365
+ file_content=response.content,
366
+ extension=extension, llm=self.llm)
367
+ yield Document(
368
+ page_content=page_content,
369
+ metadata={
370
+ 'id': node_id,
371
+ 'updated_on': document.metadata.get('updated_on', ''),
372
+ 'file_key': file_key,
373
+ 'node_id': node_id,
374
+ 'image_url': image_url,
375
+ 'type': 'image'
376
+ }
377
+ )
378
+ if idx % progress_step == 0 or idx == total_images:
379
+ percent = int((idx / total_images) * 100)
380
+ msg = f"Processed {idx}/{total_images} images ({percent}%) for file {file_key}."
381
+ logging.info(msg)
382
+ self._log_tool_event(msg)
383
+ # process text nodes
384
+ if text_nodes:
385
+ for node_id, texts in text_nodes.items():
386
+ if texts:
284
387
  yield Document(
285
- page_content=page_content,
286
- metadata={
287
- 'file_key': file_key,
288
- 'node_id': node_id,
289
- 'image_url': image_url
290
- }
291
- )
388
+ page_content="\n".join(texts),
389
+ metadata={
390
+ 'id': node_id,
391
+ 'updated_on': document.metadata.get('updated_on', ''),
392
+ 'file_key': file_key,
393
+ 'node_id': node_id,
394
+ 'type': 'text'
395
+ }
396
+ )
397
+
398
+ def _remove_metadata_keys(self):
399
+ return super()._remove_metadata_keys() + ['figma_pages_include', 'figma_pages_exclude', 'figma_nodes_include', 'figma_nodes_exclude']
292
400
 
293
401
  def _index_tool_params(self):
294
402
  """Return the parameters for indexing data."""
295
403
  return {
404
+ "file_or_page_url": (Optional[str], Field(
405
+ description="Url to file or page to index: i.e. https://www.figma.com/design/[YOUR_FILE_KEY]/Login-page-designs?node-id=[YOUR_PAGE_ID]",
406
+ default=None)),
296
407
  "project_id": (Optional[str], Field(
297
- description="ID of the project to list files from: i.e. '55391681'",
408
+ description="ID of the project to list files from: i.e. 55391681",
409
+ default=None)),
410
+ 'file_keys_include': (Optional[List[str]], Field(
411
+ description="List of file keys to include in index if project_id is not provided: i.e. ['Fp24FuzPwH0L74ODSrCnQo', 'jmhAr6q78dJoMRqt48zisY']",
412
+ default=None)),
413
+ 'file_keys_exclude': (Optional[List[str]], Field(
414
+ description="List of file keys to exclude from index. It is applied only if project_id is provided and file_keys_include is not provided: i.e. ['Fp24FuzPwH0L74ODSrCnQo', 'jmhAr6q78dJoMRqt48zisY']",
415
+ default=None)),
416
+ 'node_ids_include': (Optional[List[str]], Field(
417
+ description="List of top-level nodes (pages) in file to include in index. It is node-id from figma url: i.e. ['123-56', '7651-9230'].",
298
418
  default=None)),
299
- 'file_keys': (Optional[List[str]], Field(
300
- description="List of file keys to index: i.e. ['Fp24FuzPwH0L74ODSrCnQo', 'jmhAr6q78dJoMRqt48zisY']",
419
+ 'node_ids_exclude': (Optional[List[str]], Field(
420
+ description="List of top-level nodes (pages) in file to exclude from index. It is applied only if node_ids_include is not provided. It is node-id from figma url: i.e. ['Fp24FuzPwH0L74ODSrCnQo', 'jmhAr6q78dJoMRqt48zisY']",
421
+ default=None)),
422
+ 'node_types_include': (Optional[List[str]], Field(
423
+ description="List type of nodes to include in index: i.e. ['FRAME', 'COMPONENT', 'RECTANGLE', 'COMPONENT_SET', 'INSTANCE', 'VECTOR', ...].",
424
+ default=None)),
425
+ 'node_types_exclude': (Optional[List[str]], Field(
426
+ description="List type of nodes to exclude from index. It is applied only if node_types_include is not provided: i.e. ['FRAME', 'COMPONENT', 'RECTANGLE', 'COMPONENT_SET', 'INSTANCE', 'VECTOR', ...]",
301
427
  default=None))
302
428
  }
303
429
 
@@ -328,6 +454,11 @@ class FigmaApiWrapper(BaseVectorStoreToolApiWrapper):
328
454
  logging.error(msg)
329
455
  raise ToolException(msg)
330
456
 
457
+ @model_validator(mode='before')
458
+ @classmethod
459
+ def check_before(cls, values):
460
+ return super().validate_toolkit(values)
461
+
331
462
  @model_validator(mode="after")
332
463
  @classmethod
333
464
  def validate_toolkit(cls, values):
@@ -395,6 +526,53 @@ class FigmaApiWrapper(BaseVectorStoreToolApiWrapper):
395
526
  }
396
527
  return obj
397
528
 
529
+ def process_fields(obj, fields_retain=None, fields_remove=None, depth_start=1, depth_end=2, depth=1):
530
+ """
531
+ Reduces a nested dictionary or list by retaining or removing specified fields at certain depths.
532
+
533
+ - At each level, starting from `depth_start`, only fields in `fields_retain` are kept; fields in `fields_remove` are excluded unless also retained.
534
+ - Recursion stops at `depth_end`, ignoring all fields at or beyond this depth.
535
+ - Tracks which fields were retained and removed during processing.
536
+ - Returns a JSON string of the reduced object, plus lists of retained and removed fields.
537
+ """
538
+ fields_retain = set(fields_retain or [])
539
+ fields_remove = set(fields_remove or []) - fields_retain # fields in remove have lower priority than in retain
540
+
541
+ retained = set()
542
+ removed = set()
543
+
544
+ def _process(o, d):
545
+ if depth_end is not None and d >= depth_end:
546
+ return None # Ignore keys at or beyond cut_depth
547
+ if isinstance(o, dict):
548
+ result = {}
549
+ for k, v in o.items():
550
+ if k in fields_remove:
551
+ removed.add(k)
552
+ continue
553
+ if d >= depth_start:
554
+ if k in fields_retain:
555
+ retained.add(k)
556
+ result[k] = _process(v, d + 1) # process recursively
557
+ else:
558
+ # else: skip keys not in retain/default/to_process
559
+ removed.add(k) # remember skipped keys
560
+ else:
561
+ # retained.add(k) # remember retained keys
562
+ result[k] = _process(v, d + 1)
563
+ return result
564
+ elif isinstance(o, list):
565
+ return [_process(item, d + 1) for item in o]
566
+ else:
567
+ return o
568
+
569
+ new_obj = _process(obj, depth)
570
+ return {
571
+ "result": json.dumps(new_obj),
572
+ "retained_fields": list(retained),
573
+ "removed_fields": list(removed)
574
+ }
575
+
398
576
  def fix_trailing_commas(json_string):
399
577
  json_string = re.sub(r",\s*,+", ",", json_string)
400
578
  json_string = re.sub(r",\s*([\]}])", r"\1", json_string)
@@ -404,10 +582,12 @@ class FigmaApiWrapper(BaseVectorStoreToolApiWrapper):
404
582
  @functools.wraps(func)
405
583
  def wrapper(self, *args, **kwargs):
406
584
  extra_params = kwargs.pop("extra_params", {})
407
-
408
585
  limit = extra_params.get("limit", self.global_limit)
409
586
  regexp = extra_params.get("regexp", self.global_regexp)
410
-
587
+ fields_retain = extra_params.get("fields_retain", self.global_fields_retain)
588
+ fields_remove = extra_params.get("fields_remove", self.global_fields_remove)
589
+ depth_start = extra_params.get("depth_start", self.global_depth_start)
590
+ depth_end = extra_params.get("depth_end", self.global_depth_end)
411
591
  try:
412
592
  limit = int(limit)
413
593
  result = func(self, *args, **kwargs)
@@ -417,13 +597,26 @@ class FigmaApiWrapper(BaseVectorStoreToolApiWrapper):
417
597
  return ToolException(
418
598
  "Response result is empty. Check your input parameters or credentials"
419
599
  )
420
-
421
600
  if isinstance(result, (dict, list)):
422
- processed_result = simplified_dict(result)
423
- result = json.dumps(processed_result)
601
+ raw_result = result
602
+ processed_result = simplified_dict(raw_result)
603
+ raw_str_result = json.dumps(processed_result)
604
+ str_result = raw_str_result
605
+ if regexp:
606
+ regexp = re.compile(regexp)
607
+ str_result = re.sub(regexp, "", raw_str_result)
608
+ str_result = fix_trailing_commas(str_result)
609
+ if len(str_result) > limit:
610
+ reduced = process_fields(raw_result, fields_retain=fields_retain, fields_remove=fields_remove, depth_start=depth_start, depth_end=depth_end)
611
+ note = (f"Size of the output exceeds limit {limit}. Data reducing has been applied. "
612
+ f"Starting from the depth_start = {depth_start} the following object fields were removed: {reduced['removed_fields']}. "
613
+ f"The following fields were retained: {reduced['retained_fields']}. "
614
+ f"Starting from depth_end = {depth_end} all fields were ignored. "
615
+ f"You can adjust fields_retain, fields_remove, depth_start, depth_end, limit and regexp parameters to get more precise output")
616
+ return f"## NOTE:\n{note}.\n## Result: {reduced['result']}"[:limit]
617
+ return str_result
424
618
  else:
425
619
  result = json.dumps(result)
426
-
427
620
  if regexp:
428
621
  regexp = re.compile(regexp)
429
622
  result = re.sub(regexp, "", result)
@@ -444,6 +637,12 @@ class FigmaApiWrapper(BaseVectorStoreToolApiWrapper):
444
637
  f"files/{file_key}/nodes?ids={str(ids)}", method="get"
445
638
  )
446
639
 
640
+ def _get_file_nodes(self, file_key: str, ids: str, **kwargs):
641
+ """Reads a specified file nodes by field key from Figma."""
642
+ return self._client.api_request(
643
+ f"files/{file_key}/nodes?ids={str(ids)}", method="get"
644
+ )
645
+
447
646
  @process_output
448
647
  def get_file(
449
648
  self,
@@ -488,7 +687,7 @@ class FigmaApiWrapper(BaseVectorStoreToolApiWrapper):
488
687
  def get_file_images(
489
688
  self,
490
689
  file_key: str,
491
- ids: str = "0:0",
690
+ ids: Optional[str] = "0:0",
492
691
  scale: Optional[str] = None,
493
692
  format: Optional[str] = None,
494
693
  version: Optional[str] = None,
@@ -510,7 +709,7 @@ class FigmaApiWrapper(BaseVectorStoreToolApiWrapper):
510
709
  """Retrieves all files for a specified project ID from Figma."""
511
710
  return self._client.get_project_files(project_id)
512
711
 
513
- @extend_with_vector_tools
712
+ @extend_with_parent_available_tools
514
713
  def get_available_tools(self):
515
714
  return [
516
715
  {