alita-sdk 0.3.462__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 (261) hide show
  1. alita_sdk/cli/agent/__init__.py +5 -0
  2. alita_sdk/cli/agent/default.py +258 -0
  3. alita_sdk/cli/agent_executor.py +15 -3
  4. alita_sdk/cli/agent_loader.py +56 -8
  5. alita_sdk/cli/agent_ui.py +93 -31
  6. alita_sdk/cli/agents.py +2274 -230
  7. alita_sdk/cli/callbacks.py +96 -25
  8. alita_sdk/cli/cli.py +10 -1
  9. alita_sdk/cli/config.py +162 -9
  10. alita_sdk/cli/context/__init__.py +30 -0
  11. alita_sdk/cli/context/cleanup.py +198 -0
  12. alita_sdk/cli/context/manager.py +731 -0
  13. alita_sdk/cli/context/message.py +285 -0
  14. alita_sdk/cli/context/strategies.py +289 -0
  15. alita_sdk/cli/context/token_estimation.py +127 -0
  16. alita_sdk/cli/input_handler.py +419 -0
  17. alita_sdk/cli/inventory.py +1073 -0
  18. alita_sdk/cli/testcases/__init__.py +94 -0
  19. alita_sdk/cli/testcases/data_generation.py +119 -0
  20. alita_sdk/cli/testcases/discovery.py +96 -0
  21. alita_sdk/cli/testcases/executor.py +84 -0
  22. alita_sdk/cli/testcases/logger.py +85 -0
  23. alita_sdk/cli/testcases/parser.py +172 -0
  24. alita_sdk/cli/testcases/prompts.py +91 -0
  25. alita_sdk/cli/testcases/reporting.py +125 -0
  26. alita_sdk/cli/testcases/setup.py +108 -0
  27. alita_sdk/cli/testcases/test_runner.py +282 -0
  28. alita_sdk/cli/testcases/utils.py +39 -0
  29. alita_sdk/cli/testcases/validation.py +90 -0
  30. alita_sdk/cli/testcases/workflow.py +196 -0
  31. alita_sdk/cli/toolkit.py +14 -17
  32. alita_sdk/cli/toolkit_loader.py +35 -5
  33. alita_sdk/cli/tools/__init__.py +36 -2
  34. alita_sdk/cli/tools/approval.py +224 -0
  35. alita_sdk/cli/tools/filesystem.py +910 -64
  36. alita_sdk/cli/tools/planning.py +389 -0
  37. alita_sdk/cli/tools/terminal.py +414 -0
  38. alita_sdk/community/__init__.py +72 -12
  39. alita_sdk/community/inventory/__init__.py +236 -0
  40. alita_sdk/community/inventory/config.py +257 -0
  41. alita_sdk/community/inventory/enrichment.py +2137 -0
  42. alita_sdk/community/inventory/extractors.py +1469 -0
  43. alita_sdk/community/inventory/ingestion.py +3172 -0
  44. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  45. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  46. alita_sdk/community/inventory/parsers/base.py +295 -0
  47. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  48. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  49. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  50. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  51. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  52. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  53. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  54. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  55. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  56. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  57. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  58. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  59. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  60. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  61. alita_sdk/community/inventory/patterns/loader.py +348 -0
  62. alita_sdk/community/inventory/patterns/registry.py +198 -0
  63. alita_sdk/community/inventory/presets.py +535 -0
  64. alita_sdk/community/inventory/retrieval.py +1403 -0
  65. alita_sdk/community/inventory/toolkit.py +173 -0
  66. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  67. alita_sdk/community/inventory/visualize.py +1370 -0
  68. alita_sdk/configurations/__init__.py +1 -1
  69. alita_sdk/configurations/ado.py +141 -20
  70. alita_sdk/configurations/bitbucket.py +0 -3
  71. alita_sdk/configurations/confluence.py +76 -42
  72. alita_sdk/configurations/figma.py +76 -0
  73. alita_sdk/configurations/gitlab.py +17 -5
  74. alita_sdk/configurations/openapi.py +329 -0
  75. alita_sdk/configurations/qtest.py +72 -1
  76. alita_sdk/configurations/report_portal.py +96 -0
  77. alita_sdk/configurations/sharepoint.py +148 -0
  78. alita_sdk/configurations/testio.py +83 -0
  79. alita_sdk/runtime/clients/artifact.py +3 -3
  80. alita_sdk/runtime/clients/client.py +353 -48
  81. alita_sdk/runtime/clients/sandbox_client.py +0 -21
  82. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  83. alita_sdk/runtime/langchain/assistant.py +123 -26
  84. alita_sdk/runtime/langchain/constants.py +642 -1
  85. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  86. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  87. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +6 -3
  88. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
  89. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  90. alita_sdk/runtime/langchain/document_loaders/constants.py +12 -7
  91. alita_sdk/runtime/langchain/langraph_agent.py +279 -73
  92. alita_sdk/runtime/langchain/utils.py +82 -15
  93. alita_sdk/runtime/llms/preloaded.py +2 -6
  94. alita_sdk/runtime/skills/__init__.py +91 -0
  95. alita_sdk/runtime/skills/callbacks.py +498 -0
  96. alita_sdk/runtime/skills/discovery.py +540 -0
  97. alita_sdk/runtime/skills/executor.py +610 -0
  98. alita_sdk/runtime/skills/input_builder.py +371 -0
  99. alita_sdk/runtime/skills/models.py +330 -0
  100. alita_sdk/runtime/skills/registry.py +355 -0
  101. alita_sdk/runtime/skills/skill_runner.py +330 -0
  102. alita_sdk/runtime/toolkits/__init__.py +7 -0
  103. alita_sdk/runtime/toolkits/application.py +21 -9
  104. alita_sdk/runtime/toolkits/artifact.py +15 -5
  105. alita_sdk/runtime/toolkits/datasource.py +13 -6
  106. alita_sdk/runtime/toolkits/mcp.py +139 -251
  107. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  108. alita_sdk/runtime/toolkits/planning.py +178 -0
  109. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  110. alita_sdk/runtime/toolkits/subgraph.py +251 -6
  111. alita_sdk/runtime/toolkits/tools.py +238 -32
  112. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  113. alita_sdk/runtime/tools/__init__.py +3 -1
  114. alita_sdk/runtime/tools/application.py +20 -6
  115. alita_sdk/runtime/tools/artifact.py +511 -28
  116. alita_sdk/runtime/tools/data_analysis.py +183 -0
  117. alita_sdk/runtime/tools/function.py +43 -15
  118. alita_sdk/runtime/tools/image_generation.py +50 -44
  119. alita_sdk/runtime/tools/llm.py +852 -67
  120. alita_sdk/runtime/tools/loop.py +3 -1
  121. alita_sdk/runtime/tools/loop_output.py +3 -1
  122. alita_sdk/runtime/tools/mcp_remote_tool.py +25 -10
  123. alita_sdk/runtime/tools/mcp_server_tool.py +7 -6
  124. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  125. alita_sdk/runtime/tools/planning/models.py +246 -0
  126. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  127. alita_sdk/runtime/tools/router.py +2 -4
  128. alita_sdk/runtime/tools/sandbox.py +9 -6
  129. alita_sdk/runtime/tools/skill_router.py +776 -0
  130. alita_sdk/runtime/tools/tool.py +3 -1
  131. alita_sdk/runtime/tools/vectorstore.py +7 -2
  132. alita_sdk/runtime/tools/vectorstore_base.py +51 -11
  133. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  134. alita_sdk/runtime/utils/constants.py +5 -1
  135. alita_sdk/runtime/utils/mcp_client.py +492 -0
  136. alita_sdk/runtime/utils/mcp_oauth.py +202 -5
  137. alita_sdk/runtime/utils/mcp_sse_client.py +36 -7
  138. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  139. alita_sdk/runtime/utils/serialization.py +155 -0
  140. alita_sdk/runtime/utils/streamlit.py +6 -10
  141. alita_sdk/runtime/utils/toolkit_utils.py +16 -5
  142. alita_sdk/runtime/utils/utils.py +36 -0
  143. alita_sdk/tools/__init__.py +113 -29
  144. alita_sdk/tools/ado/repos/__init__.py +51 -33
  145. alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
  146. alita_sdk/tools/ado/test_plan/__init__.py +25 -9
  147. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  148. alita_sdk/tools/ado/utils.py +1 -18
  149. alita_sdk/tools/ado/wiki/__init__.py +25 -8
  150. alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
  151. alita_sdk/tools/ado/work_item/__init__.py +26 -9
  152. alita_sdk/tools/ado/work_item/ado_wrapper.py +56 -3
  153. alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
  154. alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
  155. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  156. alita_sdk/tools/azure_ai/search/__init__.py +11 -8
  157. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  158. alita_sdk/tools/base/tool.py +5 -1
  159. alita_sdk/tools/base_indexer_toolkit.py +170 -45
  160. alita_sdk/tools/bitbucket/__init__.py +17 -12
  161. alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
  162. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
  163. alita_sdk/tools/browser/__init__.py +5 -4
  164. alita_sdk/tools/carrier/__init__.py +5 -6
  165. alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
  166. alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
  167. alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
  168. alita_sdk/tools/chunkers/__init__.py +3 -1
  169. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  170. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  171. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  172. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  173. alita_sdk/tools/cloud/aws/__init__.py +10 -7
  174. alita_sdk/tools/cloud/azure/__init__.py +10 -7
  175. alita_sdk/tools/cloud/gcp/__init__.py +10 -7
  176. alita_sdk/tools/cloud/k8s/__init__.py +10 -7
  177. alita_sdk/tools/code/linter/__init__.py +10 -8
  178. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  179. alita_sdk/tools/code/sonar/__init__.py +10 -7
  180. alita_sdk/tools/code_indexer_toolkit.py +73 -23
  181. alita_sdk/tools/confluence/__init__.py +21 -15
  182. alita_sdk/tools/confluence/api_wrapper.py +78 -23
  183. alita_sdk/tools/confluence/loader.py +4 -2
  184. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  185. alita_sdk/tools/elastic/__init__.py +11 -8
  186. alita_sdk/tools/elitea_base.py +493 -30
  187. alita_sdk/tools/figma/__init__.py +58 -11
  188. alita_sdk/tools/figma/api_wrapper.py +1235 -143
  189. alita_sdk/tools/figma/figma_client.py +73 -0
  190. alita_sdk/tools/figma/toon_tools.py +2748 -0
  191. alita_sdk/tools/github/__init__.py +13 -14
  192. alita_sdk/tools/github/github_client.py +224 -100
  193. alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
  194. alita_sdk/tools/github/schemas.py +14 -5
  195. alita_sdk/tools/github/tool.py +5 -1
  196. alita_sdk/tools/github/tool_prompts.py +9 -22
  197. alita_sdk/tools/gitlab/__init__.py +15 -11
  198. alita_sdk/tools/gitlab/api_wrapper.py +207 -41
  199. alita_sdk/tools/gitlab_org/__init__.py +10 -8
  200. alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
  201. alita_sdk/tools/google/bigquery/__init__.py +13 -12
  202. alita_sdk/tools/google/bigquery/tool.py +5 -1
  203. alita_sdk/tools/google_places/__init__.py +10 -8
  204. alita_sdk/tools/google_places/api_wrapper.py +1 -1
  205. alita_sdk/tools/jira/__init__.py +17 -11
  206. alita_sdk/tools/jira/api_wrapper.py +91 -40
  207. alita_sdk/tools/keycloak/__init__.py +11 -8
  208. alita_sdk/tools/localgit/__init__.py +9 -3
  209. alita_sdk/tools/localgit/local_git.py +62 -54
  210. alita_sdk/tools/localgit/tool.py +5 -1
  211. alita_sdk/tools/memory/__init__.py +11 -3
  212. alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
  213. alita_sdk/tools/ocr/__init__.py +11 -8
  214. alita_sdk/tools/openapi/__init__.py +490 -114
  215. alita_sdk/tools/openapi/api_wrapper.py +1368 -0
  216. alita_sdk/tools/openapi/tool.py +20 -0
  217. alita_sdk/tools/pandas/__init__.py +20 -12
  218. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  219. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  220. alita_sdk/tools/postman/__init__.py +11 -11
  221. alita_sdk/tools/pptx/__init__.py +10 -9
  222. alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
  223. alita_sdk/tools/qtest/__init__.py +30 -10
  224. alita_sdk/tools/qtest/api_wrapper.py +430 -13
  225. alita_sdk/tools/rally/__init__.py +10 -8
  226. alita_sdk/tools/rally/api_wrapper.py +1 -1
  227. alita_sdk/tools/report_portal/__init__.py +12 -9
  228. alita_sdk/tools/salesforce/__init__.py +10 -9
  229. alita_sdk/tools/servicenow/__init__.py +17 -14
  230. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  231. alita_sdk/tools/sharepoint/__init__.py +10 -8
  232. alita_sdk/tools/sharepoint/api_wrapper.py +4 -4
  233. alita_sdk/tools/slack/__init__.py +10 -8
  234. alita_sdk/tools/slack/api_wrapper.py +2 -2
  235. alita_sdk/tools/sql/__init__.py +11 -9
  236. alita_sdk/tools/testio/__init__.py +10 -8
  237. alita_sdk/tools/testrail/__init__.py +11 -8
  238. alita_sdk/tools/testrail/api_wrapper.py +1 -1
  239. alita_sdk/tools/utils/__init__.py +9 -4
  240. alita_sdk/tools/utils/content_parser.py +77 -3
  241. alita_sdk/tools/utils/text_operations.py +410 -0
  242. alita_sdk/tools/utils/tool_prompts.py +79 -0
  243. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +17 -13
  244. alita_sdk/tools/xray/__init__.py +12 -9
  245. alita_sdk/tools/yagmail/__init__.py +9 -3
  246. alita_sdk/tools/zephyr/__init__.py +9 -7
  247. alita_sdk/tools/zephyr_enterprise/__init__.py +11 -8
  248. alita_sdk/tools/zephyr_essential/__init__.py +10 -8
  249. alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
  250. alita_sdk/tools/zephyr_essential/client.py +2 -2
  251. alita_sdk/tools/zephyr_scale/__init__.py +11 -9
  252. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  253. alita_sdk/tools/zephyr_squad/__init__.py +10 -8
  254. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +147 -7
  255. alita_sdk-0.3.627.dist-info/RECORD +468 -0
  256. alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
  257. alita_sdk-0.3.462.dist-info/RECORD +0 -384
  258. alita_sdk-0.3.462.dist-info/entry_points.txt +0 -2
  259. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
  260. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
  261. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
