empathy-framework 4.6.2__py3-none-any.whl → 4.6.3__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.
- {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/METADATA +1 -1
- {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/RECORD +53 -20
- {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/WHEEL +1 -1
- empathy_os/__init__.py +1 -1
- empathy_os/cli.py +361 -32
- empathy_os/config/xml_config.py +8 -3
- empathy_os/core.py +37 -4
- empathy_os/leverage_points.py +2 -1
- empathy_os/memory/short_term.py +45 -1
- empathy_os/meta_workflows/agent_creator 2.py +254 -0
- empathy_os/meta_workflows/builtin_templates 2.py +567 -0
- empathy_os/meta_workflows/cli_meta_workflows 2.py +1551 -0
- empathy_os/meta_workflows/form_engine 2.py +304 -0
- empathy_os/meta_workflows/intent_detector 2.py +298 -0
- empathy_os/meta_workflows/pattern_learner 2.py +754 -0
- empathy_os/meta_workflows/session_context 2.py +398 -0
- empathy_os/meta_workflows/template_registry 2.py +229 -0
- empathy_os/meta_workflows/workflow 2.py +980 -0
- empathy_os/models/token_estimator.py +16 -9
- empathy_os/models/validation.py +7 -1
- empathy_os/orchestration/pattern_learner 2.py +699 -0
- empathy_os/orchestration/real_tools 2.py +938 -0
- empathy_os/orchestration/real_tools.py +4 -2
- empathy_os/socratic/__init__ 2.py +273 -0
- empathy_os/socratic/ab_testing 2.py +969 -0
- empathy_os/socratic/blueprint 2.py +532 -0
- empathy_os/socratic/cli 2.py +689 -0
- empathy_os/socratic/collaboration 2.py +1112 -0
- empathy_os/socratic/domain_templates 2.py +916 -0
- empathy_os/socratic/embeddings 2.py +734 -0
- empathy_os/socratic/engine 2.py +729 -0
- empathy_os/socratic/explainer 2.py +663 -0
- empathy_os/socratic/feedback 2.py +767 -0
- empathy_os/socratic/forms 2.py +624 -0
- empathy_os/socratic/generator 2.py +716 -0
- empathy_os/socratic/llm_analyzer 2.py +635 -0
- empathy_os/socratic/mcp_server 2.py +751 -0
- empathy_os/socratic/session 2.py +306 -0
- empathy_os/socratic/storage 2.py +635 -0
- empathy_os/socratic/storage.py +2 -1
- empathy_os/socratic/success 2.py +719 -0
- empathy_os/socratic/visual_editor 2.py +812 -0
- empathy_os/socratic/web_ui 2.py +925 -0
- empathy_os/tier_recommender.py +5 -2
- empathy_os/workflow_commands.py +11 -6
- empathy_os/workflows/base.py +1 -1
- empathy_os/workflows/batch_processing 2.py +310 -0
- empathy_os/workflows/release_prep_crew 2.py +968 -0
- empathy_os/workflows/test_coverage_boost_crew 2.py +848 -0
- empathy_os/workflows/test_maintenance.py +3 -2
- {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/entry_points.txt +0 -0
- {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/licenses/LICENSE +0 -0
- {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""Socratic Session Management
|
|
2
|
+
|
|
3
|
+
Tracks the state of a Socratic questioning session as it progresses
|
|
4
|
+
from initial goal capture through requirements refinement to generation.
|
|
5
|
+
|
|
6
|
+
Copyright 2026 Smart-AI-Memory
|
|
7
|
+
Licensed under Fair Source License 0.9
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import uuid
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from enum import Enum
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SessionState(Enum):
|
|
20
|
+
"""State machine for Socratic sessions."""
|
|
21
|
+
|
|
22
|
+
# Initial state - waiting for goal
|
|
23
|
+
AWAITING_GOAL = "awaiting_goal"
|
|
24
|
+
|
|
25
|
+
# Goal received, analyzing for questions
|
|
26
|
+
ANALYZING_GOAL = "analyzing_goal"
|
|
27
|
+
|
|
28
|
+
# Questions generated, waiting for answers
|
|
29
|
+
AWAITING_ANSWERS = "awaiting_answers"
|
|
30
|
+
|
|
31
|
+
# Answers received, determining if more questions needed
|
|
32
|
+
PROCESSING_ANSWERS = "processing_answers"
|
|
33
|
+
|
|
34
|
+
# Requirements sufficient, ready to generate
|
|
35
|
+
READY_TO_GENERATE = "ready_to_generate"
|
|
36
|
+
|
|
37
|
+
# Generation in progress
|
|
38
|
+
GENERATING = "generating"
|
|
39
|
+
|
|
40
|
+
# Workflow generated successfully
|
|
41
|
+
COMPLETED = "completed"
|
|
42
|
+
|
|
43
|
+
# User cancelled or session expired
|
|
44
|
+
CANCELLED = "cancelled"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class GoalAnalysis:
|
|
49
|
+
"""Analysis of the user's stated goal.
|
|
50
|
+
|
|
51
|
+
Captures what we understand and what needs clarification.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Original goal statement
|
|
55
|
+
raw_goal: str
|
|
56
|
+
|
|
57
|
+
# Extracted intent (what they want to achieve)
|
|
58
|
+
intent: str
|
|
59
|
+
|
|
60
|
+
# Domain detected (e.g., "code_review", "testing", "documentation")
|
|
61
|
+
domain: str
|
|
62
|
+
|
|
63
|
+
# Confidence in our understanding (0-1)
|
|
64
|
+
confidence: float
|
|
65
|
+
|
|
66
|
+
# Identified ambiguities that need clarification
|
|
67
|
+
ambiguities: list[str] = field(default_factory=list)
|
|
68
|
+
|
|
69
|
+
# Assumptions we're making (need validation)
|
|
70
|
+
assumptions: list[str] = field(default_factory=list)
|
|
71
|
+
|
|
72
|
+
# Constraints mentioned or implied
|
|
73
|
+
constraints: list[str] = field(default_factory=list)
|
|
74
|
+
|
|
75
|
+
# Keywords extracted for matching
|
|
76
|
+
keywords: list[str] = field(default_factory=list)
|
|
77
|
+
|
|
78
|
+
def needs_clarification(self) -> bool:
|
|
79
|
+
"""Check if goal needs more clarification."""
|
|
80
|
+
return self.confidence < 0.8 or len(self.ambiguities) > 0
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class RequirementSet:
|
|
85
|
+
"""Accumulated requirements from Socratic questioning."""
|
|
86
|
+
|
|
87
|
+
# Core requirements (must have)
|
|
88
|
+
must_have: list[str] = field(default_factory=list)
|
|
89
|
+
|
|
90
|
+
# Nice to have requirements
|
|
91
|
+
should_have: list[str] = field(default_factory=list)
|
|
92
|
+
|
|
93
|
+
# Explicitly excluded requirements
|
|
94
|
+
must_not_have: list[str] = field(default_factory=list)
|
|
95
|
+
|
|
96
|
+
# Technical constraints (languages, frameworks, etc.)
|
|
97
|
+
technical_constraints: dict[str, Any] = field(default_factory=dict)
|
|
98
|
+
|
|
99
|
+
# Quality attributes (performance, security, etc.)
|
|
100
|
+
quality_attributes: dict[str, float] = field(default_factory=dict)
|
|
101
|
+
|
|
102
|
+
# Domain-specific requirements
|
|
103
|
+
domain_specific: dict[str, Any] = field(default_factory=dict)
|
|
104
|
+
|
|
105
|
+
# User preferences
|
|
106
|
+
preferences: dict[str, Any] = field(default_factory=dict)
|
|
107
|
+
|
|
108
|
+
def completeness_score(self) -> float:
|
|
109
|
+
"""Calculate how complete the requirements are (0-1)."""
|
|
110
|
+
scores = []
|
|
111
|
+
|
|
112
|
+
# Must have at least one core requirement
|
|
113
|
+
if self.must_have:
|
|
114
|
+
scores.append(1.0)
|
|
115
|
+
else:
|
|
116
|
+
scores.append(0.0)
|
|
117
|
+
|
|
118
|
+
# Technical constraints help
|
|
119
|
+
if self.technical_constraints:
|
|
120
|
+
scores.append(min(len(self.technical_constraints) / 3, 1.0))
|
|
121
|
+
else:
|
|
122
|
+
scores.append(0.3) # Can work without, but less optimal
|
|
123
|
+
|
|
124
|
+
# Quality attributes help prioritization
|
|
125
|
+
if self.quality_attributes:
|
|
126
|
+
scores.append(min(len(self.quality_attributes) / 2, 1.0))
|
|
127
|
+
else:
|
|
128
|
+
scores.append(0.5) # Can use defaults
|
|
129
|
+
|
|
130
|
+
return sum(scores) / len(scores) if scores else 0.0
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass
|
|
134
|
+
class SocraticSession:
|
|
135
|
+
"""A Socratic questioning session.
|
|
136
|
+
|
|
137
|
+
Tracks the full state of a session from goal to generated workflow.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
# Unique session identifier
|
|
141
|
+
session_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
142
|
+
|
|
143
|
+
# Current state in the state machine
|
|
144
|
+
state: SessionState = SessionState.AWAITING_GOAL
|
|
145
|
+
|
|
146
|
+
# When the session started
|
|
147
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
148
|
+
|
|
149
|
+
# Last activity timestamp
|
|
150
|
+
updated_at: datetime = field(default_factory=datetime.now)
|
|
151
|
+
|
|
152
|
+
# The user's original goal statement
|
|
153
|
+
goal: str = ""
|
|
154
|
+
|
|
155
|
+
# Analysis of the goal
|
|
156
|
+
goal_analysis: GoalAnalysis | None = None
|
|
157
|
+
|
|
158
|
+
# Accumulated requirements
|
|
159
|
+
requirements: RequirementSet = field(default_factory=RequirementSet)
|
|
160
|
+
|
|
161
|
+
# History of question rounds
|
|
162
|
+
question_rounds: list[dict[str, Any]] = field(default_factory=list)
|
|
163
|
+
|
|
164
|
+
# Current round number
|
|
165
|
+
current_round: int = 0
|
|
166
|
+
|
|
167
|
+
# Maximum rounds before forcing generation
|
|
168
|
+
max_rounds: int = 5
|
|
169
|
+
|
|
170
|
+
# Generated blueprint (when ready)
|
|
171
|
+
blueprint: Any = None # WorkflowBlueprint, imported lazily
|
|
172
|
+
|
|
173
|
+
# Generated workflow (when complete)
|
|
174
|
+
workflow: Any = None # GeneratedWorkflow, imported lazily
|
|
175
|
+
|
|
176
|
+
# Error message if something went wrong
|
|
177
|
+
error: str | None = None
|
|
178
|
+
|
|
179
|
+
# Metadata for extensibility
|
|
180
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
181
|
+
|
|
182
|
+
def touch(self) -> None:
|
|
183
|
+
"""Update the last activity timestamp."""
|
|
184
|
+
self.updated_at = datetime.now()
|
|
185
|
+
|
|
186
|
+
def is_active(self) -> bool:
|
|
187
|
+
"""Check if session is still active."""
|
|
188
|
+
return self.state not in (SessionState.COMPLETED, SessionState.CANCELLED)
|
|
189
|
+
|
|
190
|
+
def can_generate(self) -> bool:
|
|
191
|
+
"""Check if we have enough information to generate."""
|
|
192
|
+
if self.goal_analysis is None:
|
|
193
|
+
return False
|
|
194
|
+
|
|
195
|
+
# Either high confidence or we've done enough rounds
|
|
196
|
+
if self.goal_analysis.confidence >= 0.8:
|
|
197
|
+
return True
|
|
198
|
+
|
|
199
|
+
if self.current_round >= self.max_rounds:
|
|
200
|
+
return True
|
|
201
|
+
|
|
202
|
+
if self.requirements.completeness_score() >= 0.7:
|
|
203
|
+
return True
|
|
204
|
+
|
|
205
|
+
return False
|
|
206
|
+
|
|
207
|
+
def add_question_round(
|
|
208
|
+
self,
|
|
209
|
+
questions: list[dict[str, Any]],
|
|
210
|
+
answers: dict[str, Any],
|
|
211
|
+
) -> None:
|
|
212
|
+
"""Record a round of questions and answers."""
|
|
213
|
+
self.question_rounds.append({
|
|
214
|
+
"round": self.current_round,
|
|
215
|
+
"questions": questions,
|
|
216
|
+
"answers": answers,
|
|
217
|
+
"timestamp": datetime.now().isoformat(),
|
|
218
|
+
})
|
|
219
|
+
self.current_round += 1
|
|
220
|
+
self.touch()
|
|
221
|
+
|
|
222
|
+
def get_context_summary(self) -> dict[str, Any]:
|
|
223
|
+
"""Get a summary of accumulated context for display."""
|
|
224
|
+
return {
|
|
225
|
+
"session_id": self.session_id,
|
|
226
|
+
"state": self.state.value,
|
|
227
|
+
"goal": self.goal,
|
|
228
|
+
"rounds_completed": self.current_round,
|
|
229
|
+
"requirements_count": len(self.requirements.must_have),
|
|
230
|
+
"confidence": self.goal_analysis.confidence if self.goal_analysis else 0.0,
|
|
231
|
+
"ready_to_generate": self.can_generate(),
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
def to_dict(self) -> dict[str, Any]:
|
|
235
|
+
"""Serialize session to dictionary."""
|
|
236
|
+
return {
|
|
237
|
+
"session_id": self.session_id,
|
|
238
|
+
"state": self.state.value,
|
|
239
|
+
"created_at": self.created_at.isoformat(),
|
|
240
|
+
"updated_at": self.updated_at.isoformat(),
|
|
241
|
+
"goal": self.goal,
|
|
242
|
+
"goal_analysis": {
|
|
243
|
+
"intent": self.goal_analysis.intent,
|
|
244
|
+
"domain": self.goal_analysis.domain,
|
|
245
|
+
"confidence": self.goal_analysis.confidence,
|
|
246
|
+
"ambiguities": self.goal_analysis.ambiguities,
|
|
247
|
+
"assumptions": self.goal_analysis.assumptions,
|
|
248
|
+
} if self.goal_analysis else None,
|
|
249
|
+
"requirements": {
|
|
250
|
+
"must_have": self.requirements.must_have,
|
|
251
|
+
"should_have": self.requirements.should_have,
|
|
252
|
+
"must_not_have": self.requirements.must_not_have,
|
|
253
|
+
"technical_constraints": self.requirements.technical_constraints,
|
|
254
|
+
"quality_attributes": self.requirements.quality_attributes,
|
|
255
|
+
},
|
|
256
|
+
"question_rounds": self.question_rounds,
|
|
257
|
+
"current_round": self.current_round,
|
|
258
|
+
"error": self.error,
|
|
259
|
+
"metadata": self.metadata,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
@classmethod
|
|
263
|
+
def from_dict(cls, data: dict[str, Any]) -> SocraticSession:
|
|
264
|
+
"""Deserialize session from dictionary."""
|
|
265
|
+
session = cls(
|
|
266
|
+
session_id=data.get("session_id", str(uuid.uuid4())),
|
|
267
|
+
state=SessionState(data.get("state", "awaiting_goal")),
|
|
268
|
+
goal=data.get("goal", ""),
|
|
269
|
+
current_round=data.get("current_round", 0),
|
|
270
|
+
error=data.get("error"),
|
|
271
|
+
metadata=data.get("metadata", {}),
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# Parse timestamps
|
|
275
|
+
if "created_at" in data:
|
|
276
|
+
session.created_at = datetime.fromisoformat(data["created_at"])
|
|
277
|
+
if "updated_at" in data:
|
|
278
|
+
session.updated_at = datetime.fromisoformat(data["updated_at"])
|
|
279
|
+
|
|
280
|
+
# Parse goal analysis
|
|
281
|
+
if data.get("goal_analysis"):
|
|
282
|
+
ga = data["goal_analysis"]
|
|
283
|
+
session.goal_analysis = GoalAnalysis(
|
|
284
|
+
raw_goal=session.goal,
|
|
285
|
+
intent=ga.get("intent", ""),
|
|
286
|
+
domain=ga.get("domain", "general"),
|
|
287
|
+
confidence=ga.get("confidence", 0.0),
|
|
288
|
+
ambiguities=ga.get("ambiguities", []),
|
|
289
|
+
assumptions=ga.get("assumptions", []),
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Parse requirements
|
|
293
|
+
if data.get("requirements"):
|
|
294
|
+
req = data["requirements"]
|
|
295
|
+
session.requirements = RequirementSet(
|
|
296
|
+
must_have=req.get("must_have", []),
|
|
297
|
+
should_have=req.get("should_have", []),
|
|
298
|
+
must_not_have=req.get("must_not_have", []),
|
|
299
|
+
technical_constraints=req.get("technical_constraints", {}),
|
|
300
|
+
quality_attributes=req.get("quality_attributes", {}),
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Parse question rounds
|
|
304
|
+
session.question_rounds = data.get("question_rounds", [])
|
|
305
|
+
|
|
306
|
+
return session
|