igel-qe-core 1.0.0

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 (394) hide show
  1. package/dist/cli/index.js +38 -0
  2. package/dist/mcp/server.js +104 -0
  3. package/knowledge_base/__init__.py +3 -0
  4. package/knowledge_base/__pycache__/__init__.cpython-314.pyc +0 -0
  5. package/knowledge_base/__pycache__/__init__.cpython-37.pyc +0 -0
  6. package/knowledge_base/__pycache__/__init__.cpython-38.pyc +0 -0
  7. package/knowledge_base/__pycache__/confidence.cpython-37.pyc +0 -0
  8. package/knowledge_base/__pycache__/config.cpython-314.pyc +0 -0
  9. package/knowledge_base/__pycache__/config.cpython-37.pyc +0 -0
  10. package/knowledge_base/__pycache__/config.cpython-38.pyc +0 -0
  11. package/knowledge_base/__pycache__/context_fusion.cpython-314.pyc +0 -0
  12. package/knowledge_base/__pycache__/context_fusion.cpython-37.pyc +0 -0
  13. package/knowledge_base/__pycache__/feedback_analytics.cpython-37.pyc +0 -0
  14. package/knowledge_base/__pycache__/feedback_applier.cpython-37.pyc +0 -0
  15. package/knowledge_base/__pycache__/feedback_cli.cpython-37.pyc +0 -0
  16. package/knowledge_base/__pycache__/feedback_curator.cpython-37.pyc +0 -0
  17. package/knowledge_base/__pycache__/feedback_handler.cpython-37.pyc +0 -0
  18. package/knowledge_base/__pycache__/generate.cpython-37.pyc +0 -0
  19. package/knowledge_base/__pycache__/generate_qcapps134.cpython-37.pyc +0 -0
  20. package/knowledge_base/__pycache__/input_classifier.cpython-314.pyc +0 -0
  21. package/knowledge_base/__pycache__/input_classifier.cpython-37.pyc +0 -0
  22. package/knowledge_base/__pycache__/kg_extractor.cpython-37.pyc +0 -0
  23. package/knowledge_base/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
  24. package/knowledge_base/__pycache__/knowledge_graph.cpython-37.pyc +0 -0
  25. package/knowledge_base/__pycache__/manual_tc_parser.cpython-314.pyc +0 -0
  26. package/knowledge_base/__pycache__/manual_tc_parser.cpython-37.pyc +0 -0
  27. package/knowledge_base/__pycache__/precedence.cpython-37.pyc +0 -0
  28. package/knowledge_base/__pycache__/prompts.cpython-314.pyc +0 -0
  29. package/knowledge_base/__pycache__/prompts.cpython-37.pyc +0 -0
  30. package/knowledge_base/__pycache__/reranker.cpython-314.pyc +0 -0
  31. package/knowledge_base/__pycache__/reranker.cpython-37.pyc +0 -0
  32. package/knowledge_base/__pycache__/retrieval_debug.cpython-314.pyc +0 -0
  33. package/knowledge_base/__pycache__/retrieval_debug.cpython-37.pyc +0 -0
  34. package/knowledge_base/__pycache__/retriever.cpython-314.pyc +0 -0
  35. package/knowledge_base/__pycache__/retriever.cpython-37.pyc +0 -0
  36. package/knowledge_base/__pycache__/server.cpython-37.pyc +0 -0
  37. package/knowledge_base/__pycache__/tc_transformer.cpython-314.pyc +0 -0
  38. package/knowledge_base/__pycache__/tc_transformer.cpython-37.pyc +0 -0
  39. package/knowledge_base/__pycache__/test_generator.cpython-314.pyc +0 -0
  40. package/knowledge_base/__pycache__/test_generator.cpython-37.pyc +0 -0
  41. package/knowledge_base/agents/__init__.py +6 -0
  42. package/knowledge_base/agents/__pycache__/__init__.cpython-37.pyc +0 -0
  43. package/knowledge_base/agents/__pycache__/__init__.cpython-38.pyc +0 -0
  44. package/knowledge_base/agents/__pycache__/fusion_agent.cpython-37.pyc +0 -0
  45. package/knowledge_base/agents/__pycache__/fusion_agent.cpython-38.pyc +0 -0
  46. package/knowledge_base/agents/__pycache__/graph.cpython-37.pyc +0 -0
  47. package/knowledge_base/agents/__pycache__/graph.cpython-38.pyc +0 -0
  48. package/knowledge_base/agents/__pycache__/healing_agent.cpython-37.pyc +0 -0
  49. package/knowledge_base/agents/__pycache__/intent_agent.cpython-37.pyc +0 -0
  50. package/knowledge_base/agents/__pycache__/intent_agent.cpython-38.pyc +0 -0
  51. package/knowledge_base/agents/__pycache__/kg_agent.cpython-37.pyc +0 -0
  52. package/knowledge_base/agents/__pycache__/kg_agent.cpython-38.pyc +0 -0
  53. package/knowledge_base/agents/__pycache__/memory_agent.cpython-37.pyc +0 -0
  54. package/knowledge_base/agents/__pycache__/memory_agent.cpython-38.pyc +0 -0
  55. package/knowledge_base/agents/__pycache__/rca_agent.cpython-37.pyc +0 -0
  56. package/knowledge_base/agents/__pycache__/reflection_agent.cpython-37.pyc +0 -0
  57. package/knowledge_base/agents/__pycache__/reflection_agent.cpython-38.pyc +0 -0
  58. package/knowledge_base/agents/__pycache__/regression_agent.cpython-37.pyc +0 -0
  59. package/knowledge_base/agents/__pycache__/retrieval_agent.cpython-37.pyc +0 -0
  60. package/knowledge_base/agents/__pycache__/retrieval_agent.cpython-38.pyc +0 -0
  61. package/knowledge_base/agents/__pycache__/script_agent.cpython-37.pyc +0 -0
  62. package/knowledge_base/agents/__pycache__/script_agent.cpython-38.pyc +0 -0
  63. package/knowledge_base/agents/__pycache__/state.cpython-37.pyc +0 -0
  64. package/knowledge_base/agents/__pycache__/state.cpython-38.pyc +0 -0
  65. package/knowledge_base/agents/__pycache__/temporal_agent.cpython-37.pyc +0 -0
  66. package/knowledge_base/agents/__pycache__/temporal_agent.cpython-38.pyc +0 -0
  67. package/knowledge_base/agents/__pycache__/validation_agent.cpython-37.pyc +0 -0
  68. package/knowledge_base/agents/__pycache__/validation_agent.cpython-38.pyc +0 -0
  69. package/knowledge_base/agents/automation_design_agent.py +123 -0
  70. package/knowledge_base/agents/code_intelligence_agent.py +147 -0
  71. package/knowledge_base/agents/confidence_agent.py +94 -0
  72. package/knowledge_base/agents/execution_validation_agent.py +129 -0
  73. package/knowledge_base/agents/feedback_agent.py +56 -0
  74. package/knowledge_base/agents/fusion_agent.py +113 -0
  75. package/knowledge_base/agents/graph.py +152 -0
  76. package/knowledge_base/agents/healing_agent.py +62 -0
  77. package/knowledge_base/agents/intent_agent.py +76 -0
  78. package/knowledge_base/agents/kg_agent.py +91 -0
  79. package/knowledge_base/agents/memory_agent.py +62 -0
  80. package/knowledge_base/agents/rca_agent.py +53 -0
  81. package/knowledge_base/agents/reflection_agent.py +62 -0
  82. package/knowledge_base/agents/regression_agent.py +81 -0
  83. package/knowledge_base/agents/requirement_agent.py +71 -0
  84. package/knowledge_base/agents/retrieval_agent.py +66 -0
  85. package/knowledge_base/agents/scenario_agent.py +81 -0
  86. package/knowledge_base/agents/script_agent.py +121 -0
  87. package/knowledge_base/agents/script_generator_agent.py +77 -0
  88. package/knowledge_base/agents/state.py +53 -0
  89. package/knowledge_base/agents/temporal_agent.py +48 -0
  90. package/knowledge_base/agents/testcase_agent.py +111 -0
  91. package/knowledge_base/agents/validation_agent.py +49 -0
  92. package/knowledge_base/agents/workflow_engine.py +87 -0
  93. package/knowledge_base/alembic/README.md +104 -0
  94. package/knowledge_base/alembic/__pycache__/env.cpython-37.pyc +0 -0
  95. package/knowledge_base/alembic/__pycache__/env.cpython-38.pyc +0 -0
  96. package/knowledge_base/alembic/env.py +76 -0
  97. package/knowledge_base/alembic/script.py.mako +5 -0
  98. package/knowledge_base/alembic/versions/.gitkeep +26 -0
  99. package/knowledge_base/alembic/versions/001_phase0_feedback.py +122 -0
  100. package/knowledge_base/alembic/versions/002_feedback_promotions.py +60 -0
  101. package/knowledge_base/alembic/versions/003_enterprise_hardening.py +74 -0
  102. package/knowledge_base/alembic/versions/__pycache__/001_phase0_feedback.cpython-37.pyc +0 -0
  103. package/knowledge_base/alembic/versions/__pycache__/001_phase0_feedback.cpython-38.pyc +0 -0
  104. package/knowledge_base/alembic/versions/__pycache__/002_feedback_promotions.cpython-37.pyc +0 -0
  105. package/knowledge_base/alembic/versions/__pycache__/002_feedback_promotions.cpython-38.pyc +0 -0
  106. package/knowledge_base/api/__init__.py +1 -0
  107. package/knowledge_base/api/__pycache__/__init__.cpython-37.pyc +0 -0
  108. package/knowledge_base/api/__pycache__/__init__.cpython-38.pyc +0 -0
  109. package/knowledge_base/api/__pycache__/middleware.cpython-38.pyc +0 -0
  110. package/knowledge_base/api/__pycache__/server.cpython-37.pyc +0 -0
  111. package/knowledge_base/api/__pycache__/server.cpython-38.pyc +0 -0
  112. package/knowledge_base/api/middleware.py +78 -0
  113. package/knowledge_base/api/server.py +778 -0
  114. package/knowledge_base/chunking/__init__.py +0 -0
  115. package/knowledge_base/chunking/__pycache__/__init__.cpython-37.pyc +0 -0
  116. package/knowledge_base/chunking/__pycache__/__init__.cpython-38.pyc +0 -0
  117. package/knowledge_base/chunking/__pycache__/chunker.cpython-37.pyc +0 -0
  118. package/knowledge_base/chunking/__pycache__/chunker.cpython-38.pyc +0 -0
  119. package/knowledge_base/chunking/__pycache__/validators.cpython-37.pyc +0 -0
  120. package/knowledge_base/chunking/__pycache__/validators.cpython-38.pyc +0 -0
  121. package/knowledge_base/chunking/chunker.py +402 -0
  122. package/knowledge_base/chunking/validators.py +144 -0
  123. package/knowledge_base/cli/__init__.py +1 -0
  124. package/knowledge_base/cli/__pycache__/__init__.cpython-37.pyc +0 -0
  125. package/knowledge_base/cli/__pycache__/__init__.cpython-38.pyc +0 -0
  126. package/knowledge_base/cli/__pycache__/feedback_cli.cpython-37.pyc +0 -0
  127. package/knowledge_base/cli/__pycache__/feedback_cli.cpython-38.pyc +0 -0
  128. package/knowledge_base/cli/__pycache__/generate.cpython-37.pyc +0 -0
  129. package/knowledge_base/cli/__pycache__/generate.cpython-38.pyc +0 -0
  130. package/knowledge_base/cli/__pycache__/generate_qcapps134.cpython-37.pyc +0 -0
  131. package/knowledge_base/cli/feedback_cli.py +121 -0
  132. package/knowledge_base/cli/generate.py +308 -0
  133. package/knowledge_base/cli/generate_qcapps134.py +391 -0
  134. package/knowledge_base/config/.env +100 -0
  135. package/knowledge_base/config/.env.example +75 -0
  136. package/knowledge_base/config/__init__.py +5 -0
  137. package/knowledge_base/config/__pycache__/__init__.cpython-37.pyc +0 -0
  138. package/knowledge_base/config/__pycache__/__init__.cpython-38.pyc +0 -0
  139. package/knowledge_base/config/__pycache__/settings.cpython-37.pyc +0 -0
  140. package/knowledge_base/config/__pycache__/settings.cpython-38.pyc +0 -0
  141. package/knowledge_base/config/settings.py +280 -0
  142. package/knowledge_base/connectors/__init__.py +5 -0
  143. package/knowledge_base/connectors/__pycache__/__init__.cpython-37.pyc +0 -0
  144. package/knowledge_base/connectors/__pycache__/__init__.cpython-38.pyc +0 -0
  145. package/knowledge_base/connectors/__pycache__/base.cpython-37.pyc +0 -0
  146. package/knowledge_base/connectors/__pycache__/base.cpython-38.pyc +0 -0
  147. package/knowledge_base/connectors/__pycache__/bitbucket_connector.cpython-37.pyc +0 -0
  148. package/knowledge_base/connectors/__pycache__/bitbucket_connector.cpython-38.pyc +0 -0
  149. package/knowledge_base/connectors/__pycache__/community_connector.cpython-38.pyc +0 -0
  150. package/knowledge_base/connectors/__pycache__/confluence_connector.cpython-37.pyc +0 -0
  151. package/knowledge_base/connectors/__pycache__/confluence_connector.cpython-38.pyc +0 -0
  152. package/knowledge_base/connectors/__pycache__/jira_connector.cpython-37.pyc +0 -0
  153. package/knowledge_base/connectors/__pycache__/jira_connector.cpython-38.pyc +0 -0
  154. package/knowledge_base/connectors/__pycache__/kb_igel_connector.cpython-38.pyc +0 -0
  155. package/knowledge_base/connectors/__pycache__/playwright_fetcher.cpython-38.pyc +0 -0
  156. package/knowledge_base/connectors/__pycache__/query_bridge.cpython-38.pyc +0 -0
  157. package/knowledge_base/connectors/__pycache__/search_backends.cpython-38.pyc +0 -0
  158. package/knowledge_base/connectors/__pycache__/serviceNow_connector.cpython-37.pyc +0 -0
  159. package/knowledge_base/connectors/__pycache__/serviceNow_connector.cpython-38.pyc +0 -0
  160. package/knowledge_base/connectors/__pycache__/sync_scheduler.cpython-37.pyc +0 -0
  161. package/knowledge_base/connectors/__pycache__/sync_scheduler.cpython-38.pyc +0 -0
  162. package/knowledge_base/connectors/__pycache__/teamcity_connector.cpython-37.pyc +0 -0
  163. package/knowledge_base/connectors/__pycache__/teamcity_connector.cpython-38.pyc +0 -0
  164. package/knowledge_base/connectors/__pycache__/testRail_connector.cpython-37.pyc +0 -0
  165. package/knowledge_base/connectors/__pycache__/testRail_connector.cpython-38.pyc +0 -0
  166. package/knowledge_base/connectors/__pycache__/web_fetcher.cpython-38.pyc +0 -0
  167. package/knowledge_base/connectors/base.py +44 -0
  168. package/knowledge_base/connectors/bitbucket_connector.py +25 -0
  169. package/knowledge_base/connectors/community_connector.py +112 -0
  170. package/knowledge_base/connectors/confluence_connector.py +165 -0
  171. package/knowledge_base/connectors/jira_connector.py +174 -0
  172. package/knowledge_base/connectors/kb_igel_connector.py +145 -0
  173. package/knowledge_base/connectors/playwright_fetcher.py +50 -0
  174. package/knowledge_base/connectors/query_bridge.py +170 -0
  175. package/knowledge_base/connectors/search_backends.py +368 -0
  176. package/knowledge_base/connectors/serviceNow_connector.py +25 -0
  177. package/knowledge_base/connectors/sync_scheduler.py +111 -0
  178. package/knowledge_base/connectors/teamcity_connector.py +25 -0
  179. package/knowledge_base/connectors/testRail_connector.py +25 -0
  180. package/knowledge_base/connectors/web_fetcher.py +280 -0
  181. package/knowledge_base/core/__init__.py +8 -0
  182. package/knowledge_base/core/__pycache__/__init__.cpython-37.pyc +0 -0
  183. package/knowledge_base/core/__pycache__/__init__.cpython-38.pyc +0 -0
  184. package/knowledge_base/core/__pycache__/cache.cpython-38.pyc +0 -0
  185. package/knowledge_base/core/__pycache__/circuit_breaker.cpython-38.pyc +0 -0
  186. package/knowledge_base/core/__pycache__/confidence.cpython-37.pyc +0 -0
  187. package/knowledge_base/core/__pycache__/confidence.cpython-38.pyc +0 -0
  188. package/knowledge_base/core/__pycache__/logging_setup.cpython-38.pyc +0 -0
  189. package/knowledge_base/core/__pycache__/metrics.cpython-38.pyc +0 -0
  190. package/knowledge_base/core/__pycache__/precedence.cpython-37.pyc +0 -0
  191. package/knowledge_base/core/__pycache__/precedence.cpython-38.pyc +0 -0
  192. package/knowledge_base/core/__pycache__/request_context.cpython-38.pyc +0 -0
  193. package/knowledge_base/core/cache.py +98 -0
  194. package/knowledge_base/core/circuit_breaker.py +33 -0
  195. package/knowledge_base/core/confidence.py +254 -0
  196. package/knowledge_base/core/logging_setup.py +39 -0
  197. package/knowledge_base/core/metrics.py +63 -0
  198. package/knowledge_base/core/precedence.py +354 -0
  199. package/knowledge_base/core/request_context.py +54 -0
  200. package/knowledge_base/db/__init__.py +0 -0
  201. package/knowledge_base/db/__pycache__/__init__.cpython-314.pyc +0 -0
  202. package/knowledge_base/db/__pycache__/__init__.cpython-37.pyc +0 -0
  203. package/knowledge_base/db/__pycache__/__init__.cpython-38.pyc +0 -0
  204. package/knowledge_base/db/__pycache__/client.cpython-314.pyc +0 -0
  205. package/knowledge_base/db/__pycache__/client.cpython-37.pyc +0 -0
  206. package/knowledge_base/db/__pycache__/client.cpython-38.pyc +0 -0
  207. package/knowledge_base/db/__pycache__/connector_sync.cpython-38.pyc +0 -0
  208. package/knowledge_base/db/__pycache__/feedback_schema.cpython-37.pyc +0 -0
  209. package/knowledge_base/db/__pycache__/feedback_schema.cpython-38.pyc +0 -0
  210. package/knowledge_base/db/__pycache__/kg_introspect.cpython-314.pyc +0 -0
  211. package/knowledge_base/db/__pycache__/kg_introspect.cpython-37.pyc +0 -0
  212. package/knowledge_base/db/__pycache__/kg_introspect.cpython-38.pyc +0 -0
  213. package/knowledge_base/db/__pycache__/schema.cpython-37.pyc +0 -0
  214. package/knowledge_base/db/__pycache__/schema.cpython-38.pyc +0 -0
  215. package/knowledge_base/db/client.py +100 -0
  216. package/knowledge_base/db/connector_sync.py +43 -0
  217. package/knowledge_base/db/feedback_schema.py +548 -0
  218. package/knowledge_base/db/igel_schema.py +150 -0
  219. package/knowledge_base/db/kg_introspect.py +116 -0
  220. package/knowledge_base/db/migrations.py +22 -0
  221. package/knowledge_base/db/schema.py +649 -0
  222. package/knowledge_base/deploy/Dockerfile +23 -0
  223. package/knowledge_base/deploy/feedback_automation.sh +18 -0
  224. package/knowledge_base/deploy/firecrawl-docker.sh +50 -0
  225. package/knowledge_base/deploy/igel-testgen.service +26 -0
  226. package/knowledge_base/deploy/install_reranker.sh +92 -0
  227. package/knowledge_base/deploy/requirements.txt +68 -0
  228. package/knowledge_base/deploy/run_server.sh +35 -0
  229. package/knowledge_base/docs/TEST_GENERATION_GUIDE.md +174 -0
  230. package/knowledge_base/embeddings/__init__.py +0 -0
  231. package/knowledge_base/embeddings/__pycache__/__init__.cpython-314.pyc +0 -0
  232. package/knowledge_base/embeddings/__pycache__/__init__.cpython-37.pyc +0 -0
  233. package/knowledge_base/embeddings/__pycache__/__init__.cpython-38.pyc +0 -0
  234. package/knowledge_base/embeddings/__pycache__/azure_embedder.cpython-314.pyc +0 -0
  235. package/knowledge_base/embeddings/__pycache__/azure_embedder.cpython-37.pyc +0 -0
  236. package/knowledge_base/embeddings/__pycache__/azure_embedder.cpython-38.pyc +0 -0
  237. package/knowledge_base/embeddings/azure_embedder.py +99 -0
  238. package/knowledge_base/eval/__init__.py +33 -0
  239. package/knowledge_base/eval/__main__.py +172 -0
  240. package/knowledge_base/eval/__pycache__/__init__.cpython-37.pyc +0 -0
  241. package/knowledge_base/eval/__pycache__/__init__.cpython-38.pyc +0 -0
  242. package/knowledge_base/eval/__pycache__/__main__.cpython-37.pyc +0 -0
  243. package/knowledge_base/eval/__pycache__/baseline.cpython-37.pyc +0 -0
  244. package/knowledge_base/eval/__pycache__/baseline.cpython-38.pyc +0 -0
  245. package/knowledge_base/eval/__pycache__/harness.cpython-37.pyc +0 -0
  246. package/knowledge_base/eval/__pycache__/harness.cpython-38.pyc +0 -0
  247. package/knowledge_base/eval/__pycache__/scorers.cpython-37.pyc +0 -0
  248. package/knowledge_base/eval/__pycache__/scorers.cpython-38.pyc +0 -0
  249. package/knowledge_base/eval/baseline.py +213 -0
  250. package/knowledge_base/eval/harness.py +400 -0
  251. package/knowledge_base/eval/queries_50.json +302 -0
  252. package/knowledge_base/eval/scorers.py +494 -0
  253. package/knowledge_base/exemplars/README.md +7 -0
  254. package/knowledge_base/exemplars/__pycache__/pytest_reference_snippet.cpython-37.pyc +0 -0
  255. package/knowledge_base/exemplars/gold_manual_concise_snippet.md +8 -0
  256. package/knowledge_base/exemplars/gold_manual_detailed_snippet.md +7 -0
  257. package/knowledge_base/exemplars/pytest_reference_snippet.py +37 -0
  258. package/knowledge_base/feedback/__init__.py +10 -0
  259. package/knowledge_base/feedback/__pycache__/__init__.cpython-37.pyc +0 -0
  260. package/knowledge_base/feedback/__pycache__/__init__.cpython-38.pyc +0 -0
  261. package/knowledge_base/feedback/__pycache__/analytics.cpython-37.pyc +0 -0
  262. package/knowledge_base/feedback/__pycache__/analytics.cpython-38.pyc +0 -0
  263. package/knowledge_base/feedback/__pycache__/applier.cpython-37.pyc +0 -0
  264. package/knowledge_base/feedback/__pycache__/applier.cpython-38.pyc +0 -0
  265. package/knowledge_base/feedback/__pycache__/curator.cpython-37.pyc +0 -0
  266. package/knowledge_base/feedback/__pycache__/curator.cpython-38.pyc +0 -0
  267. package/knowledge_base/feedback/__pycache__/handler.cpython-37.pyc +0 -0
  268. package/knowledge_base/feedback/__pycache__/handler.cpython-38.pyc +0 -0
  269. package/knowledge_base/feedback/analytics.py +72 -0
  270. package/knowledge_base/feedback/applier.py +93 -0
  271. package/knowledge_base/feedback/curator.py +49 -0
  272. package/knowledge_base/feedback/handler.py +151 -0
  273. package/knowledge_base/generation/__init__.py +5 -0
  274. package/knowledge_base/generation/__pycache__/__init__.cpython-37.pyc +0 -0
  275. package/knowledge_base/generation/__pycache__/__init__.cpython-38.pyc +0 -0
  276. package/knowledge_base/generation/__pycache__/input_classifier.cpython-37.pyc +0 -0
  277. package/knowledge_base/generation/__pycache__/input_classifier.cpython-38.pyc +0 -0
  278. package/knowledge_base/generation/__pycache__/manual_tc_parser.cpython-37.pyc +0 -0
  279. package/knowledge_base/generation/__pycache__/manual_tc_parser.cpython-38.pyc +0 -0
  280. package/knowledge_base/generation/__pycache__/prompts.cpython-37.pyc +0 -0
  281. package/knowledge_base/generation/__pycache__/prompts.cpython-38.pyc +0 -0
  282. package/knowledge_base/generation/__pycache__/tc_transformer.cpython-37.pyc +0 -0
  283. package/knowledge_base/generation/__pycache__/test_generator.cpython-37.pyc +0 -0
  284. package/knowledge_base/generation/__pycache__/test_generator.cpython-38.pyc +0 -0
  285. package/knowledge_base/generation/input_classifier.py +148 -0
  286. package/knowledge_base/generation/manual_tc_parser.py +180 -0
  287. package/knowledge_base/generation/prompts.py +755 -0
  288. package/knowledge_base/generation/tc_transformer.py +368 -0
  289. package/knowledge_base/generation/test_generator.py +553 -0
  290. package/knowledge_base/graph_db/__init__.py +5 -0
  291. package/knowledge_base/graph_db/__pycache__/__init__.cpython-37.pyc +0 -0
  292. package/knowledge_base/graph_db/__pycache__/__init__.cpython-38.pyc +0 -0
  293. package/knowledge_base/graph_db/__pycache__/graphiti_client.cpython-37.pyc +0 -0
  294. package/knowledge_base/graph_db/__pycache__/kg_traversal.cpython-37.pyc +0 -0
  295. package/knowledge_base/graph_db/__pycache__/migrate_kg.cpython-37.pyc +0 -0
  296. package/knowledge_base/graph_db/__pycache__/neo4j_client.cpython-37.pyc +0 -0
  297. package/knowledge_base/graph_db/__pycache__/neo4j_client.cpython-38.pyc +0 -0
  298. package/knowledge_base/graph_db/__pycache__/schema.cpython-37.pyc +0 -0
  299. package/knowledge_base/graph_db/__pycache__/temporal_ingest.cpython-37.pyc +0 -0
  300. package/knowledge_base/graph_db/graphiti_client.py +127 -0
  301. package/knowledge_base/graph_db/kg_traversal.py +192 -0
  302. package/knowledge_base/graph_db/migrate_kg.py +93 -0
  303. package/knowledge_base/graph_db/neo4j_client.py +116 -0
  304. package/knowledge_base/graph_db/schema.py +22 -0
  305. package/knowledge_base/graph_db/temporal_ingest.py +59 -0
  306. package/knowledge_base/ingestion/__init__.py +1 -0
  307. package/knowledge_base/ingestion/__pycache__/__init__.cpython-37.pyc +0 -0
  308. package/knowledge_base/ingestion/__pycache__/__init__.cpython-38.pyc +0 -0
  309. package/knowledge_base/ingestion/__pycache__/ingest.cpython-37.pyc +0 -0
  310. package/knowledge_base/ingestion/__pycache__/ingest.cpython-38.pyc +0 -0
  311. package/knowledge_base/ingestion/__pycache__/ingest_images.cpython-37.pyc +0 -0
  312. package/knowledge_base/ingestion/__pycache__/ingest_images.cpython-38.pyc +0 -0
  313. package/knowledge_base/ingestion/__pycache__/kg_extractor.cpython-37.pyc +0 -0
  314. package/knowledge_base/ingestion/__pycache__/kg_extractor.cpython-38.pyc +0 -0
  315. package/knowledge_base/ingestion/__pycache__/web_ingest.cpython-38.pyc +0 -0
  316. package/knowledge_base/ingestion/ingest.py +346 -0
  317. package/knowledge_base/ingestion/ingest_images.py +330 -0
  318. package/knowledge_base/ingestion/kg_extractor.py +358 -0
  319. package/knowledge_base/ingestion/web_ingest.py +83 -0
  320. package/knowledge_base/intelligence/__init__.py +1 -0
  321. package/knowledge_base/intelligence/__pycache__/__init__.cpython-37.pyc +0 -0
  322. package/knowledge_base/intelligence/__pycache__/execution_ingest.cpython-37.pyc +0 -0
  323. package/knowledge_base/intelligence/__pycache__/predictive_engine.cpython-37.pyc +0 -0
  324. package/knowledge_base/intelligence/__pycache__/risk_scorer.cpython-37.pyc +0 -0
  325. package/knowledge_base/intelligence/execution_ingest.py +34 -0
  326. package/knowledge_base/intelligence/predictive_engine.py +7 -0
  327. package/knowledge_base/intelligence/risk_scorer.py +8 -0
  328. package/knowledge_base/memory/__init__.py +6 -0
  329. package/knowledge_base/memory/__pycache__/__init__.cpython-37.pyc +0 -0
  330. package/knowledge_base/memory/__pycache__/__init__.cpython-38.pyc +0 -0
  331. package/knowledge_base/memory/__pycache__/cognee_client.cpython-37.pyc +0 -0
  332. package/knowledge_base/memory/__pycache__/cognee_client.cpython-38.pyc +0 -0
  333. package/knowledge_base/memory/__pycache__/ingest_memory.cpython-37.pyc +0 -0
  334. package/knowledge_base/memory/__pycache__/memory_types.cpython-37.pyc +0 -0
  335. package/knowledge_base/memory/__pycache__/memory_types.cpython-38.pyc +0 -0
  336. package/knowledge_base/memory/cognee_client.py +164 -0
  337. package/knowledge_base/memory/ingest_memory.py +60 -0
  338. package/knowledge_base/memory/memory_types.py +23 -0
  339. package/knowledge_base/models/test_requirement.py +84 -0
  340. package/knowledge_base/parsers/__init__.py +0 -0
  341. package/knowledge_base/parsers/__pycache__/__init__.cpython-37.pyc +0 -0
  342. package/knowledge_base/parsers/__pycache__/__init__.cpython-38.pyc +0 -0
  343. package/knowledge_base/parsers/__pycache__/csv_parser.cpython-37.pyc +0 -0
  344. package/knowledge_base/parsers/__pycache__/csv_parser.cpython-38.pyc +0 -0
  345. package/knowledge_base/parsers/__pycache__/doc_parser.cpython-37.pyc +0 -0
  346. package/knowledge_base/parsers/__pycache__/doc_parser.cpython-38.pyc +0 -0
  347. package/knowledge_base/parsers/__pycache__/html_parser.cpython-38.pyc +0 -0
  348. package/knowledge_base/parsers/__pycache__/image_extractor.cpython-37.pyc +0 -0
  349. package/knowledge_base/parsers/__pycache__/image_extractor.cpython-38.pyc +0 -0
  350. package/knowledge_base/parsers/__pycache__/markdown_parser.cpython-37.pyc +0 -0
  351. package/knowledge_base/parsers/__pycache__/markdown_parser.cpython-38.pyc +0 -0
  352. package/knowledge_base/parsers/__pycache__/pdf_parser.cpython-37.pyc +0 -0
  353. package/knowledge_base/parsers/__pycache__/pdf_parser.cpython-38.pyc +0 -0
  354. package/knowledge_base/parsers/csv_parser.py +126 -0
  355. package/knowledge_base/parsers/doc_parser.py +176 -0
  356. package/knowledge_base/parsers/html_parser.py +151 -0
  357. package/knowledge_base/parsers/image_extractor.py +370 -0
  358. package/knowledge_base/parsers/markdown_parser.py +116 -0
  359. package/knowledge_base/parsers/pdf_parser.py +178 -0
  360. package/knowledge_base/retrieval/__init__.py +1 -0
  361. package/knowledge_base/retrieval/__pycache__/__init__.cpython-37.pyc +0 -0
  362. package/knowledge_base/retrieval/__pycache__/__init__.cpython-38.pyc +0 -0
  363. package/knowledge_base/retrieval/__pycache__/context_fusion.cpython-37.pyc +0 -0
  364. package/knowledge_base/retrieval/__pycache__/context_fusion.cpython-38.pyc +0 -0
  365. package/knowledge_base/retrieval/__pycache__/knowledge_graph.cpython-37.pyc +0 -0
  366. package/knowledge_base/retrieval/__pycache__/knowledge_graph.cpython-38.pyc +0 -0
  367. package/knowledge_base/retrieval/__pycache__/reranker.cpython-37.pyc +0 -0
  368. package/knowledge_base/retrieval/__pycache__/reranker.cpython-38.pyc +0 -0
  369. package/knowledge_base/retrieval/__pycache__/retrieval_debug.cpython-37.pyc +0 -0
  370. package/knowledge_base/retrieval/__pycache__/retrieval_debug.cpython-38.pyc +0 -0
  371. package/knowledge_base/retrieval/__pycache__/retriever.cpython-37.pyc +0 -0
  372. package/knowledge_base/retrieval/__pycache__/retriever.cpython-38.pyc +0 -0
  373. package/knowledge_base/retrieval/__pycache__/unified_query.cpython-38.pyc +0 -0
  374. package/knowledge_base/retrieval/__pycache__/web_retriever.cpython-38.pyc +0 -0
  375. package/knowledge_base/retrieval/context_fusion.py +264 -0
  376. package/knowledge_base/retrieval/knowledge_graph.py +267 -0
  377. package/knowledge_base/retrieval/reranker.py +308 -0
  378. package/knowledge_base/retrieval/retrieval_debug.py +69 -0
  379. package/knowledge_base/retrieval/retriever.py +885 -0
  380. package/knowledge_base/retrieval/unified_query.py +189 -0
  381. package/knowledge_base/retrieval/web_retriever.py +337 -0
  382. package/knowledge_base/validators/__init__.py +1 -0
  383. package/knowledge_base/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  384. package/knowledge_base/validators/__pycache__/__init__.cpython-37.pyc +0 -0
  385. package/knowledge_base/validators/__pycache__/step_fidelity.cpython-314.pyc +0 -0
  386. package/knowledge_base/validators/__pycache__/step_fidelity.cpython-37.pyc +0 -0
  387. package/knowledge_base/validators/step_fidelity.py +88 -0
  388. package/knowledge_base/vision/__init__.py +0 -0
  389. package/knowledge_base/vision/__pycache__/__init__.cpython-37.pyc +0 -0
  390. package/knowledge_base/vision/__pycache__/__init__.cpython-38.pyc +0 -0
  391. package/knowledge_base/vision/__pycache__/image_describer.cpython-37.pyc +0 -0
  392. package/knowledge_base/vision/__pycache__/image_describer.cpython-38.pyc +0 -0
  393. package/knowledge_base/vision/image_describer.py +202 -0
  394. package/package.json +31 -0