@@ -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")
@@ -0,0 +1,84 @@
1
+ """
2
+ Executor cache management for test execution.
3
+
4
+ Handles creating, caching, and cleaning up agent executors.
5
+ """
6
+
7
+ import logging
8
+ import sqlite3
9
+ from typing import Dict, Optional, Any, Tuple
10
+ from rich.console import Console
11
+
12
+ logger = logging.getLogger(__name__)
13
+ console = Console()
14
+
15
+
16
+ def cleanup_executor_cache(cache: Dict[str, tuple], cache_name: str = "executor") -> None:
17
+ """Clean up executor cache resources.
18
+
19
+ Args:
20
+ cache: Dictionary of cached executors
21
+ cache_name: Name of cache for logging
22
+ """
23
+ console.print(f"[dim]Cleaning up {cache_name} cache...[/dim]")
24
+ for cache_key, cached_items in cache.items():
25
+ try:
26
+ # Extract memory from tuple (second element)
27
+ memory = cached_items[1] if len(cached_items) > 1 else None
28
+
29
+ # Close SQLite memory connection
30
+ if memory and hasattr(memory, 'conn') and memory.conn:
31
+ memory.conn.close()
32
+ except Exception as e:
33
+ logger.debug(f"Error cleaning up {cache_name} cache for {cache_key}: {e}")
34
+
35
+
36
+ def create_executor_from_cache(
37
+ cache: Dict[str, tuple],
38
+ cache_key: str,
39
+ client,
40
+ agent_def: Dict[str, Any],
41
+ toolkit_config_path: Optional[str],
42
+ config,
43
+ model: Optional[str],
44
+ temperature: Optional[float],
45
+ max_tokens: Optional[int],
46
+ work_dir: Optional[str],
47
+ setup_executor_func
48
+ ) -> Tuple:
49
+ """Get or create executor from cache.
50
+
51
+ Args:
52
+ cache: Executor cache dictionary
53
+ cache_key: Key for caching
54
+ client: API client
55
+ agent_def: Agent definition
56
+ toolkit_config_path: Path to toolkit config
57
+ config: CLI configuration
58
+ model: Model override
59
+ temperature: Temperature override
60
+ max_tokens: Max tokens override
61
+ work_dir: Working directory
62
+ setup_executor_func: Function to setup local agent executor
63
+
64
+ Returns:
65
+ Tuple of (agent_executor, memory, mcp_session_manager)
66
+ """
67
+ if cache_key in cache:
68
+ return cache[cache_key]
69
+
70
+ # Create new executor
71
+ from langgraph.checkpoint.sqlite import SqliteSaver
72
+
73
+ memory = SqliteSaver(sqlite3.connect(":memory:", check_same_thread=False))
74
+ toolkit_config_tuple = (toolkit_config_path,) if toolkit_config_path else ()
75
+
76
+ agent_executor, mcp_session_manager, _, _, _, _, _ = setup_executor_func(
77
+ client, agent_def, toolkit_config_tuple, config, model, temperature,
78
+ max_tokens, memory, work_dir
79
+ )
80
+
81
+ # Cache the executor
82
+ cached_tuple = (agent_executor, memory, mcp_session_manager)
83
+ cache[cache_key] = cached_tuple
84
+ return cached_tuple
@@ -0,0 +1,85 @@
1
+ """
2
+ Test execution logging utilities.
3
+
4
+ Provides the TestLogCapture context manager for capturing console output to log files.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from datetime import datetime
9
+ from typing import Optional
10
+
11
+ from rich.console import Console
12
+
13
+
14
+ class TestLogCapture:
15
+ """
16
+ Context manager to capture console output to a log file.
17
+ Creates a single log file for the entire test execution session.
18
+ Strips Rich markup for plain text output with UTF-8 encoding.
19
+ """
20
+
21
+ def __init__(self, results_dir: Path, session_name: str, console: Optional[Console] = None):
22
+ """
23
+ Initialize log capture.
24
+
25
+ Args:
26
+ results_dir: Base results directory
27
+ session_name: Name for this test session
28
+ """
29
+ self.results_dir = results_dir
30
+ self.session_name = session_name
31
+ self.log_file_path = None
32
+ self.was_recording = False
33
+ # Use injected console if provided; otherwise create a dedicated console.
34
+ # Injecting allows other components (e.g., tool-call callbacks) to print to the
35
+ # same console and be captured in the session log.
36
+ self.console: Console = console or Console()
37
+
38
+ def __enter__(self):
39
+ """Start capturing console output."""
40
+ # Extract toolkit name from session name (e.g., "test-execution-confluence" -> "confluence")
41
+ toolkit_name = self.session_name.replace('test-execution-', '')
42
+
43
+ # Create toolkit-specific directory
44
+ toolkit_dir = self.results_dir / toolkit_name
45
+ toolkit_dir.mkdir(parents=True, exist_ok=True)
46
+
47
+ # Generate timestamped filename
48
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
49
+ log_filename = f"{timestamp}.{self.session_name}.txt"
50
+ self.log_file_path = toolkit_dir / log_filename
51
+
52
+ # Enable recording on this console
53
+ self.was_recording = self.console.record
54
+ self.console.record = True
55
+
56
+ return self
57
+
58
+ def print(self, *args, **kwargs):
59
+ """Print to console (both display and recording)."""
60
+ self.console.print(*args, **kwargs)
61
+
62
+ def status(self, message, **kwargs):
63
+ """Create status spinner and log the message."""
64
+ # Manually record the status message (spinners are transient)
65
+ self.console.print(message)
66
+ # Show spinner (this won't be recorded due to transient nature)
67
+ return self.console.status(message, **kwargs)
68
+
69
+ def __exit__(self, exc_type, exc_val, exc_tb):
70
+ """Stop capturing and save log file."""
71
+ if self.log_file_path:
72
+ # Export recorded text (strips Rich markup)
73
+ plain_text = self.console.export_text()
74
+
75
+ # Save to file with UTF-8 encoding
76
+ with open(self.log_file_path, 'w', encoding='utf-8') as f:
77
+ f.write(f"Test Execution Session: {self.session_name}\n")
78
+ f.write(f"Timestamp: {datetime.now().isoformat()}\n")
79
+ f.write("=" * 80 + "\n\n")
80
+ f.write(plain_text)
81
+
82
+ # Restore previous recording state
83
+ self.console.record = self.was_recording
84
+
85
+ return False
@@ -0,0 +1,172 @@
1
+ """
2
+ Test case parsing utilities.
3
+
4
+ Handles parsing of test case markdown files and resolving configuration paths.
5
+ """
6
+
7
+ import re
8
+ from pathlib import Path
9
+ from typing import Optional, Dict, Any
10
+
11
+
12
+ def resolve_toolkit_config_path(config_path_str: str, test_file: Path, test_cases_dir: Path) -> Optional[str]:
13
+ """
14
+ Resolve toolkit configuration file path from test case.
15
+
16
+ Tries multiple locations in order:
17
+ 1. Absolute path
18
+ 2. Relative to test case file directory
19
+ 3. Relative to test cases directory
20
+ 4. Relative to workspace root
21
+
22
+ Args:
23
+ config_path_str: Config path from test case
24
+ test_file: Path to the test case file
25
+ test_cases_dir: Path to test cases directory
26
+
27
+ Returns:
28
+ Absolute path to config file if found, None otherwise
29
+ """
30
+ if not config_path_str:
31
+ return None
32
+
33
+ # Normalize path separators
34
+ config_path_str = config_path_str.replace('\\', '/')
35
+
36
+ # Try absolute path first
37
+ config_path = Path(config_path_str)
38
+ if config_path.is_absolute() and config_path.exists():
39
+ return str(config_path)
40
+
41
+ # Try relative to test case file directory
42
+ config_path = test_file.parent / config_path_str
43
+ if config_path.exists():
44
+ return str(config_path)
45
+
46
+ # Try relative to test_cases_dir
47
+ config_path = test_cases_dir / config_path_str
48
+ if config_path.exists():
49
+ return str(config_path)
50
+
51
+ # Try relative to workspace root
52
+ workspace_root = Path.cwd()
53
+ config_path = workspace_root / config_path_str
54
+ if config_path.exists():
55
+ return str(config_path)
56
+
57
+ return None
58
+
59
+
60
+ def parse_test_case(test_case_path: str) -> Dict[str, Any]:
61
+ """
62
+ Parse a test case markdown file to extract configuration, steps, and expectations.
63
+
64
+ Args:
65
+ test_case_path: Path to the test case markdown file
66
+
67
+ Returns:
68
+ Dictionary containing:
69
+ - name: Test case name
70
+ - objective: Test objective
71
+ - config_path: Path to toolkit config file
72
+ - generate_test_data: Boolean flag indicating if test data generation is needed (default: True)
73
+ - test_data_config: Dictionary of test data configuration from table
74
+ - prerequisites: Pre-requisites section text
75
+ - variables: List of variable placeholders found (e.g., {{TEST_PR_NUMBER}})
76
+ - steps: List of test steps with their descriptions
77
+ - expectations: List of expectations/assertions
78
+ """
79
+ path = Path(test_case_path)
80
+ if not path.exists():
81
+ raise FileNotFoundError(f"Test case not found: {test_case_path}")
82
+
83
+ content = path.read_text(encoding='utf-8')
84
+
85
+ # Extract test case name from the first heading
86
+ name_match = re.search(r'^#\s+(.+)$', content, re.MULTILINE)
87
+ name = name_match.group(1) if name_match else path.stem
88
+
89
+ # Extract objective
90
+ objective_match = re.search(r'##\s+Objective\s*\n\n(.+?)(?=\n\n##|\Z)', content, re.DOTALL)
91
+ objective = objective_match.group(1).strip() if objective_match else ""
92
+
93
+ # Extract config path and generateTestData flag
94
+ config_section_match = re.search(r'##\s+Config\s*\n\n(.+?)(?=\n\n##|\Z)', content, re.DOTALL)
95
+ config_path = None
96
+ generate_test_data = True # Default to True if not specified
97
+
98
+ if config_section_match:
99
+ config_section = config_section_match.group(1)
100
+ # Extract path
101
+ path_match = re.search(r'path:\s*(.+?)(?=\n|$)', config_section, re.MULTILINE)
102
+ if path_match:
103
+ config_path = path_match.group(1).strip()
104
+
105
+ # Extract generateTestData flag
106
+ gen_data_match = re.search(r'generateTestData\s*:\s*(true|false)', config_section, re.IGNORECASE)
107
+ if gen_data_match:
108
+ generate_test_data = gen_data_match.group(1).lower() == 'true'
109
+
110
+ # Extract Test Data Configuration section as a raw fenced code block string
111
+ # NOTE: We intentionally store the entire section as a single string rather than parsing
112
+ # individual table rows. This preserves the original formatting for downstream tools
113
+ # which may prefer the raw markdown block.
114
+ test_data_config = None
115
+ config_section_match = re.search(r'##\s+Test Data Configuration\s*\n(.+?)(?=\n##|\Z)', content, re.DOTALL)
116
+ if config_section_match:
117
+ config_section = config_section_match.group(1).strip()
118
+ # Store as a fenced code block to make it clear this is a raw block of text
119
+ test_data_config = f"\n{config_section}\n"
120
+
121
+ # Extract Pre-requisites section
122
+ prerequisites = ""
123
+ prereq_match = re.search(r'##\s+Pre-requisites\s*\n\n(.+?)(?=\n\n##|\Z)', content, re.DOTALL)
124
+ if prereq_match:
125
+ prerequisites = prereq_match.group(1).strip()
126
+
127
+ # Find all variable placeholders ({{VARIABLE_NAME}})
128
+ variables = list(set(re.findall(r'\{\{([A-Z_]+)\}\}', content)))
129
+
130
+ # Extract test steps and expectations
131
+ steps = []
132
+ expectations = []
133
+
134
+ # Find all Step sections
135
+ step_pattern = r'###\s+Step\s+(\d+):\s+(.+?)\n\n(.+?)(?=\n\n###|\n\n##|\Z)'
136
+ for step_match in re.finditer(step_pattern, content, re.DOTALL):
137
+ step_num = step_match.group(1)
138
+ step_title = step_match.group(2).strip()
139
+ step_content = step_match.group(3).strip()
140
+
141
+ # Extract the actual instruction (first paragraph before "Expectation:")
142
+ instruction_match = re.search(r'(.+?)(?=\n\n\*\*Expectation:\*\*|\Z)', step_content, re.DOTALL)
143
+ instruction = instruction_match.group(1).strip() if instruction_match else step_content
144
+
145
+ # Extract expectation if present
146
+ expectation_match = re.search(r'\*\*Expectation:\*\*\s+(.+)', step_content, re.DOTALL)
147
+ expectation = expectation_match.group(1).strip() if expectation_match else None
148
+
149
+ steps.append({
150
+ 'number': int(step_num),
151
+ 'title': step_title,
152
+ 'instruction': instruction,
153
+ 'expectation': expectation
154
+ })
155
+
156
+ if expectation:
157
+ expectations.append({
158
+ 'step': int(step_num),
159
+ 'description': expectation
160
+ })
161
+
162
+ return {
163
+ 'name': name,
164
+ 'objective': objective,
165
+ 'config_path': config_path,
166
+ 'generate_test_data': generate_test_data,
167
+ 'test_data_config': test_data_config,
168
+ 'prerequisites': prerequisites,
169
+ 'variables': variables,
170
+ 'steps': steps,
171
+ 'expectations': expectations
172
+ }