alita-sdk 0.3.379__py3-none-any.whl โ†’ 0.3.627__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 +156 -0
  6. alita_sdk/cli/agent_loader.py +245 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3113 -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/testcases/__init__.py +94 -0
  23. alita_sdk/cli/testcases/data_generation.py +119 -0
  24. alita_sdk/cli/testcases/discovery.py +96 -0
  25. alita_sdk/cli/testcases/executor.py +84 -0
  26. alita_sdk/cli/testcases/logger.py +85 -0
  27. alita_sdk/cli/testcases/parser.py +172 -0
  28. alita_sdk/cli/testcases/prompts.py +91 -0
  29. alita_sdk/cli/testcases/reporting.py +125 -0
  30. alita_sdk/cli/testcases/setup.py +108 -0
  31. alita_sdk/cli/testcases/test_runner.py +282 -0
  32. alita_sdk/cli/testcases/utils.py +39 -0
  33. alita_sdk/cli/testcases/validation.py +90 -0
  34. alita_sdk/cli/testcases/workflow.py +196 -0
  35. alita_sdk/cli/toolkit.py +327 -0
  36. alita_sdk/cli/toolkit_loader.py +85 -0
  37. alita_sdk/cli/tools/__init__.py +43 -0
  38. alita_sdk/cli/tools/approval.py +224 -0
  39. alita_sdk/cli/tools/filesystem.py +1751 -0
  40. alita_sdk/cli/tools/planning.py +389 -0
  41. alita_sdk/cli/tools/terminal.py +414 -0
  42. alita_sdk/community/__init__.py +72 -12
  43. alita_sdk/community/inventory/__init__.py +236 -0
  44. alita_sdk/community/inventory/config.py +257 -0
  45. alita_sdk/community/inventory/enrichment.py +2137 -0
  46. alita_sdk/community/inventory/extractors.py +1469 -0
  47. alita_sdk/community/inventory/ingestion.py +3172 -0
  48. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  49. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  50. alita_sdk/community/inventory/parsers/base.py +295 -0
  51. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  52. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  53. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  54. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  55. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  56. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  57. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  58. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  59. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  60. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  61. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  62. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  63. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  64. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  65. alita_sdk/community/inventory/patterns/loader.py +348 -0
  66. alita_sdk/community/inventory/patterns/registry.py +198 -0
  67. alita_sdk/community/inventory/presets.py +535 -0
  68. alita_sdk/community/inventory/retrieval.py +1403 -0
  69. alita_sdk/community/inventory/toolkit.py +173 -0
  70. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  71. alita_sdk/community/inventory/visualize.py +1370 -0
  72. alita_sdk/configurations/__init__.py +1 -1
  73. alita_sdk/configurations/ado.py +141 -20
  74. alita_sdk/configurations/bitbucket.py +94 -2
  75. alita_sdk/configurations/confluence.py +130 -1
  76. alita_sdk/configurations/figma.py +76 -0
  77. alita_sdk/configurations/gitlab.py +91 -0
  78. alita_sdk/configurations/jira.py +103 -0
  79. alita_sdk/configurations/openapi.py +329 -0
  80. alita_sdk/configurations/qtest.py +72 -1
  81. alita_sdk/configurations/report_portal.py +96 -0
  82. alita_sdk/configurations/sharepoint.py +148 -0
  83. alita_sdk/configurations/testio.py +83 -0
  84. alita_sdk/configurations/testrail.py +88 -0
  85. alita_sdk/configurations/xray.py +93 -0
  86. alita_sdk/configurations/zephyr_enterprise.py +93 -0
  87. alita_sdk/configurations/zephyr_essential.py +75 -0
  88. alita_sdk/runtime/clients/artifact.py +3 -3
  89. alita_sdk/runtime/clients/client.py +388 -46
  90. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  91. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  92. alita_sdk/runtime/clients/sandbox_client.py +8 -21
  93. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  94. alita_sdk/runtime/langchain/assistant.py +157 -39
  95. alita_sdk/runtime/langchain/constants.py +647 -1
  96. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  97. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  98. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  99. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -4
  100. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
  101. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  102. alita_sdk/runtime/langchain/document_loaders/constants.py +40 -19
  103. alita_sdk/runtime/langchain/langraph_agent.py +405 -84
  104. alita_sdk/runtime/langchain/utils.py +106 -7
  105. alita_sdk/runtime/llms/preloaded.py +2 -6
  106. alita_sdk/runtime/models/mcp_models.py +61 -0
  107. alita_sdk/runtime/skills/__init__.py +91 -0
  108. alita_sdk/runtime/skills/callbacks.py +498 -0
  109. alita_sdk/runtime/skills/discovery.py +540 -0
  110. alita_sdk/runtime/skills/executor.py +610 -0
  111. alita_sdk/runtime/skills/input_builder.py +371 -0
  112. alita_sdk/runtime/skills/models.py +330 -0
  113. alita_sdk/runtime/skills/registry.py +355 -0
  114. alita_sdk/runtime/skills/skill_runner.py +330 -0
  115. alita_sdk/runtime/toolkits/__init__.py +31 -0
  116. alita_sdk/runtime/toolkits/application.py +29 -10
  117. alita_sdk/runtime/toolkits/artifact.py +20 -11
  118. alita_sdk/runtime/toolkits/datasource.py +13 -6
  119. alita_sdk/runtime/toolkits/mcp.py +783 -0
  120. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  121. alita_sdk/runtime/toolkits/planning.py +178 -0
  122. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  123. alita_sdk/runtime/toolkits/subgraph.py +251 -6
  124. alita_sdk/runtime/toolkits/tools.py +356 -69
  125. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  126. alita_sdk/runtime/tools/__init__.py +10 -3
  127. alita_sdk/runtime/tools/application.py +27 -6
  128. alita_sdk/runtime/tools/artifact.py +511 -28
  129. alita_sdk/runtime/tools/data_analysis.py +183 -0
  130. alita_sdk/runtime/tools/function.py +67 -35
  131. alita_sdk/runtime/tools/graph.py +10 -4
  132. alita_sdk/runtime/tools/image_generation.py +148 -46
  133. alita_sdk/runtime/tools/llm.py +1003 -128
  134. alita_sdk/runtime/tools/loop.py +3 -1
  135. alita_sdk/runtime/tools/loop_output.py +3 -1
  136. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  137. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  138. alita_sdk/runtime/tools/mcp_server_tool.py +8 -5
  139. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  140. alita_sdk/runtime/tools/planning/models.py +246 -0
  141. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  142. alita_sdk/runtime/tools/router.py +2 -4
  143. alita_sdk/runtime/tools/sandbox.py +65 -48
  144. alita_sdk/runtime/tools/skill_router.py +776 -0
  145. alita_sdk/runtime/tools/tool.py +3 -1
  146. alita_sdk/runtime/tools/vectorstore.py +9 -3
  147. alita_sdk/runtime/tools/vectorstore_base.py +70 -14
  148. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  149. alita_sdk/runtime/utils/constants.py +5 -1
  150. alita_sdk/runtime/utils/mcp_client.py +492 -0
  151. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  152. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  153. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  154. alita_sdk/runtime/utils/serialization.py +155 -0
  155. alita_sdk/runtime/utils/streamlit.py +40 -13
  156. alita_sdk/runtime/utils/toolkit_utils.py +30 -9
  157. alita_sdk/runtime/utils/utils.py +36 -0
  158. alita_sdk/tools/__init__.py +134 -35
  159. alita_sdk/tools/ado/repos/__init__.py +51 -32
  160. alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
  161. alita_sdk/tools/ado/test_plan/__init__.py +25 -9
  162. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  163. alita_sdk/tools/ado/utils.py +1 -18
  164. alita_sdk/tools/ado/wiki/__init__.py +25 -12
  165. alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
  166. alita_sdk/tools/ado/work_item/__init__.py +26 -13
  167. alita_sdk/tools/ado/work_item/ado_wrapper.py +73 -11
  168. alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
  169. alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
  170. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  171. alita_sdk/tools/azure_ai/search/__init__.py +11 -8
  172. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  173. alita_sdk/tools/base/tool.py +5 -1
  174. alita_sdk/tools/base_indexer_toolkit.py +271 -84
  175. alita_sdk/tools/bitbucket/__init__.py +17 -11
  176. alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
  177. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
  178. alita_sdk/tools/browser/__init__.py +5 -4
  179. alita_sdk/tools/carrier/__init__.py +5 -6
  180. alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
  181. alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
  182. alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
  183. alita_sdk/tools/chunkers/__init__.py +3 -1
  184. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  185. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  186. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  187. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  188. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  189. alita_sdk/tools/cloud/aws/__init__.py +10 -7
  190. alita_sdk/tools/cloud/azure/__init__.py +10 -7
  191. alita_sdk/tools/cloud/gcp/__init__.py +10 -7
  192. alita_sdk/tools/cloud/k8s/__init__.py +10 -7
  193. alita_sdk/tools/code/linter/__init__.py +10 -8
  194. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  195. alita_sdk/tools/code/sonar/__init__.py +11 -8
  196. alita_sdk/tools/code_indexer_toolkit.py +82 -22
  197. alita_sdk/tools/confluence/__init__.py +22 -16
  198. alita_sdk/tools/confluence/api_wrapper.py +107 -30
  199. alita_sdk/tools/confluence/loader.py +14 -2
  200. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  201. alita_sdk/tools/elastic/__init__.py +11 -8
  202. alita_sdk/tools/elitea_base.py +493 -30
  203. alita_sdk/tools/figma/__init__.py +58 -11
  204. alita_sdk/tools/figma/api_wrapper.py +1235 -143
  205. alita_sdk/tools/figma/figma_client.py +73 -0
  206. alita_sdk/tools/figma/toon_tools.py +2748 -0
  207. alita_sdk/tools/github/__init__.py +14 -15
  208. alita_sdk/tools/github/github_client.py +224 -100
  209. alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
  210. alita_sdk/tools/github/schemas.py +14 -5
  211. alita_sdk/tools/github/tool.py +5 -1
  212. alita_sdk/tools/github/tool_prompts.py +9 -22
  213. alita_sdk/tools/gitlab/__init__.py +16 -11
  214. alita_sdk/tools/gitlab/api_wrapper.py +218 -48
  215. alita_sdk/tools/gitlab_org/__init__.py +10 -9
  216. alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
  217. alita_sdk/tools/google/bigquery/__init__.py +13 -12
  218. alita_sdk/tools/google/bigquery/tool.py +5 -1
  219. alita_sdk/tools/google_places/__init__.py +11 -8
  220. alita_sdk/tools/google_places/api_wrapper.py +1 -1
  221. alita_sdk/tools/jira/__init__.py +17 -10
  222. alita_sdk/tools/jira/api_wrapper.py +92 -41
  223. alita_sdk/tools/keycloak/__init__.py +11 -8
  224. alita_sdk/tools/localgit/__init__.py +9 -3
  225. alita_sdk/tools/localgit/local_git.py +62 -54
  226. alita_sdk/tools/localgit/tool.py +5 -1
  227. alita_sdk/tools/memory/__init__.py +12 -4
  228. alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
  229. alita_sdk/tools/ocr/__init__.py +11 -8
  230. alita_sdk/tools/openapi/__init__.py +491 -106
  231. alita_sdk/tools/openapi/api_wrapper.py +1368 -0
  232. alita_sdk/tools/openapi/tool.py +20 -0
  233. alita_sdk/tools/pandas/__init__.py +20 -12
  234. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  235. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  236. alita_sdk/tools/postman/__init__.py +10 -9
  237. alita_sdk/tools/pptx/__init__.py +11 -10
  238. alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
  239. alita_sdk/tools/qtest/__init__.py +31 -11
  240. alita_sdk/tools/qtest/api_wrapper.py +2135 -86
  241. alita_sdk/tools/rally/__init__.py +10 -9
  242. alita_sdk/tools/rally/api_wrapper.py +1 -1
  243. alita_sdk/tools/report_portal/__init__.py +12 -8
  244. alita_sdk/tools/salesforce/__init__.py +10 -8
  245. alita_sdk/tools/servicenow/__init__.py +17 -15
  246. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  247. alita_sdk/tools/sharepoint/__init__.py +10 -7
  248. alita_sdk/tools/sharepoint/api_wrapper.py +129 -38
  249. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  250. alita_sdk/tools/sharepoint/utils.py +8 -2
  251. alita_sdk/tools/slack/__init__.py +10 -7
  252. alita_sdk/tools/slack/api_wrapper.py +2 -2
  253. alita_sdk/tools/sql/__init__.py +12 -9
  254. alita_sdk/tools/testio/__init__.py +10 -7
  255. alita_sdk/tools/testrail/__init__.py +11 -10
  256. alita_sdk/tools/testrail/api_wrapper.py +1 -1
  257. alita_sdk/tools/utils/__init__.py +9 -4
  258. alita_sdk/tools/utils/content_parser.py +103 -18
  259. alita_sdk/tools/utils/text_operations.py +410 -0
  260. alita_sdk/tools/utils/tool_prompts.py +79 -0
  261. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +30 -13
  262. alita_sdk/tools/xray/__init__.py +13 -9
  263. alita_sdk/tools/yagmail/__init__.py +9 -3
  264. alita_sdk/tools/zephyr/__init__.py +10 -7
  265. alita_sdk/tools/zephyr_enterprise/__init__.py +11 -7
  266. alita_sdk/tools/zephyr_essential/__init__.py +10 -7
  267. alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
  268. alita_sdk/tools/zephyr_essential/client.py +2 -2
  269. alita_sdk/tools/zephyr_scale/__init__.py +11 -8
  270. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  271. alita_sdk/tools/zephyr_squad/__init__.py +10 -7
  272. {alita_sdk-0.3.379.dist-info โ†’ alita_sdk-0.3.627.dist-info}/METADATA +154 -8
  273. alita_sdk-0.3.627.dist-info/RECORD +468 -0
  274. alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
  275. alita_sdk-0.3.379.dist-info/RECORD +0 -360
  276. {alita_sdk-0.3.379.dist-info โ†’ alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
  277. {alita_sdk-0.3.379.dist-info โ†’ alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
  278. {alita_sdk-0.3.379.dist-info โ†’ alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,315 @@
1
+ """
2
+ MCP (Model Context Protocol) server integration.
3
+
4
+ Handles loading MCP server configurations and connecting to MCP servers
5
+ using langchain-mcp-adapters to load tools with persistent sessions.
6
+
7
+ Requires: pip install langchain-mcp-adapters
8
+ Documentation: https://docs.langchain.com/oss/python/langchain/mcp
9
+ Issue Reference: https://github.com/langchain-ai/langchain-mcp-adapters/issues/178
10
+ """
11
+
12
+ import json
13
+ import logging
14
+ from pathlib import Path
15
+ from typing import Dict, Any, List, Optional
16
+ from rich.console import Console
17
+
18
+ from .config import substitute_env_vars
19
+
20
+ console = Console()
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ def load_mcp_config(file_path: str) -> Dict[str, Any]:
25
+ """Load MCP configuration from JSON file (VSCode/Copilot format).
26
+
27
+ Args:
28
+ file_path: Path to mcp.json configuration file
29
+
30
+ Returns:
31
+ Dictionary with mcpServers configuration, or empty dict if file doesn't exist
32
+ """
33
+ path = Path(file_path)
34
+
35
+ if not path.exists():
36
+ # Gracefully handle missing file - MCP is optional
37
+ return {}
38
+
39
+ try:
40
+ with open(path) as f:
41
+ content = f.read()
42
+
43
+ # Apply environment variable substitution
44
+ content = substitute_env_vars(content)
45
+ config = json.loads(content)
46
+
47
+ # Validate structure
48
+ if 'mcpServers' not in config:
49
+ console.print(f"[yellow]Warning: {file_path} missing 'mcpServers' key[/yellow]")
50
+ return {}
51
+
52
+ return config
53
+ except json.JSONDecodeError as e:
54
+ console.print(f"[red]Error parsing {file_path}:[/red] {e}")
55
+ return {}
56
+ except Exception as e:
57
+ console.print(f"[red]Error loading {file_path}:[/red] {e}")
58
+ return {}
59
+
60
+
61
+ def load_mcp_tools(agent_def: Dict[str, Any], mcp_config_path: str) -> List[Dict[str, Any]]:
62
+ """Load MCP tools from agent definition with tool-level filtering.
63
+
64
+ This function creates MCP server configuration objects that will be processed
65
+ by the runtime layer using langchain-mcp-adapters to connect to MCP servers
66
+ and load their tools.
67
+
68
+ The actual connection happens in create_mcp_client() which uses:
69
+ - langchain_mcp_adapters.client.MultiServerMCPClient for connection
70
+ - langchain_mcp_adapters.tools.load_mcp_tools for tool loading
71
+
72
+ Args:
73
+ agent_def: Agent definition dictionary containing mcps list
74
+ mcp_config_path: Path to mcp.json configuration file (workspace-level)
75
+
76
+ Returns:
77
+ List of MCP server configurations that will be used by MultiServerMCPClient
78
+ to connect to servers and load tools with filtering applied.
79
+ """
80
+ import fnmatch
81
+
82
+ toolkit_configs = []
83
+
84
+ # Get MCP configuration
85
+ mcps = agent_def.get('mcps', [])
86
+ if not mcps:
87
+ return toolkit_configs
88
+
89
+ # Load mcp.json config file from workspace
90
+ mcp_config = load_mcp_config(mcp_config_path)
91
+ mcp_servers = mcp_config.get('mcpServers', {})
92
+
93
+ # Process each MCP entry
94
+ for mcp_entry in mcps:
95
+ # Handle both string and object formats
96
+ if isinstance(mcp_entry, str):
97
+ server_name = mcp_entry
98
+ agent_selected_tools = None
99
+ agent_excluded_tools = None
100
+ elif isinstance(mcp_entry, dict):
101
+ server_name = mcp_entry.get('name')
102
+ agent_selected_tools = mcp_entry.get('selected_tools')
103
+ agent_excluded_tools = mcp_entry.get('excluded_tools')
104
+ else:
105
+ console.print(f"[yellow]Warning: Invalid MCP entry format: {mcp_entry}[/yellow]")
106
+ continue
107
+
108
+ if not server_name:
109
+ console.print(f"[yellow]Warning: MCP entry missing 'name': {mcp_entry}[/yellow]")
110
+ continue
111
+
112
+ # Get server configuration
113
+ server_config = mcp_servers.get(server_name)
114
+ if not server_config:
115
+ console.print(f"[yellow]Warning: MCP server '{server_name}' not found in MCP configuration[/yellow]")
116
+ continue
117
+
118
+ # Determine tool selection (agent overrides server defaults)
119
+ server_selected_tools = server_config.get('selected_tools')
120
+ server_excluded_tools = server_config.get('excluded_tools')
121
+
122
+ # Agent overrides take precedence
123
+ final_selected_tools = agent_selected_tools if agent_selected_tools is not None else server_selected_tools
124
+ final_excluded_tools = agent_excluded_tools if agent_excluded_tools is not None else server_excluded_tools
125
+
126
+ # Get connection details
127
+ server_type = server_config.get('type') # VSCode format: "stdio" or "streamable_http"
128
+ server_url = server_config.get('url')
129
+ server_command = server_config.get('command')
130
+ server_args = server_config.get('args', [])
131
+ server_env = server_config.get('env', {})
132
+ stateful = server_config.get('stateful', False)
133
+
134
+ # Build MCP server config for langchain-mcp-adapters
135
+ # Support both VSCode format (type: "stdio") and our format (command: "...")
136
+ if server_type == 'stdio' or server_command:
137
+ # stdio transport (subprocess)
138
+ if not server_command:
139
+ console.print(f"[red]Error: MCP server '{server_name}' has type 'stdio' but no 'command'[/red]")
140
+ continue
141
+
142
+ console.print(f"[yellow]Note: MCP server '{server_name}' uses command/stdio connection[/yellow]")
143
+ mcp_server_config = {
144
+ 'transport': 'stdio',
145
+ 'command': server_command,
146
+ 'args': server_args or []
147
+ }
148
+ elif server_type == 'streamable_http' or server_url:
149
+ # HTTP-based transport
150
+ if not server_url:
151
+ console.print(f"[red]Error: MCP server '{server_name}' has type 'streamable_http' but no 'url'[/red]")
152
+ continue
153
+
154
+ mcp_server_config = {
155
+ 'transport': 'streamable_http',
156
+ 'url': server_url
157
+ }
158
+ else:
159
+ console.print(f"[red]Error: MCP server '{server_name}' has neither 'type'/'url' nor 'command'[/red]")
160
+ continue
161
+
162
+ # Add environment variables if specified
163
+ if server_env:
164
+ mcp_server_config['env'] = server_env
165
+
166
+ # Wrap in toolkit config format
167
+ toolkit_config = {
168
+ 'toolkit_type': 'mcp',
169
+ 'name': server_name,
170
+ 'mcp_server_config': mcp_server_config,
171
+ }
172
+
173
+ # Add tool filtering if specified
174
+ if final_selected_tools is not None:
175
+ toolkit_config['selected_tools'] = final_selected_tools
176
+ if final_excluded_tools is not None:
177
+ toolkit_config['excluded_tools'] = final_excluded_tools
178
+
179
+ toolkit_configs.append(toolkit_config)
180
+
181
+ # Display loaded tools info
182
+ if final_selected_tools:
183
+ tools_display = ', '.join(final_selected_tools)
184
+ console.print(f"โœ“ Loaded MCP server: [cyan]{server_name}[/cyan] (selected: {tools_display})")
185
+ elif final_excluded_tools:
186
+ console.print(f"โœ“ Loaded MCP server: [cyan]{server_name}[/cyan] (excluded: {', '.join(final_excluded_tools)})")
187
+ else:
188
+ console.print(f"โœ“ Loaded MCP server: [cyan]{server_name}[/cyan] (all tools)")
189
+
190
+ return toolkit_configs
191
+
192
+
193
+ class MCPSessionManager:
194
+ """Manages persistent MCP sessions for stateful tools like Playwright.
195
+
196
+ Holds MCP client and session context managers alive for the entire agent lifetime.
197
+ This is critical for stateful MCP servers that maintain state across tool calls
198
+ (e.g., Playwright browser sessions).
199
+
200
+ See: https://github.com/langchain-ai/langchain-mcp-adapters/issues/178
201
+ """
202
+ def __init__(self):
203
+ self.client = None
204
+ self.sessions = {}
205
+ self._session_contexts = {}
206
+
207
+ def __del__(self):
208
+ """Cleanup sessions when manager is garbage collected."""
209
+ # Best effort cleanup - sessions will be closed when process exits anyway
210
+ pass
211
+
212
+
213
+ async def load_mcp_tools_async(mcp_toolkit_configs: List[Dict[str, Any]]):
214
+ """
215
+ Load actual MCP tools from servers with persistent sessions.
216
+
217
+ IMPORTANT: This function returns an MCPSessionManager that MUST be kept alive
218
+ for the entire agent session to maintain stateful MCP server state (e.g., browser).
219
+
220
+ Args:
221
+ mcp_toolkit_configs: List of MCP toolkit configurations with transport details
222
+
223
+ Returns:
224
+ Tuple of (session_manager, list_of_tools) where:
225
+ - session_manager: MCPSessionManager that holds client and persistent sessions
226
+ - list_of_tools: LangChain async tools from MCP that use persistent sessions
227
+ """
228
+ if not mcp_toolkit_configs:
229
+ return None, []
230
+
231
+ try:
232
+ from langchain_mcp_adapters.client import MultiServerMCPClient
233
+ from langchain_mcp_adapters.tools import load_mcp_tools
234
+ except ImportError:
235
+ console.print("[yellow]Warning: langchain-mcp-adapters not installed. MCP tools will not be available.[/yellow]")
236
+ console.print("[yellow]Install with: pip install langchain-mcp-adapters[/yellow]")
237
+ return None, []
238
+
239
+ # Build server configs for MultiServerMCPClient
240
+ server_configs = {}
241
+ for config in mcp_toolkit_configs:
242
+ server_name = config.get('name', 'unknown')
243
+ mcp_server_config = config.get('mcp_server_config', {})
244
+ transport = mcp_server_config.get('transport', 'stdio')
245
+
246
+ if transport == 'stdio':
247
+ command = mcp_server_config.get('command')
248
+ args = mcp_server_config.get('args', [])
249
+ env = mcp_server_config.get('env')
250
+
251
+ # Ensure command is a string
252
+ if isinstance(command, list):
253
+ command = command[0] if command else None
254
+
255
+ # Build config dict - only include env if it's set
256
+ config_dict = {
257
+ 'transport': 'stdio',
258
+ 'command': command,
259
+ 'args': args
260
+ }
261
+ if env:
262
+ config_dict['env'] = env
263
+
264
+ server_configs[server_name] = config_dict
265
+ console.print(f"[dim]MCP server {server_name}: {command} {' '.join(args)}[/dim]")
266
+
267
+ elif transport == 'streamable_http':
268
+ server_configs[server_name] = {
269
+ 'transport': 'streamable_http',
270
+ 'url': mcp_server_config.get('url'),
271
+ 'headers': mcp_server_config.get('headers')
272
+ }
273
+
274
+ if not server_configs:
275
+ return None, []
276
+
277
+ try:
278
+ # Create session manager to hold everything alive
279
+ manager = MCPSessionManager()
280
+ manager.client = MultiServerMCPClient(server_configs)
281
+
282
+ # Create PERSISTENT sessions for each server
283
+ # This keeps the MCP server subprocess and state alive for stateful tools
284
+ all_tools = []
285
+
286
+ for server_name in server_configs.keys():
287
+ # Enter the session context and keep it alive for entire agent lifetime
288
+ session_context = manager.client.session(server_name)
289
+ session = await session_context.__aenter__()
290
+
291
+ # Store both for cleanup and reference
292
+ manager.sessions[server_name] = session
293
+ manager._session_contexts[server_name] = session_context
294
+
295
+ # Load tools using the persistent session
296
+ tools = await load_mcp_tools(
297
+ session,
298
+ connection=manager.client.connections[server_name],
299
+ server_name=server_name
300
+ )
301
+ all_tools.extend(tools)
302
+
303
+ console.print(f"[dim]Created persistent session for {server_name}: {len(tools)} tools[/dim]")
304
+
305
+ console.print(f"โœ“ Loaded {len(all_tools)} MCP tools total from {len(mcp_toolkit_configs)} servers with persistent sessions")
306
+
307
+ return manager, all_tools
308
+
309
+ except ImportError:
310
+ # Already handled above
311
+ raise
312
+ except Exception as e:
313
+ logger.exception(f"Failed to create MCP client and sessions")
314
+ console.print(f"[red]Error: Failed to load MCP tools: {e}[/red]")
315
+ return None, []
@@ -0,0 +1,94 @@
1
+ """
2
+ Test execution utilities for Alita CLI.
3
+
4
+ This package provides utilities for executing test cases, including:
5
+ - Test case parsing
6
+ - Prompt building
7
+ - Result validation
8
+ - Execution logging
9
+ - Executor cache management
10
+ - Agent setup and loading
11
+ - Test case discovery
12
+ - Test data generation
13
+ - Test execution and validation
14
+ - Result reporting
15
+ """
16
+
17
+ from .parser import parse_test_case, resolve_toolkit_config_path
18
+ from .prompts import (
19
+ build_bulk_data_gen_prompt,
20
+ build_single_test_execution_prompt,
21
+ build_single_test_validation_prompt
22
+ )
23
+ from .validation import (
24
+ extract_json_from_text,
25
+ create_fallback_result_for_test,
26
+ print_validation_diagnostics
27
+ )
28
+ from .logger import TestLogCapture
29
+ from .executor import create_executor_from_cache, cleanup_executor_cache
30
+ from .utils import extract_toolkit_name
31
+ from .setup import (
32
+ load_test_runner_agent,
33
+ load_data_generator_agent,
34
+ load_validator_agent
35
+ )
36
+ from .discovery import (
37
+ discover_test_case_files,
38
+ validate_test_case_files,
39
+ print_test_execution_header
40
+ )
41
+ from .data_generation import execute_bulk_data_generation
42
+ from .test_runner import execute_single_test_case, validate_single_test_case
43
+ from .reporting import (
44
+ generate_summary_report,
45
+ save_structured_report,
46
+ print_test_execution_summary
47
+ )
48
+ from .workflow import (
49
+ parse_all_test_cases,
50
+ filter_test_cases_needing_data_gen,
51
+ execute_all_test_cases
52
+ )
53
+
54
+ __all__ = [
55
+ # Parser
56
+ 'parse_test_case',
57
+ 'resolve_toolkit_config_path',
58
+ # Prompts
59
+ 'build_bulk_data_gen_prompt',
60
+ 'build_single_test_execution_prompt',
61
+ 'build_single_test_validation_prompt',
62
+ # Validation
63
+ 'extract_json_from_text',
64
+ 'create_fallback_result_for_test',
65
+ 'print_validation_diagnostics',
66
+ # Logger
67
+ 'TestLogCapture',
68
+ # Executor
69
+ 'create_executor_from_cache',
70
+ 'cleanup_executor_cache',
71
+ # Utils
72
+ 'extract_toolkit_name',
73
+ # Setup
74
+ 'load_test_runner_agent',
75
+ 'load_data_generator_agent',
76
+ 'load_validator_agent',
77
+ # Discovery
78
+ 'discover_test_case_files',
79
+ 'validate_test_case_files',
80
+ 'print_test_execution_header',
81
+ # Data Generation
82
+ 'execute_bulk_data_generation',
83
+ # Test Runner
84
+ 'execute_single_test_case',
85
+ 'validate_single_test_case',
86
+ # Reporting
87
+ 'generate_summary_report',
88
+ 'save_structured_report',
89
+ 'print_test_execution_summary',
90
+ # Workflow
91
+ 'parse_all_test_cases',
92
+ 'filter_test_cases_needing_data_gen',
93
+ 'execute_all_test_cases',
94
+ ]
@@ -0,0 +1,119 @@
1
+ """
2
+ Bulk test data generation utilities.
3
+
4
+ Handles executing the data generator agent to provision test data.
5
+ """
6
+
7
+ import logging
8
+ import sqlite3
9
+ from pathlib import Path
10
+ from typing import Dict, Any, List, Optional
11
+ from langgraph.checkpoint.sqlite import SqliteSaver
12
+
13
+ from langchain_core.runnables import RunnableConfig
14
+
15
+ from ..callbacks import create_cli_callback
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def execute_bulk_data_generation(
21
+ data_gen_def: Dict[str, Any],
22
+ test_cases_needing_data_gen: List[Dict[str, Any]],
23
+ parsed_test_cases: List[Dict[str, Any]],
24
+ test_cases_path: Path,
25
+ client,
26
+ config,
27
+ model: Optional[str],
28
+ temperature: Optional[float],
29
+ max_tokens: Optional[int],
30
+ work_dir: str,
31
+ master_log,
32
+ setup_executor_func,
33
+ verbose: bool = True,
34
+ debug: bool = False,
35
+ ) -> List[Dict[str, str]]:
36
+ """Execute bulk test data generation.
37
+
38
+ Args:
39
+ data_gen_def: Data generator agent definition
40
+ test_cases_needing_data_gen: Test cases requiring data generation
41
+ parsed_test_cases: All parsed test cases
42
+ test_cases_path: Path to test cases directory
43
+ client: API client
44
+ config: CLI configuration
45
+ model: Model override
46
+ temperature: Temperature override
47
+ max_tokens: Max tokens override
48
+ work_dir: Working directory
49
+ master_log: Log capture instance
50
+ setup_executor_func: Function to setup executor
51
+
52
+ Returns:
53
+ Chat history list from data generation
54
+ """
55
+ from .parser import resolve_toolkit_config_path
56
+ from .prompts import build_bulk_data_gen_prompt
57
+ from ..agent_ui import extract_output_from_result
58
+
59
+ master_log.print(f"\n[bold yellow]๐Ÿ”ง Bulk Test Data Generation[/bold yellow]")
60
+ master_log.print(f"Generating test data for {len(test_cases_needing_data_gen)} test cases...\n")
61
+ master_log.print(f"[dim]Skipping {len(parsed_test_cases) - len(test_cases_needing_data_gen)} test cases with generateTestData: false[/dim]\n")
62
+
63
+ bulk_data_gen_prompt = build_bulk_data_gen_prompt(test_cases_needing_data_gen)
64
+ master_log.print(f"Executing test data generation prompt \n{bulk_data_gen_prompt}\n")
65
+
66
+ try:
67
+ # Setup data generator agent
68
+ bulk_memory = SqliteSaver(sqlite3.connect(":memory:", check_same_thread=False))
69
+
70
+ # Use first test case's config or empty tuple
71
+ first_config_path = None
72
+ if parsed_test_cases:
73
+ first_tc = parsed_test_cases[0]
74
+ first_config_path = resolve_toolkit_config_path(
75
+ first_tc['data'].get('config_path', ''),
76
+ first_tc['file'],
77
+ test_cases_path
78
+ )
79
+
80
+ data_gen_config_tuple = (first_config_path,) if first_config_path else ()
81
+ data_gen_executor, _, _, _, _, _, _ = setup_executor_func(
82
+ client, data_gen_def, data_gen_config_tuple, config,
83
+ model, temperature, max_tokens, bulk_memory, work_dir
84
+ )
85
+
86
+ if data_gen_executor:
87
+ master_log.print("\n" + "=" * 80)
88
+ master_log.print("[bold yellow]๐Ÿ“‹ BULK DATA GENERATION[/bold yellow]")
89
+ master_log.print("=" * 80 + "\n")
90
+ invoke_config = None
91
+ if verbose:
92
+ cli_callback = create_cli_callback(verbose=True, debug=debug)
93
+ invoke_config = RunnableConfig(callbacks=[cli_callback])
94
+ with master_log.status("[yellow]Generating test data for all test cases...[/yellow]", spinner="dots"):
95
+ bulk_gen_result = data_gen_executor.invoke(
96
+ {
97
+ "input": bulk_data_gen_prompt,
98
+ "chat_history": [],
99
+ },
100
+ config=invoke_config,
101
+ )
102
+ bulk_gen_output = extract_output_from_result(bulk_gen_result)
103
+ master_log.print(f"[green]โœ“ Bulk test data generation completed[/green]")
104
+ master_log.print(f"\n{bulk_gen_output}\n")
105
+
106
+ # Store chat history from data generation to pass to test executors
107
+ return [
108
+ {"role": "user", "content": bulk_data_gen_prompt},
109
+ {"role": "assistant", "content": bulk_gen_output}
110
+ ]
111
+ else:
112
+ master_log.print(f"[yellow]โš  Warning: Data generator has no executor[/yellow]\n")
113
+ return []
114
+
115
+ except Exception as e:
116
+ master_log.print(f"[yellow]โš  Warning: Bulk data generation failed: {e}[/yellow]")
117
+ master_log.print("[yellow]Continuing with test execution...[/yellow]\n")
118
+ logger.debug(f"Bulk data generation error: {e}", exc_info=True)
119
+ return []
@@ -0,0 +1,96 @@
1
+ """
2
+ Test case discovery and filtering utilities.
3
+
4
+ Handles finding test case files and filtering based on user selections.
5
+ """
6
+
7
+ import logging
8
+ from pathlib import Path
9
+ from typing import List, Tuple
10
+ from rich.console import Console
11
+
12
+ logger = logging.getLogger(__name__)
13
+ console = Console()
14
+
15
+
16
+ def discover_test_case_files(
17
+ test_cases_dir: str,
18
+ test_case_files: Tuple[str, ...]
19
+ ) -> List[Path]:
20
+ """Discover and filter test case files based on user selection.
21
+
22
+ Args:
23
+ test_cases_dir: Directory containing test case files
24
+ test_case_files: Specific test case files to execute (empty = all)
25
+
26
+ Returns:
27
+ List of test case file paths
28
+ """
29
+ test_cases_path = Path(test_cases_dir)
30
+
31
+ if test_case_files:
32
+ # User specified specific test case files
33
+ test_case_files_set = set(test_case_files)
34
+ all_test_cases = sorted(test_cases_path.rglob('TC-*.md'))
35
+ test_case_files_list = [
36
+ tc for tc in all_test_cases
37
+ if tc.name in test_case_files_set
38
+ ]
39
+
40
+ # Check if all specified files were found
41
+ found_names = {tc.name for tc in test_case_files_list}
42
+ not_found = test_case_files_set - found_names
43
+ if not_found:
44
+ console.print(f"[yellow]โš  Warning: Test case files not found: {', '.join(not_found)}[/yellow]")
45
+ else:
46
+ # Execute all test cases
47
+ test_case_files_list = sorted(test_cases_path.rglob('TC-*.md'))
48
+
49
+ return test_case_files_list
50
+
51
+
52
+ def validate_test_case_files(
53
+ test_case_files_list: List[Path],
54
+ test_cases_dir: str,
55
+ test_case_files: Tuple[str, ...]
56
+ ) -> bool:
57
+ """Validate that test case files were found.
58
+
59
+ Args:
60
+ test_case_files_list: List of discovered test case files
61
+ test_cases_dir: Directory that was searched
62
+ test_case_files: Specific files that were requested
63
+
64
+ Returns:
65
+ True if files were found, False otherwise (prints warning)
66
+ """
67
+ if not test_case_files_list:
68
+ if test_case_files:
69
+ console.print(f"[yellow]No matching test case files found in {test_cases_dir}[/yellow]")
70
+ else:
71
+ console.print(f"[yellow]No test case files found in {test_cases_dir}[/yellow]")
72
+ return False
73
+
74
+ return True
75
+
76
+
77
+ def print_test_execution_header(
78
+ agent_name: str,
79
+ test_case_files_list: List[Path],
80
+ test_case_files: Tuple[str, ...],
81
+ results_dir: str
82
+ ) -> None:
83
+ """Print test execution header information.
84
+
85
+ Args:
86
+ agent_name: Name of the test runner agent
87
+ test_case_files_list: List of test cases to execute
88
+ test_case_files: Specific files requested (if any)
89
+ results_dir: Directory where results will be saved
90
+ """
91
+ console.print(f"\n[bold cyan]๐Ÿงช Test Execution Started[/bold cyan]")
92
+ console.print(f"Agent: [bold]{agent_name}[/bold]")
93
+ console.print(f"Test Cases: {len(test_case_files_list)}")
94
+ if test_case_files:
95
+ console.print(f"Selected: [cyan]{', '.join(test_case_files)}[/cyan]")
96
+ console.print(f"Results Directory: {results_dir}\n")