@@ -0,0 +1,553 @@
1
+ """
2
+ IGEL Test Case Generator — LLM Orchestration
3
+
4
+ Orchestrates:
5
+ 1. KB retrieval (retriever.py)
6
+ 2. Prompt assembly (prompts.py)
7
+ 3. Two Azure GPT-4.1 calls (markdown spec → Python pytest file)
8
+ 4. Syntax validation + file writing
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import ast
13
+ import logging
14
+ import re
15
+ import time
16
+ from dataclasses import dataclass, field
17
+ from pathlib import Path
18
+
19
+ from openai import AzureOpenAI, RateLimitError, APIConnectionError, APITimeoutError
20
+ from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
21
+
22
+ from knowledge_base.config import cfg
23
+ from knowledge_base.retrieval.unified_query import unified_query
24
+ from knowledge_base.generation.prompts import (
25
+ PromptContext,
26
+ build_system_prompt,
27
+ build_markdown_user_prompt,
28
+ build_python_user_prompt,
29
+ format_kb_retrieval_references_markdown,
30
+ format_kb_retrieval_references_python_comment,
31
+ kb_trace_lines,
32
+ _slugify,
33
+ )
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+ DEFAULT_OUTPUT_DIR = Path(__file__).resolve().parents[2] / "tests" / "Generated"
38
+
39
+ _REQUIRED_IMPORT_MARKERS = [
40
+ "allure", "UMS", "SSHClient", "OcrUiInteractor",
41
+ "ums_login", "ums_cred", "device_cred", "logger",
42
+ ]
43
+
44
+ _FEATURE_KEYWORDS: dict[str, str] = {
45
+ "sso": "SSO Validation",
46
+ "entra": "SSO Validation",
47
+ "okta": "SSO Validation",
48
+ "pingone": "SSO Validation",
49
+ "omnissa": "SSO Validation",
50
+ "ums": "UMS Configuration",
51
+ "profile": "UMS Configuration",
52
+ "wums": "UMS Configuration",
53
+ "reset": "Device Lifecycle",
54
+ "upgrade": "Device Lifecycle",
55
+ "downgrade": "Device Lifecycle",
56
+ "factory": "Device Lifecycle",
57
+ "citrix": "Session Connection",
58
+ "rdp": "Session Connection",
59
+ "avd": "Session Connection",
60
+ "horizon": "Session Connection",
61
+ "windows 365": "Session Connection",
62
+ "certificate": "Security & Certificates",
63
+ "cert": "Security & Certificates",
64
+ "rmagent": "Security & Certificates",
65
+ "obs": "Device Management",
66
+ "broadcast": "Device Management",
67
+ "cic": "Corporate Identity Customization",
68
+ "branding": "Corporate Identity Customization",
69
+ "data retention": "Data Management",
70
+ "ssh": "Device Management",
71
+ }
72
+
73
+
74
+ # ── Data Model ────────────────────────────────────────────────────────────────
75
+
76
+ @dataclass
77
+ class GeneratedTestCase:
78
+ test_id: str
79
+ topic: str
80
+ feature_area: str
81
+ product: str | None
82
+ markdown_path: Path | None
83
+ python_path: Path | None
84
+ kb_sources: list[str]
85
+ kb_chunk_count: int
86
+ tokens_used: int
87
+ generation_time_sec: float
88
+ syntax_valid: bool
89
+ warnings: list[str] = field(default_factory=list)
90
+ retrieval_debug: str | None = None
91
+ traceability_table: str = ""
92
+ confidence: dict | None = None
93
+ retrieved_chunks: list = field(default_factory=list)
94
+ retrieval_metadata: dict = field(default_factory=dict)
95
+ kg_entities: dict | None = None
96
+ precedence_conflicts: list[dict] = field(default_factory=list)
97
+
98
+
99
+ # ── Public API ────────────────────────────────────────────────────────────────
100
+
101
+ def generate_test_cases(
102
+ topic: str,
103
+ output_format: str = "both",
104
+ product: str | None = None,
105
+ test_id: str | None = None,
106
+ feature_area: str | None = None,
107
+ top_k: int = 8,
108
+ output_dir: Path | None = None,
109
+ debug_retrieval: bool = False,
110
+ source_tc: str | None = None,
111
+ detail_level: str | None = None,
112
+ ) -> GeneratedTestCase:
113
+ """
114
+ Generate a test case from the IGEL Knowledge Base.
115
+
116
+ Args:
117
+ topic: Natural language description, e.g. "Entra ID SSO login validation"
118
+ output_format: "md", "py", or "both"
119
+ product: Optional KB filter — "UMS", "IGEL OS", "COSMOS", "ICG", "IMI"
120
+ test_id: Custom ID like "TC015". Auto-generated if None.
121
+ feature_area: Allure feature group. Auto-inferred from topic if None.
122
+ top_k: Number of KB chunks to retrieve.
123
+ output_dir: Where to write files. Defaults to tests/Generated/.
124
+ source_tc: Optional pasted official manual TC (CSV or JIRA export). Enables transform/expand fidelity mode.
125
+ detail_level: "concise", "detailed", or "auto" (default detail policy).
126
+
127
+ Returns:
128
+ GeneratedTestCase with paths and metadata.
129
+
130
+ Raises:
131
+ ValueError: if no KB content found for topic.
132
+ RuntimeError: if LLM calls fail after retries.
133
+ """
134
+ t0 = time.time()
135
+ output_dir = output_dir or DEFAULT_OUTPUT_DIR
136
+ output_dir.mkdir(parents=True, exist_ok=True)
137
+
138
+ from knowledge_base.generation.input_classifier import DetailLevel, InputMode, classify_input
139
+
140
+ try:
141
+ dl = DetailLevel((detail_level or "auto").lower())
142
+ except ValueError:
143
+ dl = DetailLevel.AUTO
144
+ cls = classify_input(topic, source_tc, detail_level=dl)
145
+
146
+ if cls.mode in (InputMode.TRANSFORM, InputMode.EXPAND):
147
+ from knowledge_base.generation.tc_transformer import run_faithful_generation
148
+
149
+ resolved_test_id = test_id or _resolve_test_id(output_dir)
150
+ resolved_feature = feature_area or _infer_feature_area(topic)
151
+ fr = run_faithful_generation(
152
+ topic,
153
+ source_tc=source_tc,
154
+ product=product,
155
+ test_id=resolved_test_id,
156
+ feature_area=resolved_feature,
157
+ top_k=top_k,
158
+ output_format=output_format,
159
+ detail_level=dl,
160
+ )
161
+ md_path: Path | None = None
162
+ py_path: Path | None = None
163
+ if fr.markdown_content and output_format in ("md", "both"):
164
+ md_filename = f"{resolved_test_id}_{_slugify(topic)}.md"
165
+ md_path = _safe_write_path(output_dir / md_filename)
166
+ md_path.write_text(fr.markdown_content.rstrip(), encoding="utf-8")
167
+ if fr.python_content and output_format in ("py", "both"):
168
+ py_filename = f"test_{resolved_test_id}_{_slugify(topic)}.py"
169
+ if not fr.syntax_valid:
170
+ py_filename += ".invalid"
171
+ py_path = _safe_write_path(output_dir / py_filename)
172
+ py_path.write_text(fr.python_content.rstrip(), encoding="utf-8")
173
+ elapsed = round(time.time() - t0, 1)
174
+ md_text = fr.markdown_content or ""
175
+ py_text = fr.python_content
176
+ conf = _score_output(
177
+ topic=topic,
178
+ markdown=md_text,
179
+ python=py_text,
180
+ chunks=getattr(fr, "retrieved_chunks", []),
181
+ retrieval_metadata=getattr(fr, "retrieval_metadata", {}),
182
+ kg_entities=getattr(fr, "kg_entities", None),
183
+ )
184
+ return GeneratedTestCase(
185
+ test_id=resolved_test_id,
186
+ topic=topic,
187
+ feature_area=fr.feature_area,
188
+ product=product,
189
+ markdown_path=md_path,
190
+ python_path=py_path,
191
+ kb_sources=fr.kb_sources,
192
+ kb_chunk_count=fr.kb_chunk_count,
193
+ tokens_used=fr.tokens_used,
194
+ generation_time_sec=elapsed,
195
+ syntax_valid=fr.syntax_valid,
196
+ warnings=fr.warnings,
197
+ retrieval_debug=None,
198
+ traceability_table=fr.traceability_table or "",
199
+ confidence=conf,
200
+ retrieved_chunks=getattr(fr, "retrieved_chunks", []),
201
+ retrieval_metadata=getattr(fr, "retrieval_metadata", {}),
202
+ kg_entities=getattr(fr, "kg_entities", None),
203
+ precedence_conflicts=getattr(fr, "precedence_conflicts", []),
204
+ )
205
+
206
+ # Step 1: Unified retrieval (RAG + EKG + Web + MCP)
207
+ uq = unified_query(query=topic, top_k=top_k, product=product)
208
+ bundle = uq.rag
209
+ if not bundle.results:
210
+ raise ValueError(
211
+ f"No KB content found for topic '{topic}'. "
212
+ "Ensure documents are ingested: python -m knowledge_base.ingestion.ingest"
213
+ )
214
+
215
+ retrieval_debug_json: str | None = bundle.debug.to_json() if debug_retrieval else None
216
+ if debug_retrieval:
217
+ bundle.debug.log_summary()
218
+ logger.debug("Retrieval trace JSON:\n%s", retrieval_debug_json)
219
+
220
+ # Step 2: Resolve test_id and feature_area
221
+ resolved_test_id = test_id or _resolve_test_id(output_dir)
222
+ resolved_feature = feature_area or _infer_feature_area(topic)
223
+
224
+ tokens_used = bundle.debug.total_tokens_used
225
+
226
+ ctx = PromptContext(
227
+ topic=topic,
228
+ test_id=resolved_test_id,
229
+ feature_area=resolved_feature,
230
+ product=product,
231
+ context_chunks=bundle.results,
232
+ token_budget=int(cfg.CONTEXT_TOKEN_BUDGET),
233
+ kg_context_block=bundle.kg_context.text_block,
234
+ image_context_block=bundle.image_block,
235
+ web_context_block=uq.web_block,
236
+ mcp_context_block=uq.mcp.block,
237
+ skip_assemble_window=True,
238
+ )
239
+
240
+ system_prompt = build_system_prompt()
241
+ warnings: list[str] = []
242
+ md_path: Path | None = None
243
+ py_path: Path | None = None
244
+
245
+ # Step 3a: Generate markdown (if requested)
246
+ markdown_content = ""
247
+ if output_format in ("md", "both"):
248
+ logger.info("Generating markdown for '%s'...", topic)
249
+ markdown_user_prompt = build_markdown_user_prompt(ctx)
250
+ markdown_content = _call_gpt(system_prompt, markdown_user_prompt, max_tokens=4096, temperature=0.2)
251
+ md_filename = f"{resolved_test_id}_{_slugify(topic)}.md"
252
+ md_path = _safe_write_path(output_dir / md_filename)
253
+ md_ref = format_kb_retrieval_references_markdown(bundle.results)
254
+ md_body = markdown_content.rstrip() + ("\n\n---\n\n" + md_ref if md_ref else "")
255
+ md_path.write_text(md_body, encoding="utf-8")
256
+ logger.info("Markdown written: %s", md_path)
257
+
258
+ # Step 3b: Generate Python (if requested)
259
+ syntax_valid = True
260
+ if output_format in ("py", "both"):
261
+ logger.info("Generating Python test file for '%s'...", topic)
262
+ # If we didn't generate markdown yet, do a lightweight context for python prompt
263
+ if not markdown_content:
264
+ md_for_python = _build_minimal_spec(ctx)
265
+ else:
266
+ md_for_python = markdown_content
267
+ python_user_prompt = build_python_user_prompt(ctx, md_for_python)
268
+ python_content = _call_gpt(system_prompt, python_user_prompt, max_tokens=3072, temperature=0.1)
269
+ python_content = _strip_code_fences(python_content)
270
+
271
+ # Validate syntax
272
+ syntax_valid, syntax_errors = _validate_python_syntax(python_content)
273
+ import_warnings = _check_required_imports(python_content)
274
+ warnings.extend(syntax_errors)
275
+ warnings.extend(import_warnings)
276
+
277
+ py_filename = f"test_{resolved_test_id}_{_slugify(topic)}.py"
278
+ if not syntax_valid:
279
+ py_filename += ".invalid"
280
+ logger.warning("Generated Python has syntax errors — saved as .invalid: %s", syntax_errors)
281
+
282
+ py_path = _safe_write_path(output_dir / py_filename)
283
+ py_ref = format_kb_retrieval_references_python_comment(bundle.results)
284
+ py_path.write_text(python_content.rstrip() + py_ref, encoding="utf-8")
285
+ logger.info("Python test written: %s", py_path)
286
+
287
+ elapsed = round(time.time() - t0, 1)
288
+ kb_sources = kb_trace_lines(bundle.results)
289
+ trace_table = format_kb_retrieval_references_markdown(bundle.results)
290
+ md_text = md_path.read_text(encoding="utf-8") if md_path and md_path.exists() else markdown_content
291
+ py_text = py_path.read_text(encoding="utf-8") if py_path and py_path.exists() else None
292
+ retrieval_meta = _build_retrieval_metadata(bundle)
293
+ kg_entities = _build_kg_entities(bundle)
294
+ conf = _score_output(
295
+ topic=topic,
296
+ markdown=md_text,
297
+ python=py_text,
298
+ chunks=bundle.results,
299
+ retrieval_metadata=retrieval_meta,
300
+ kg_entities=kg_entities,
301
+ )
302
+
303
+ return GeneratedTestCase(
304
+ test_id=resolved_test_id,
305
+ topic=topic,
306
+ feature_area=resolved_feature,
307
+ product=product,
308
+ markdown_path=md_path,
309
+ python_path=py_path,
310
+ kb_sources=kb_sources,
311
+ kb_chunk_count=len(bundle.results),
312
+ tokens_used=tokens_used,
313
+ generation_time_sec=elapsed,
314
+ syntax_valid=syntax_valid,
315
+ warnings=warnings,
316
+ retrieval_debug=retrieval_debug_json,
317
+ traceability_table=trace_table,
318
+ confidence=conf,
319
+ retrieved_chunks=bundle.results,
320
+ retrieval_metadata=retrieval_meta,
321
+ kg_entities=kg_entities,
322
+ precedence_conflicts=bundle.precedence_conflicts,
323
+ )
324
+
325
+
326
+ def batch_generate(
327
+ topics: list[str],
328
+ output_format: str = "both",
329
+ product: str | None = None,
330
+ feature_area: str | None = None,
331
+ top_k: int = 8,
332
+ output_dir: Path | None = None,
333
+ debug_retrieval: bool = False,
334
+ ) -> list[GeneratedTestCase]:
335
+ """Generate test cases for multiple topics with auto-incrementing test IDs."""
336
+ output_dir = output_dir or DEFAULT_OUTPUT_DIR
337
+ output_dir.mkdir(parents=True, exist_ok=True)
338
+
339
+ # Resolve starting test ID once, then increment manually to avoid re-scanning each iteration
340
+ base_id = _resolve_test_id(output_dir)
341
+ base_num = int(re.search(r"\d+", base_id).group())
342
+
343
+ results_list: list[GeneratedTestCase] = []
344
+ for i, topic in enumerate(topics):
345
+ current_id = f"TC{(base_num + i):03d}"
346
+ try:
347
+ result = generate_test_cases(
348
+ topic=topic,
349
+ output_format=output_format,
350
+ product=product,
351
+ test_id=current_id,
352
+ feature_area=feature_area,
353
+ top_k=top_k,
354
+ output_dir=output_dir,
355
+ debug_retrieval=debug_retrieval,
356
+ )
357
+ results_list.append(result)
358
+ except Exception as e:
359
+ logger.error("batch_generate: failed for topic '%s': %s", topic, e)
360
+ results_list.append(GeneratedTestCase(
361
+ test_id=current_id,
362
+ topic=topic,
363
+ feature_area=feature_area or _infer_feature_area(topic),
364
+ product=product,
365
+ markdown_path=None,
366
+ python_path=None,
367
+ kb_sources=[],
368
+ kb_chunk_count=0,
369
+ tokens_used=0,
370
+ generation_time_sec=0.0,
371
+ syntax_valid=False,
372
+ warnings=[f"FAILED: {e}"],
373
+ retrieval_debug=None,
374
+ traceability_table="",
375
+ ))
376
+
377
+ return results_list
378
+
379
+
380
+ # ── LLM Call ──────────────────────────────────────────────────────────────────
381
+
382
+ _llm_client: AzureOpenAI | None = None
383
+
384
+
385
+ def _get_llm_client() -> AzureOpenAI:
386
+ global _llm_client
387
+ if _llm_client is None:
388
+ _llm_client = AzureOpenAI(
389
+ api_key=cfg.AZURE_API_KEY,
390
+ azure_endpoint=cfg.AZURE_ENDPOINT,
391
+ api_version=cfg.AZURE_API_VERSION,
392
+ )
393
+ return _llm_client
394
+
395
+
396
+ @retry(
397
+ retry=retry_if_exception_type((RateLimitError, APIConnectionError, APITimeoutError)),
398
+ wait=wait_exponential(multiplier=1, min=2, max=30),
399
+ stop=stop_after_attempt(3),
400
+ reraise=True,
401
+ )
402
+ def _call_gpt(
403
+ system_prompt: str,
404
+ user_prompt: str,
405
+ max_tokens: int = 4096,
406
+ temperature: float = 0.2,
407
+ ) -> str:
408
+ client = _get_llm_client()
409
+ response = client.chat.completions.create(
410
+ model=cfg.AZURE_DEPLOYMENT,
411
+ messages=[
412
+ {"role": "system", "content": system_prompt},
413
+ {"role": "user", "content": user_prompt},
414
+ ],
415
+ max_tokens=max_tokens,
416
+ temperature=temperature,
417
+ )
418
+ content = response.choices[0].message.content or ""
419
+ logger.debug("GPT response: %d chars", len(content))
420
+ return content
421
+
422
+
423
+ # ── Post-Processing ───────────────────────────────────────────────────────────
424
+
425
+ def _strip_code_fences(code: str) -> str:
426
+ code = re.sub(r"^```python\s*\n?", "", code, flags=re.MULTILINE)
427
+ code = re.sub(r"^```\s*\n?", "", code, flags=re.MULTILINE)
428
+ return code.strip()
429
+
430
+
431
+ def _validate_python_syntax(code: str) -> tuple[bool, list[str]]:
432
+ try:
433
+ ast.parse(code)
434
+ return True, []
435
+ except SyntaxError as e:
436
+ return False, [f"SyntaxError at line {e.lineno}: {e.msg}"]
437
+
438
+
439
+ def _check_required_imports(code: str) -> list[str]:
440
+ missing = []
441
+ for marker in _REQUIRED_IMPORT_MARKERS:
442
+ if marker not in code:
443
+ missing.append(f"MISSING_IMPORT: '{marker}' not found in generated code")
444
+ return missing
445
+
446
+
447
+ def _safe_write_path(path: Path) -> Path:
448
+ """Return path with _v2, _v3 suffix if file already exists."""
449
+ if not path.exists():
450
+ return path
451
+ stem = path.stem
452
+ suffix = path.suffix
453
+ parent = path.parent
454
+ for v in range(2, 20):
455
+ candidate = parent / f"{stem}_v{v}{suffix}"
456
+ if not candidate.exists():
457
+ return candidate
458
+ return path # overwrite as last resort
459
+
460
+
461
+ # ── Test ID Resolution ────────────────────────────────────────────────────────
462
+
463
+ def _resolve_test_id(output_dir: Path) -> str:
464
+ """Find highest TC number in project and return next one (e.g. TC011)."""
465
+ search_dirs = [
466
+ output_dir,
467
+ Path(__file__).resolve().parents[1] / "tests" / "Testscript_10",
468
+ ]
469
+ pattern = re.compile(r"test_TC(\d+)", re.IGNORECASE)
470
+ highest = 0
471
+
472
+ for d in search_dirs:
473
+ if not d.exists():
474
+ continue
475
+ for f in d.rglob("test_TC*.py"):
476
+ m = pattern.search(f.name)
477
+ if m:
478
+ highest = max(highest, int(m.group(1)))
479
+
480
+ return f"TC{highest + 1:03d}"
481
+
482
+
483
+ # ── Feature Area Inference ────────────────────────────────────────────────────
484
+
485
+ def _infer_feature_area(topic: str) -> str:
486
+ topic_lower = topic.lower()
487
+ for keyword, area in _FEATURE_KEYWORDS.items():
488
+ if keyword in topic_lower:
489
+ return area
490
+ return "IGEL Validation"
491
+
492
+
493
+ def _build_minimal_spec(ctx: PromptContext) -> str:
494
+ """Summary for Python generation when markdown was not generated (KB still in code prompt)."""
495
+ return (
496
+ f"Test ID: {ctx.test_id}\n"
497
+ f"Topic: {ctx.topic}\n"
498
+ f"Feature Area: {ctx.feature_area}\n"
499
+ f"Product: {ctx.product or 'IGEL OS 12'}\n\n"
500
+ "Generate full executable test steps from the KNOWLEDGE BASE CONTEXT in the same prompt:\n"
501
+ "- Match exact paths, commands, and UI strings from the KB; do not generalize.\n"
502
+ "- See repository guide: knowledge_base/TEST_GENERATION_GUIDE.md\n"
503
+ )
504
+
505
+
506
+ def _build_retrieval_metadata(bundle) -> dict:
507
+ """Build retrieval metadata dict for confidence scoring and feedback."""
508
+ debug = bundle.debug
509
+ return {
510
+ "chunk_count": len(bundle.results),
511
+ "kg_nodes": bundle.kg_context.node_count,
512
+ "kg_edges": bundle.kg_context.edge_count,
513
+ "reranker_applied": debug.reranker_applied,
514
+ "total_tokens_used": debug.total_tokens_used,
515
+ "precedence_attribution": bundle.precedence_attribution,
516
+ "precedence_conflicts": bundle.precedence_conflicts,
517
+ }
518
+
519
+
520
+ def _build_kg_entities(bundle) -> dict | None:
521
+ """Summarize KG context for domain scoring."""
522
+ if not bundle.kg_context.node_count:
523
+ return None
524
+ return {
525
+ "node_count": bundle.kg_context.node_count,
526
+ "edge_count": bundle.kg_context.edge_count,
527
+ "node_ids": bundle.kg_context.node_ids,
528
+ }
529
+
530
+
531
+ def _score_output(
532
+ *,
533
+ topic: str,
534
+ markdown: str,
535
+ python: str | None,
536
+ chunks: list,
537
+ retrieval_metadata: dict,
538
+ kg_entities: dict | None,
539
+ ) -> dict | None:
540
+ """Compute confidence scores when evaluation is enabled."""
541
+ if not cfg.EVAL_ENABLED:
542
+ return None
543
+ from knowledge_base.core.confidence import flatten_confidence_for_api, score_generation
544
+
545
+ raw = score_generation(
546
+ query=topic,
547
+ markdown_output=markdown,
548
+ python_output=python,
549
+ retrieved_chunks=chunks,
550
+ retrieval_metadata=retrieval_metadata,
551
+ kg_entities=kg_entities,
552
+ )
553
+ return flatten_confidence_for_api(raw)
@@ -0,0 +1,5 @@
1
+ """Neo4j graph database layer (Phase 3 shadow deployment)."""
2
+
3
+ from knowledge_base.graph_db.neo4j_client import Neo4jClient, get_neo4j_client
4
+
5
+ __all__ = ["Neo4jClient", "get_neo4j_client"]