empathy-framework 3.7.0__py3-none-any.whl → 3.7.1__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 (267) hide show
  1. coach_wizards/code_reviewer_README.md +60 -0
  2. coach_wizards/code_reviewer_wizard.py +180 -0
  3. {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/METADATA +20 -2
  4. empathy_framework-3.7.1.dist-info/RECORD +327 -0
  5. {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/top_level.txt +5 -1
  6. empathy_healthcare_plugin/monitors/__init__.py +9 -0
  7. empathy_healthcare_plugin/monitors/clinical_protocol_monitor.py +315 -0
  8. empathy_healthcare_plugin/monitors/monitoring/__init__.py +44 -0
  9. empathy_healthcare_plugin/monitors/monitoring/protocol_checker.py +300 -0
  10. empathy_healthcare_plugin/monitors/monitoring/protocol_loader.py +214 -0
  11. empathy_healthcare_plugin/monitors/monitoring/sensor_parsers.py +306 -0
  12. empathy_healthcare_plugin/monitors/monitoring/trajectory_analyzer.py +389 -0
  13. empathy_llm_toolkit/agent_factory/__init__.py +53 -0
  14. empathy_llm_toolkit/agent_factory/adapters/__init__.py +85 -0
  15. empathy_llm_toolkit/agent_factory/adapters/autogen_adapter.py +312 -0
  16. empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +454 -0
  17. empathy_llm_toolkit/agent_factory/adapters/haystack_adapter.py +298 -0
  18. empathy_llm_toolkit/agent_factory/adapters/langchain_adapter.py +362 -0
  19. empathy_llm_toolkit/agent_factory/adapters/langgraph_adapter.py +333 -0
  20. empathy_llm_toolkit/agent_factory/adapters/native.py +228 -0
  21. empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +426 -0
  22. empathy_llm_toolkit/agent_factory/base.py +305 -0
  23. empathy_llm_toolkit/agent_factory/crews/__init__.py +67 -0
  24. empathy_llm_toolkit/agent_factory/crews/code_review.py +1113 -0
  25. empathy_llm_toolkit/agent_factory/crews/health_check.py +1246 -0
  26. empathy_llm_toolkit/agent_factory/crews/refactoring.py +1128 -0
  27. empathy_llm_toolkit/agent_factory/crews/security_audit.py +1018 -0
  28. empathy_llm_toolkit/agent_factory/decorators.py +286 -0
  29. empathy_llm_toolkit/agent_factory/factory.py +558 -0
  30. empathy_llm_toolkit/agent_factory/framework.py +192 -0
  31. empathy_llm_toolkit/agent_factory/memory_integration.py +324 -0
  32. empathy_llm_toolkit/agent_factory/resilient.py +320 -0
  33. empathy_llm_toolkit/cli/__init__.py +8 -0
  34. empathy_llm_toolkit/cli/sync_claude.py +487 -0
  35. empathy_llm_toolkit/code_health.py +150 -3
  36. empathy_llm_toolkit/config/__init__.py +29 -0
  37. empathy_llm_toolkit/config/unified.py +295 -0
  38. empathy_llm_toolkit/routing/__init__.py +32 -0
  39. empathy_llm_toolkit/routing/model_router.py +362 -0
  40. empathy_llm_toolkit/security/IMPLEMENTATION_SUMMARY.md +413 -0
  41. empathy_llm_toolkit/security/PHASE2_COMPLETE.md +384 -0
  42. empathy_llm_toolkit/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
  43. empathy_llm_toolkit/security/QUICK_REFERENCE.md +316 -0
  44. empathy_llm_toolkit/security/README.md +262 -0
  45. empathy_llm_toolkit/security/__init__.py +62 -0
  46. empathy_llm_toolkit/security/audit_logger.py +929 -0
  47. empathy_llm_toolkit/security/audit_logger_example.py +152 -0
  48. empathy_llm_toolkit/security/pii_scrubber.py +640 -0
  49. empathy_llm_toolkit/security/secrets_detector.py +678 -0
  50. empathy_llm_toolkit/security/secrets_detector_example.py +304 -0
  51. empathy_llm_toolkit/security/secure_memdocs.py +1192 -0
  52. empathy_llm_toolkit/security/secure_memdocs_example.py +278 -0
  53. empathy_llm_toolkit/wizards/__init__.py +38 -0
  54. empathy_llm_toolkit/wizards/base_wizard.py +364 -0
  55. empathy_llm_toolkit/wizards/customer_support_wizard.py +190 -0
  56. empathy_llm_toolkit/wizards/healthcare_wizard.py +362 -0
  57. empathy_llm_toolkit/wizards/patient_assessment_README.md +64 -0
  58. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +193 -0
  59. empathy_llm_toolkit/wizards/technology_wizard.py +194 -0
  60. empathy_os/__init__.py +52 -52
  61. empathy_os/adaptive/__init__.py +13 -0
  62. empathy_os/adaptive/task_complexity.py +127 -0
  63. empathy_os/cli.py +118 -8
  64. empathy_os/cli_unified.py +121 -1
  65. empathy_os/config/__init__.py +63 -0
  66. empathy_os/config/xml_config.py +239 -0
  67. empathy_os/dashboard/__init__.py +15 -0
  68. empathy_os/dashboard/server.py +743 -0
  69. empathy_os/memory/__init__.py +195 -0
  70. empathy_os/memory/claude_memory.py +466 -0
  71. empathy_os/memory/config.py +224 -0
  72. empathy_os/memory/control_panel.py +1298 -0
  73. empathy_os/memory/edges.py +179 -0
  74. empathy_os/memory/graph.py +567 -0
  75. empathy_os/memory/long_term.py +1193 -0
  76. empathy_os/memory/nodes.py +179 -0
  77. empathy_os/memory/redis_bootstrap.py +540 -0
  78. empathy_os/memory/security/__init__.py +31 -0
  79. empathy_os/memory/security/audit_logger.py +930 -0
  80. empathy_os/memory/security/pii_scrubber.py +640 -0
  81. empathy_os/memory/security/secrets_detector.py +678 -0
  82. empathy_os/memory/short_term.py +2119 -0
  83. empathy_os/memory/storage/__init__.py +15 -0
  84. empathy_os/memory/summary_index.py +583 -0
  85. empathy_os/memory/unified.py +619 -0
  86. empathy_os/metrics/__init__.py +12 -0
  87. empathy_os/metrics/prompt_metrics.py +190 -0
  88. empathy_os/models/__init__.py +136 -0
  89. empathy_os/models/__main__.py +13 -0
  90. empathy_os/models/cli.py +655 -0
  91. empathy_os/models/empathy_executor.py +354 -0
  92. empathy_os/models/executor.py +252 -0
  93. empathy_os/models/fallback.py +671 -0
  94. empathy_os/models/provider_config.py +563 -0
  95. empathy_os/models/registry.py +382 -0
  96. empathy_os/models/tasks.py +302 -0
  97. empathy_os/models/telemetry.py +548 -0
  98. empathy_os/models/token_estimator.py +378 -0
  99. empathy_os/models/validation.py +274 -0
  100. empathy_os/monitoring/__init__.py +52 -0
  101. empathy_os/monitoring/alerts.py +23 -0
  102. empathy_os/monitoring/alerts_cli.py +268 -0
  103. empathy_os/monitoring/multi_backend.py +271 -0
  104. empathy_os/monitoring/otel_backend.py +363 -0
  105. empathy_os/optimization/__init__.py +19 -0
  106. empathy_os/optimization/context_optimizer.py +272 -0
  107. empathy_os/plugins/__init__.py +28 -0
  108. empathy_os/plugins/base.py +361 -0
  109. empathy_os/plugins/registry.py +268 -0
  110. empathy_os/project_index/__init__.py +30 -0
  111. empathy_os/project_index/cli.py +335 -0
  112. empathy_os/project_index/crew_integration.py +430 -0
  113. empathy_os/project_index/index.py +425 -0
  114. empathy_os/project_index/models.py +501 -0
  115. empathy_os/project_index/reports.py +473 -0
  116. empathy_os/project_index/scanner.py +538 -0
  117. empathy_os/prompts/__init__.py +61 -0
  118. empathy_os/prompts/config.py +77 -0
  119. empathy_os/prompts/context.py +177 -0
  120. empathy_os/prompts/parser.py +285 -0
  121. empathy_os/prompts/registry.py +313 -0
  122. empathy_os/prompts/templates.py +208 -0
  123. empathy_os/resilience/__init__.py +56 -0
  124. empathy_os/resilience/circuit_breaker.py +256 -0
  125. empathy_os/resilience/fallback.py +179 -0
  126. empathy_os/resilience/health.py +300 -0
  127. empathy_os/resilience/retry.py +209 -0
  128. empathy_os/resilience/timeout.py +135 -0
  129. empathy_os/routing/__init__.py +43 -0
  130. empathy_os/routing/chain_executor.py +433 -0
  131. empathy_os/routing/classifier.py +217 -0
  132. empathy_os/routing/smart_router.py +234 -0
  133. empathy_os/routing/wizard_registry.py +307 -0
  134. empathy_os/trust/__init__.py +28 -0
  135. empathy_os/trust/circuit_breaker.py +579 -0
  136. empathy_os/validation/__init__.py +19 -0
  137. empathy_os/validation/xml_validator.py +281 -0
  138. empathy_os/wizard_factory_cli.py +170 -0
  139. empathy_os/workflows/__init__.py +360 -0
  140. empathy_os/workflows/base.py +1530 -0
  141. empathy_os/workflows/bug_predict.py +962 -0
  142. empathy_os/workflows/code_review.py +960 -0
  143. empathy_os/workflows/code_review_adapters.py +310 -0
  144. empathy_os/workflows/code_review_pipeline.py +720 -0
  145. empathy_os/workflows/config.py +600 -0
  146. empathy_os/workflows/dependency_check.py +648 -0
  147. empathy_os/workflows/document_gen.py +1069 -0
  148. empathy_os/workflows/documentation_orchestrator.py +1205 -0
  149. empathy_os/workflows/health_check.py +679 -0
  150. empathy_os/workflows/keyboard_shortcuts/__init__.py +39 -0
  151. empathy_os/workflows/keyboard_shortcuts/generators.py +386 -0
  152. empathy_os/workflows/keyboard_shortcuts/parsers.py +414 -0
  153. empathy_os/workflows/keyboard_shortcuts/prompts.py +295 -0
  154. empathy_os/workflows/keyboard_shortcuts/schema.py +193 -0
  155. empathy_os/workflows/keyboard_shortcuts/workflow.py +505 -0
  156. empathy_os/workflows/manage_documentation.py +804 -0
  157. empathy_os/workflows/new_sample_workflow1.py +146 -0
  158. empathy_os/workflows/new_sample_workflow1_README.md +150 -0
  159. empathy_os/workflows/perf_audit.py +687 -0
  160. empathy_os/workflows/pr_review.py +748 -0
  161. empathy_os/workflows/progress.py +445 -0
  162. empathy_os/workflows/progress_server.py +322 -0
  163. empathy_os/workflows/refactor_plan.py +691 -0
  164. empathy_os/workflows/release_prep.py +808 -0
  165. empathy_os/workflows/research_synthesis.py +404 -0
  166. empathy_os/workflows/secure_release.py +585 -0
  167. empathy_os/workflows/security_adapters.py +297 -0
  168. empathy_os/workflows/security_audit.py +1050 -0
  169. empathy_os/workflows/step_config.py +234 -0
  170. empathy_os/workflows/test5.py +125 -0
  171. empathy_os/workflows/test5_README.md +158 -0
  172. empathy_os/workflows/test_gen.py +1855 -0
  173. empathy_os/workflows/test_lifecycle.py +526 -0
  174. empathy_os/workflows/test_maintenance.py +626 -0
  175. empathy_os/workflows/test_maintenance_cli.py +590 -0
  176. empathy_os/workflows/test_maintenance_crew.py +821 -0
  177. empathy_os/workflows/xml_enhanced_crew.py +285 -0
  178. empathy_software_plugin/cli/__init__.py +120 -0
  179. empathy_software_plugin/cli/inspect.py +362 -0
  180. empathy_software_plugin/cli.py +3 -1
  181. empathy_software_plugin/wizards/__init__.py +42 -0
  182. empathy_software_plugin/wizards/advanced_debugging_wizard.py +392 -0
  183. empathy_software_plugin/wizards/agent_orchestration_wizard.py +511 -0
  184. empathy_software_plugin/wizards/ai_collaboration_wizard.py +503 -0
  185. empathy_software_plugin/wizards/ai_context_wizard.py +441 -0
  186. empathy_software_plugin/wizards/ai_documentation_wizard.py +503 -0
  187. empathy_software_plugin/wizards/base_wizard.py +288 -0
  188. empathy_software_plugin/wizards/book_chapter_wizard.py +519 -0
  189. empathy_software_plugin/wizards/code_review_wizard.py +606 -0
  190. empathy_software_plugin/wizards/debugging/__init__.py +50 -0
  191. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +414 -0
  192. empathy_software_plugin/wizards/debugging/config_loaders.py +442 -0
  193. empathy_software_plugin/wizards/debugging/fix_applier.py +469 -0
  194. empathy_software_plugin/wizards/debugging/language_patterns.py +383 -0
  195. empathy_software_plugin/wizards/debugging/linter_parsers.py +470 -0
  196. empathy_software_plugin/wizards/debugging/verification.py +369 -0
  197. empathy_software_plugin/wizards/enhanced_testing_wizard.py +537 -0
  198. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +816 -0
  199. empathy_software_plugin/wizards/multi_model_wizard.py +501 -0
  200. empathy_software_plugin/wizards/pattern_extraction_wizard.py +422 -0
  201. empathy_software_plugin/wizards/pattern_retriever_wizard.py +400 -0
  202. empathy_software_plugin/wizards/performance/__init__.py +9 -0
  203. empathy_software_plugin/wizards/performance/bottleneck_detector.py +221 -0
  204. empathy_software_plugin/wizards/performance/profiler_parsers.py +278 -0
  205. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +429 -0
  206. empathy_software_plugin/wizards/performance_profiling_wizard.py +305 -0
  207. empathy_software_plugin/wizards/prompt_engineering_wizard.py +425 -0
  208. empathy_software_plugin/wizards/rag_pattern_wizard.py +461 -0
  209. empathy_software_plugin/wizards/security/__init__.py +32 -0
  210. empathy_software_plugin/wizards/security/exploit_analyzer.py +290 -0
  211. empathy_software_plugin/wizards/security/owasp_patterns.py +241 -0
  212. empathy_software_plugin/wizards/security/vulnerability_scanner.py +604 -0
  213. empathy_software_plugin/wizards/security_analysis_wizard.py +322 -0
  214. empathy_software_plugin/wizards/security_learning_wizard.py +740 -0
  215. empathy_software_plugin/wizards/tech_debt_wizard.py +726 -0
  216. empathy_software_plugin/wizards/testing/__init__.py +27 -0
  217. empathy_software_plugin/wizards/testing/coverage_analyzer.py +459 -0
  218. empathy_software_plugin/wizards/testing/quality_analyzer.py +531 -0
  219. empathy_software_plugin/wizards/testing/test_suggester.py +533 -0
  220. empathy_software_plugin/wizards/testing_wizard.py +274 -0
  221. hot_reload/README.md +473 -0
  222. hot_reload/__init__.py +62 -0
  223. hot_reload/config.py +84 -0
  224. hot_reload/integration.py +228 -0
  225. hot_reload/reloader.py +298 -0
  226. hot_reload/watcher.py +179 -0
  227. hot_reload/websocket.py +176 -0
  228. scaffolding/README.md +589 -0
  229. scaffolding/__init__.py +35 -0
  230. scaffolding/__main__.py +14 -0
  231. scaffolding/cli.py +240 -0
  232. test_generator/__init__.py +38 -0
  233. test_generator/__main__.py +14 -0
  234. test_generator/cli.py +226 -0
  235. test_generator/generator.py +325 -0
  236. test_generator/risk_analyzer.py +216 -0
  237. workflow_patterns/__init__.py +33 -0
  238. workflow_patterns/behavior.py +249 -0
  239. workflow_patterns/core.py +76 -0
  240. workflow_patterns/output.py +99 -0
  241. workflow_patterns/registry.py +255 -0
  242. workflow_patterns/structural.py +288 -0
  243. workflow_scaffolding/__init__.py +11 -0
  244. workflow_scaffolding/__main__.py +12 -0
  245. workflow_scaffolding/cli.py +206 -0
  246. workflow_scaffolding/generator.py +265 -0
  247. agents/code_inspection/patterns/inspection/recurring_B112.json +0 -18
  248. agents/code_inspection/patterns/inspection/recurring_F541.json +0 -16
  249. agents/code_inspection/patterns/inspection/recurring_FORMAT.json +0 -25
  250. agents/code_inspection/patterns/inspection/recurring_bug_20250822_def456.json +0 -16
  251. agents/code_inspection/patterns/inspection/recurring_bug_20250915_abc123.json +0 -16
  252. agents/code_inspection/patterns/inspection/recurring_bug_20251212_3c5b9951.json +0 -16
  253. agents/code_inspection/patterns/inspection/recurring_bug_20251212_97c0f72f.json +0 -16
  254. agents/code_inspection/patterns/inspection/recurring_bug_20251212_a0871d53.json +0 -16
  255. agents/code_inspection/patterns/inspection/recurring_bug_20251212_a9b6ec41.json +0 -16
  256. agents/code_inspection/patterns/inspection/recurring_bug_null_001.json +0 -16
  257. agents/code_inspection/patterns/inspection/recurring_builtin.json +0 -16
  258. agents/compliance_anticipation_agent.py +0 -1422
  259. agents/compliance_db.py +0 -339
  260. agents/epic_integration_wizard.py +0 -530
  261. agents/notifications.py +0 -291
  262. agents/trust_building_behaviors.py +0 -872
  263. empathy_framework-3.7.0.dist-info/RECORD +0 -105
  264. {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/WHEEL +0 -0
  265. {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/entry_points.txt +0 -0
  266. {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/licenses/LICENSE +0 -0
  267. /empathy_os/{monitoring.py → agent_monitoring.py} +0 -0
@@ -0,0 +1,177 @@
1
+ """Prompt Context
2
+
3
+ Provides dataclass for structured prompt context used by XML templates.
4
+
5
+ Copyright 2025 Smart-AI-Memory
6
+ Licensed under Fair Source License 0.9
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass, field
12
+ from typing import Any
13
+
14
+
15
+ @dataclass
16
+ class PromptContext:
17
+ """Context for rendering XML prompts.
18
+
19
+ Provides a structured way to pass information to prompt templates,
20
+ ensuring consistency across different workflows.
21
+
22
+ Attributes:
23
+ role: The role the AI should assume (e.g., "security analyst").
24
+ goal: The primary objective (e.g., "identify vulnerabilities").
25
+ instructions: Step-by-step instructions for the task.
26
+ constraints: Rules, limits, and guidelines to follow.
27
+ input_type: Type of input content ("code", "diff", "document", "question").
28
+ input_payload: The actual content to analyze or process.
29
+ extra: Additional context-specific data.
30
+
31
+ """
32
+
33
+ role: str
34
+ goal: str
35
+ instructions: list[str] = field(default_factory=list)
36
+ constraints: list[str] = field(default_factory=list)
37
+ input_type: str = "code"
38
+ input_payload: str = ""
39
+ extra: dict[str, Any] = field(default_factory=dict)
40
+
41
+ def __post_init__(self) -> None:
42
+ """Validate context after initialization."""
43
+ if not self.role:
44
+ raise ValueError("role is required")
45
+ if not self.goal:
46
+ raise ValueError("goal is required")
47
+
48
+ @classmethod
49
+ def for_security_audit(
50
+ cls,
51
+ code: str,
52
+ findings_summary: str = "",
53
+ risk_level: str = "",
54
+ **extra: Any,
55
+ ) -> PromptContext:
56
+ """Create a context for security audit workflows.
57
+
58
+ Args:
59
+ code: The code to audit.
60
+ findings_summary: Summary of detected findings.
61
+ risk_level: Current risk assessment level.
62
+ **extra: Additional context data.
63
+
64
+ """
65
+ return cls(
66
+ role="application security engineer",
67
+ goal="Identify security vulnerabilities and provide remediation guidance",
68
+ instructions=[
69
+ "Analyze the code for security vulnerabilities",
70
+ "Focus on OWASP Top 10 categories",
71
+ "Provide severity ratings (critical, high, medium, low)",
72
+ "Include specific file and line references where applicable",
73
+ "Suggest concrete remediation steps for each finding",
74
+ ],
75
+ constraints=[
76
+ "Be specific and actionable",
77
+ "Prioritize findings by severity",
78
+ "Include code examples for fixes when helpful",
79
+ ],
80
+ input_type="code",
81
+ input_payload=code,
82
+ extra={
83
+ "findings_summary": findings_summary,
84
+ "risk_level": risk_level,
85
+ **extra,
86
+ },
87
+ )
88
+
89
+ @classmethod
90
+ def for_code_review(
91
+ cls,
92
+ code_or_diff: str,
93
+ input_type: str = "code",
94
+ context: str = "",
95
+ **extra: Any,
96
+ ) -> PromptContext:
97
+ """Create a context for code review workflows.
98
+
99
+ Args:
100
+ code_or_diff: The code or diff to review.
101
+ input_type: Either "code" or "diff".
102
+ context: Additional context about the change.
103
+ **extra: Additional context data.
104
+
105
+ """
106
+ return cls(
107
+ role="senior staff engineer performing code review",
108
+ goal="Review code quality, identify issues, and suggest improvements",
109
+ instructions=[
110
+ "Identify bugs, security risks, and performance issues",
111
+ "Evaluate code structure and maintainability",
112
+ "Check for missing error handling",
113
+ "Identify tests that should be added or updated",
114
+ "Suggest improvements while respecting existing patterns",
115
+ ],
116
+ constraints=[
117
+ "Be direct and technical",
118
+ "Reference specific files and lines",
119
+ "Keep feedback actionable",
120
+ "Maximum 500 words",
121
+ ],
122
+ input_type=input_type,
123
+ input_payload=code_or_diff,
124
+ extra={
125
+ "context": context,
126
+ **extra,
127
+ },
128
+ )
129
+
130
+ @classmethod
131
+ def for_research(
132
+ cls,
133
+ question: str,
134
+ context: str = "",
135
+ **extra: Any,
136
+ ) -> PromptContext:
137
+ """Create a context for research/synthesis workflows.
138
+
139
+ Args:
140
+ question: The research question to answer.
141
+ context: Related context or codebase information.
142
+ **extra: Additional context data.
143
+
144
+ """
145
+ return cls(
146
+ role="staff engineer conducting technical research",
147
+ goal="Research and synthesize information to answer the question",
148
+ instructions=[
149
+ "Explain key concepts and tradeoffs",
150
+ "Relate the answer to the provided context if relevant",
151
+ "Propose 1-2 concrete next steps or decisions",
152
+ ],
153
+ constraints=[
154
+ "Be clear and pragmatic",
155
+ "3-5 short paragraphs",
156
+ "Focus on actionable insights",
157
+ ],
158
+ input_type="question",
159
+ input_payload=question,
160
+ extra={
161
+ "context": context,
162
+ **extra,
163
+ },
164
+ )
165
+
166
+ def with_extra(self, **kwargs: Any) -> PromptContext:
167
+ """Return a new context with additional extra fields."""
168
+ new_extra = {**self.extra, **kwargs}
169
+ return PromptContext(
170
+ role=self.role,
171
+ goal=self.goal,
172
+ instructions=self.instructions.copy(),
173
+ constraints=self.constraints.copy(),
174
+ input_type=self.input_type,
175
+ input_payload=self.input_payload,
176
+ extra=new_extra,
177
+ )
@@ -0,0 +1,285 @@
1
+ """XML Response Parser
2
+
3
+ Parses structured XML responses from LLMs for dashboard display
4
+ and workflow automation.
5
+
6
+ Copyright 2025 Smart-AI-Memory
7
+ Licensed under Fair Source License 0.9
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ import xml.etree.ElementTree as ET
14
+ from dataclasses import dataclass, field
15
+ from typing import Any
16
+
17
+ import defusedxml.ElementTree as DefusedET
18
+
19
+
20
+ @dataclass
21
+ class Finding:
22
+ """Structured finding from XML response.
23
+
24
+ Represents a single issue, vulnerability, or code review comment
25
+ extracted from an LLM response.
26
+ """
27
+
28
+ severity: str # critical, high, medium, low, info
29
+ title: str
30
+ location: str | None = None
31
+ details: str = ""
32
+ fix: str = ""
33
+
34
+ def to_dict(self) -> dict[str, Any]:
35
+ """Convert to dictionary for serialization."""
36
+ return {
37
+ "severity": self.severity,
38
+ "title": self.title,
39
+ "location": self.location,
40
+ "details": self.details,
41
+ "fix": self.fix,
42
+ }
43
+
44
+ @classmethod
45
+ def from_dict(cls, data: dict[str, Any]) -> Finding:
46
+ """Create Finding from dictionary."""
47
+ return cls(
48
+ severity=data.get("severity", "medium"),
49
+ title=data.get("title", ""),
50
+ location=data.get("location"),
51
+ details=data.get("details", ""),
52
+ fix=data.get("fix", ""),
53
+ )
54
+
55
+
56
+ @dataclass
57
+ class ParsedResponse:
58
+ """Result of parsing an XML response.
59
+
60
+ Contains extracted structured data or fallback raw text
61
+ if parsing fails.
62
+ """
63
+
64
+ success: bool
65
+ raw: str
66
+ summary: str | None = None
67
+ findings: list[Finding] = field(default_factory=list)
68
+ checklist: list[str] = field(default_factory=list)
69
+ errors: list[str] = field(default_factory=list)
70
+ extra: dict[str, Any] = field(default_factory=dict)
71
+
72
+ def to_dict(self) -> dict[str, Any]:
73
+ """Convert to dictionary for serialization."""
74
+ return {
75
+ "success": self.success,
76
+ "raw": self.raw,
77
+ "summary": self.summary,
78
+ "findings": [f.to_dict() for f in self.findings],
79
+ "checklist": self.checklist,
80
+ "errors": self.errors,
81
+ "extra": self.extra,
82
+ }
83
+
84
+ @classmethod
85
+ def from_dict(cls, data: dict[str, Any]) -> ParsedResponse:
86
+ """Create ParsedResponse from dictionary."""
87
+ findings = [Finding.from_dict(f) for f in data.get("findings", [])]
88
+ return cls(
89
+ success=data.get("success", False),
90
+ raw=data.get("raw", ""),
91
+ summary=data.get("summary"),
92
+ findings=findings,
93
+ checklist=data.get("checklist", []),
94
+ errors=data.get("errors", []),
95
+ extra=data.get("extra", {}),
96
+ )
97
+
98
+ @classmethod
99
+ def from_raw(cls, raw: str, errors: list[str] | None = None) -> ParsedResponse:
100
+ """Create a fallback ParsedResponse from raw text."""
101
+ return cls(
102
+ success=False,
103
+ raw=raw,
104
+ summary=raw[:500] if raw else None,
105
+ errors=errors or ["No XML content found"],
106
+ )
107
+
108
+
109
+ class XmlResponseParser:
110
+ """Parse LLM responses containing XML.
111
+
112
+ Extracts structured data from XML-formatted responses while
113
+ gracefully handling malformed or missing XML.
114
+ """
115
+
116
+ def __init__(self, fallback_on_error: bool = True):
117
+ """Initialize the parser.
118
+
119
+ Args:
120
+ fallback_on_error: If True, return raw text on parse failure
121
+ instead of raising an exception.
122
+
123
+ """
124
+ self.fallback_on_error = fallback_on_error
125
+
126
+ def parse(self, response: str) -> ParsedResponse:
127
+ """Parse XML response, with graceful fallback.
128
+
129
+ Args:
130
+ response: The LLM response text, potentially containing XML.
131
+
132
+ Returns:
133
+ ParsedResponse with extracted data or fallback raw text.
134
+
135
+ """
136
+ if not response:
137
+ return ParsedResponse.from_raw("", ["Empty response"])
138
+
139
+ # Extract XML from response (may be wrapped in markdown)
140
+ xml_content = self._extract_xml(response)
141
+
142
+ if not xml_content:
143
+ if self.fallback_on_error:
144
+ return ParsedResponse.from_raw(response, ["No XML content found"])
145
+ raise ValueError("No XML content in response")
146
+
147
+ try:
148
+ # Use defusedxml for safe XML parsing (prevents XXE attacks)
149
+ root = DefusedET.fromstring(xml_content)
150
+
151
+ return ParsedResponse(
152
+ success=True,
153
+ raw=response,
154
+ summary=self._extract_text(root, "summary"),
155
+ findings=self._extract_findings(root),
156
+ checklist=self._extract_checklist(root),
157
+ extra=self._extract_extra(root),
158
+ )
159
+ except ET.ParseError as e:
160
+ if self.fallback_on_error:
161
+ return ParsedResponse.from_raw(response, [f"XML parse error: {e}"])
162
+ raise
163
+
164
+ def _extract_xml(self, response: str) -> str | None:
165
+ """Extract XML content from response.
166
+
167
+ Handles various formats:
168
+ - Direct XML
169
+ - XML in markdown code blocks
170
+ - XML mixed with other text
171
+ """
172
+ # Handle markdown code blocks with xml tag
173
+ xml_block = re.search(r"```xml\s*(.*?)\s*```", response, re.DOTALL)
174
+ if xml_block:
175
+ return xml_block.group(1).strip()
176
+
177
+ # Handle generic markdown code blocks
178
+ code_block = re.search(r"```\s*(.*?)\s*```", response, re.DOTALL)
179
+ if code_block:
180
+ content = code_block.group(1).strip()
181
+ if content.startswith("<"):
182
+ return content
183
+
184
+ # Try to find <response> tags
185
+ response_match = re.search(r"<response\b[^>]*>.*?</response>", response, re.DOTALL)
186
+ if response_match:
187
+ return response_match.group(0)
188
+
189
+ # Try to find any root XML element
190
+ xml_match = re.search(r"<(\w+)\b[^>]*>.*?</\1>", response, re.DOTALL)
191
+ if xml_match:
192
+ return xml_match.group(0)
193
+
194
+ # If response itself looks like XML
195
+ stripped = response.strip()
196
+ if stripped.startswith("<") and stripped.endswith(">"):
197
+ return stripped
198
+
199
+ return None
200
+
201
+ def _extract_text(self, root: ET.Element, tag: str) -> str | None:
202
+ """Extract text content from a tag."""
203
+ element = root.find(f".//{tag}")
204
+ if element is not None and element.text:
205
+ return element.text.strip()
206
+ return None
207
+
208
+ def _extract_findings(self, root: ET.Element) -> list[Finding]:
209
+ """Extract findings from parsed XML."""
210
+ findings = []
211
+
212
+ # Look for findings in various possible locations
213
+ for finding_el in root.findall(".//finding"):
214
+ findings.append(self._parse_finding(finding_el))
215
+
216
+ # Also check for <item> inside <findings>
217
+ findings_container = root.find(".//findings")
218
+ if findings_container is not None:
219
+ for item_el in findings_container.findall("item"):
220
+ findings.append(self._parse_finding(item_el))
221
+
222
+ return findings
223
+
224
+ def _parse_finding(self, element: ET.Element) -> Finding:
225
+ """Parse a single finding element."""
226
+ title = self._extract_text(element, "title") or element.get("title") or ""
227
+ return Finding(
228
+ severity=element.get("severity") or "medium",
229
+ title=title,
230
+ location=self._extract_text(element, "location"),
231
+ details=self._extract_text(element, "details") or "",
232
+ fix=self._extract_text(element, "fix") or "",
233
+ )
234
+
235
+ def _extract_checklist(self, root: ET.Element) -> list[str]:
236
+ """Extract checklist items from parsed XML."""
237
+ items = []
238
+
239
+ # Look for remediation-checklist
240
+ checklist = root.find(".//remediation-checklist")
241
+ if checklist is not None:
242
+ for item in checklist.findall("item"):
243
+ if item.text:
244
+ items.append(item.text.strip())
245
+
246
+ # Also check for generic checklist
247
+ if not items:
248
+ checklist = root.find(".//checklist")
249
+ if checklist is not None:
250
+ for item in checklist.findall("item"):
251
+ if item.text:
252
+ items.append(item.text.strip())
253
+
254
+ return items
255
+
256
+ def _extract_extra(self, root: ET.Element) -> dict[str, Any]:
257
+ """Extract additional fields from the response."""
258
+ extra: dict[str, Any] = {}
259
+
260
+ # Extract verdict (for code review)
261
+ verdict = self._extract_text(root, "verdict")
262
+ if verdict:
263
+ extra["verdict"] = verdict
264
+
265
+ # Extract confidence (for research)
266
+ confidence = root.find(".//confidence")
267
+ if confidence is not None:
268
+ extra["confidence"] = {
269
+ "level": confidence.get("level", "medium"),
270
+ "reasoning": confidence.text.strip() if confidence.text else "",
271
+ }
272
+
273
+ # Extract key insights (for research)
274
+ insights = root.find(".//key-insights")
275
+ if insights is not None:
276
+ extra["key_insights"] = [i.text.strip() for i in insights.findall("insight") if i.text]
277
+
278
+ # Extract suggestions (for code review)
279
+ suggestions = root.find(".//suggestions")
280
+ if suggestions is not None:
281
+ extra["suggestions"] = [
282
+ s.text.strip() for s in suggestions.findall("suggestion") if s.text
283
+ ]
284
+
285
+ return extra