algomath-extract 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.
- package/README.md +260 -0
- package/bin/algo-extract.js +143 -0
- package/bin/algo-generate.js +102 -0
- package/bin/algo-help.js +136 -0
- package/bin/algo-list.js +56 -0
- package/bin/algo-run.js +141 -0
- package/bin/algo-status.js +88 -0
- package/bin/algo-verify.js +189 -0
- package/bin/install.js +349 -0
- package/package.json +57 -0
- package/requirements.txt +20 -0
- package/src/__pycache__/intent.cpython-313.pyc +0 -0
- package/src/cli/__pycache__/commands.cpython-313.pyc +0 -0
- package/src/cli/cli_entry.py +106 -0
- package/src/cli/commands.py +339 -0
- package/src/execution/__init__.py +74 -0
- package/src/execution/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/execution/__pycache__/display.cpython-313.pyc +0 -0
- package/src/execution/__pycache__/errors.cpython-313.pyc +0 -0
- package/src/execution/__pycache__/executor.cpython-313.pyc +0 -0
- package/src/execution/__pycache__/sandbox.cpython-313.pyc +0 -0
- package/src/execution/display.py +261 -0
- package/src/execution/errors.py +158 -0
- package/src/execution/executor.py +253 -0
- package/src/execution/sandbox.py +333 -0
- package/src/extraction/__init__.py +102 -0
- package/src/extraction/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/extraction/__pycache__/boundaries.cpython-313.pyc +0 -0
- package/src/extraction/__pycache__/errors.cpython-313.pyc +0 -0
- package/src/extraction/__pycache__/llm_extraction.cpython-313.pyc +0 -0
- package/src/extraction/__pycache__/notation.cpython-313.pyc +0 -0
- package/src/extraction/__pycache__/parser.cpython-313.pyc +0 -0
- package/src/extraction/__pycache__/pdf_processor.cpython-313.pyc +0 -0
- package/src/extraction/__pycache__/prompts.cpython-313.pyc +0 -0
- package/src/extraction/__pycache__/review.cpython-313.pyc +0 -0
- package/src/extraction/__pycache__/schema.cpython-313.pyc +0 -0
- package/src/extraction/__pycache__/validation.cpython-313.pyc +0 -0
- package/src/extraction/boundaries.py +281 -0
- package/src/extraction/errors.py +156 -0
- package/src/extraction/llm_extraction.py +225 -0
- package/src/extraction/notation.py +240 -0
- package/src/extraction/parser.py +402 -0
- package/src/extraction/pdf_processor.py +281 -0
- package/src/extraction/prompts.py +90 -0
- package/src/extraction/review.py +298 -0
- package/src/extraction/schema.py +173 -0
- package/src/extraction/validation.py +202 -0
- package/src/generation/__init__.py +79 -0
- package/src/generation/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/generation/__pycache__/code_generator.cpython-313.pyc +0 -0
- package/src/generation/__pycache__/errors.cpython-313.pyc +0 -0
- package/src/generation/__pycache__/hybrid.cpython-313.pyc +0 -0
- package/src/generation/__pycache__/llm_generator.cpython-313.pyc +0 -0
- package/src/generation/__pycache__/persistence.cpython-313.pyc +0 -0
- package/src/generation/__pycache__/prompts.cpython-313.pyc +0 -0
- package/src/generation/__pycache__/review.cpython-313.pyc +0 -0
- package/src/generation/__pycache__/templates.cpython-313.pyc +0 -0
- package/src/generation/__pycache__/types.cpython-313.pyc +0 -0
- package/src/generation/__pycache__/validation.cpython-313.pyc +0 -0
- package/src/generation/code_generator.py +375 -0
- package/src/generation/errors.py +84 -0
- package/src/generation/hybrid.py +210 -0
- package/src/generation/llm_generator.py +223 -0
- package/src/generation/persistence.py +221 -0
- package/src/generation/prompts.py +202 -0
- package/src/generation/review.py +254 -0
- package/src/generation/templates.py +208 -0
- package/src/generation/types.py +196 -0
- package/src/generation/validation.py +278 -0
- package/src/intent.py +323 -0
- package/src/verification/__init__.py +63 -0
- package/src/verification/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/verification/__pycache__/checker.cpython-313.pyc +0 -0
- package/src/verification/__pycache__/comparison.cpython-313.pyc +0 -0
- package/src/verification/__pycache__/explainer.cpython-313.pyc +0 -0
- package/src/verification/__pycache__/static_analysis.cpython-313.pyc +0 -0
- package/src/verification/checker.py +220 -0
- package/src/verification/comparison.py +492 -0
- package/src/verification/explainer.py +414 -0
- package/src/verification/static_analysis.py +540 -0
- package/src/workflows/__init__.py +21 -0
- package/src/workflows/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/workflows/__pycache__/extract.cpython-313.pyc +0 -0
- package/src/workflows/__pycache__/generate.cpython-313.pyc +0 -0
- package/src/workflows/__pycache__/run.cpython-313.pyc +0 -0
- package/src/workflows/__pycache__/verify.cpython-313.pyc +0 -0
- package/src/workflows/extract.py +181 -0
- package/src/workflows/generate.py +155 -0
- package/src/workflows/run.py +187 -0
- package/src/workflows/verify.py +334 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""Code validation utilities.
|
|
2
|
+
|
|
3
|
+
This module provides validation for generated Python code.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import ast
|
|
7
|
+
import importlib.util
|
|
8
|
+
import re
|
|
9
|
+
import subprocess
|
|
10
|
+
import tempfile
|
|
11
|
+
import os
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
from src.generation.errors import GenerationError, SyntaxGenerationError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class ValidationResult:
|
|
20
|
+
"""
|
|
21
|
+
Represents validation results.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
is_valid: Whether validation passed
|
|
25
|
+
errors: List of error dictionaries
|
|
26
|
+
warnings: List of warning messages
|
|
27
|
+
"""
|
|
28
|
+
is_valid: bool
|
|
29
|
+
errors: List[Dict[str, Any]] = field(default_factory=list)
|
|
30
|
+
warnings: List[str] = field(default_factory=list)
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def has_errors(self) -> bool:
|
|
34
|
+
return len(self.errors) > 0
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def error_count(self) -> int:
|
|
38
|
+
return len(self.errors)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CodeValidator:
|
|
42
|
+
"""
|
|
43
|
+
Validate generated Python code.
|
|
44
|
+
|
|
45
|
+
Provides syntax, import, and optional runtime validation.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, check_runtime: bool = False):
|
|
49
|
+
"""
|
|
50
|
+
Initialize validator.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
check_runtime: Whether to run runtime checks
|
|
54
|
+
"""
|
|
55
|
+
self.check_runtime = check_runtime
|
|
56
|
+
self.errors = []
|
|
57
|
+
|
|
58
|
+
def validate(self, code: str) -> ValidationResult:
|
|
59
|
+
"""
|
|
60
|
+
Run all validation checks.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
code: Python code to validate
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
ValidationResult with errors and warnings
|
|
67
|
+
"""
|
|
68
|
+
results = []
|
|
69
|
+
|
|
70
|
+
# Syntax validation
|
|
71
|
+
results.append(self.validate_syntax(code))
|
|
72
|
+
|
|
73
|
+
# Import validation
|
|
74
|
+
results.append(self.validate_imports(code))
|
|
75
|
+
|
|
76
|
+
# Runtime check if enabled
|
|
77
|
+
if self.check_runtime:
|
|
78
|
+
results.append(self.validate_runtime(code))
|
|
79
|
+
|
|
80
|
+
# Merge results
|
|
81
|
+
all_errors = []
|
|
82
|
+
all_warnings = []
|
|
83
|
+
for r in results:
|
|
84
|
+
all_errors.extend(r.errors)
|
|
85
|
+
all_warnings.extend(r.warnings)
|
|
86
|
+
|
|
87
|
+
return ValidationResult(
|
|
88
|
+
is_valid=len(all_errors) == 0,
|
|
89
|
+
errors=all_errors,
|
|
90
|
+
warnings=all_warnings
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def validate_syntax(self, code: str) -> ValidationResult:
|
|
94
|
+
"""
|
|
95
|
+
Validate Python syntax using ast module.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
code: Python code to validate
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
ValidationResult
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
ast.parse(code)
|
|
105
|
+
return ValidationResult(is_valid=True, errors=[], warnings=[])
|
|
106
|
+
except SyntaxError as e:
|
|
107
|
+
error = {
|
|
108
|
+
'message': f"Syntax error: {e.msg}",
|
|
109
|
+
'line': e.lineno,
|
|
110
|
+
'type': 'syntax',
|
|
111
|
+
'text': e.text
|
|
112
|
+
}
|
|
113
|
+
return ValidationResult(
|
|
114
|
+
is_valid=False,
|
|
115
|
+
errors=[error],
|
|
116
|
+
warnings=[]
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def validate_imports(self, code: str) -> ValidationResult:
|
|
120
|
+
"""
|
|
121
|
+
Verify imports can be resolved.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
code: Python code to validate
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
ValidationResult
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
tree = ast.parse(code)
|
|
131
|
+
except SyntaxError:
|
|
132
|
+
return ValidationResult(is_valid=False, errors=[], warnings=[])
|
|
133
|
+
|
|
134
|
+
errors = []
|
|
135
|
+
warnings = []
|
|
136
|
+
|
|
137
|
+
for node in ast.walk(tree):
|
|
138
|
+
if isinstance(node, ast.Import):
|
|
139
|
+
for alias in node.names:
|
|
140
|
+
if not self._can_import(alias.name.split('.')[0]):
|
|
141
|
+
errors.append({
|
|
142
|
+
'message': f"Cannot import module: {alias.name}",
|
|
143
|
+
'line': node.lineno,
|
|
144
|
+
'type': 'import'
|
|
145
|
+
})
|
|
146
|
+
elif isinstance(node, ast.ImportFrom):
|
|
147
|
+
if node.module:
|
|
148
|
+
if not self._can_import(node.module.split('.')[0]):
|
|
149
|
+
errors.append({
|
|
150
|
+
'message': f"Cannot import module: {node.module}",
|
|
151
|
+
'line': node.lineno,
|
|
152
|
+
'type': 'import'
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
return ValidationResult(
|
|
156
|
+
is_valid=len(errors) == 0,
|
|
157
|
+
errors=errors,
|
|
158
|
+
warnings=warnings
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def _can_import(self, module_name: str) -> bool:
|
|
162
|
+
"""
|
|
163
|
+
Check if module can be imported.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
module_name: Module name to check
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
True if module can be imported
|
|
170
|
+
"""
|
|
171
|
+
# Built-in modules that don't need checking
|
|
172
|
+
built_ins = {'builtins', 'types', 'typing', 'abc', 'collections'}
|
|
173
|
+
if module_name in built_ins:
|
|
174
|
+
return True
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
return importlib.util.find_spec(module_name) is not None
|
|
178
|
+
except (ImportError, ModuleNotFoundError):
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
def validate_runtime(self, code: str, timeout: int = 5) -> ValidationResult:
|
|
182
|
+
"""
|
|
183
|
+
Execute code in sandbox to check for NameError.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
code: Python code to validate
|
|
187
|
+
timeout: Timeout in seconds
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
ValidationResult
|
|
191
|
+
"""
|
|
192
|
+
# Create temp file
|
|
193
|
+
with tempfile.NamedTemporaryFile(
|
|
194
|
+
mode='w',
|
|
195
|
+
suffix='.py',
|
|
196
|
+
delete=False
|
|
197
|
+
) as f:
|
|
198
|
+
f.write(code)
|
|
199
|
+
temp_path = f.name
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
# Run with timeout
|
|
203
|
+
result = subprocess.run(
|
|
204
|
+
['python', temp_path],
|
|
205
|
+
capture_output=True,
|
|
206
|
+
text=True,
|
|
207
|
+
timeout=timeout
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
errors = []
|
|
211
|
+
if result.returncode != 0:
|
|
212
|
+
if 'NameError' in result.stderr:
|
|
213
|
+
errors.append({
|
|
214
|
+
'message': f"NameError: {result.stderr}",
|
|
215
|
+
'line': self._extract_line_from_traceback(result.stderr),
|
|
216
|
+
'type': 'runtime'
|
|
217
|
+
})
|
|
218
|
+
elif 'SyntaxError' not in result.stderr:
|
|
219
|
+
errors.append({
|
|
220
|
+
'message': f"Runtime error: {result.stderr}",
|
|
221
|
+
'line': None,
|
|
222
|
+
'type': 'runtime'
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
return ValidationResult(
|
|
226
|
+
is_valid=len(errors) == 0,
|
|
227
|
+
errors=errors,
|
|
228
|
+
warnings=[]
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
except subprocess.TimeoutExpired:
|
|
232
|
+
return ValidationResult(
|
|
233
|
+
is_valid=False,
|
|
234
|
+
errors=[{
|
|
235
|
+
'message': f'Runtime validation timed out after {timeout}s',
|
|
236
|
+
'line': None,
|
|
237
|
+
'type': 'timeout'
|
|
238
|
+
}],
|
|
239
|
+
warnings=[]
|
|
240
|
+
)
|
|
241
|
+
finally:
|
|
242
|
+
os.unlink(temp_path)
|
|
243
|
+
|
|
244
|
+
def _extract_line_from_traceback(self, stderr: str) -> Optional[int]:
|
|
245
|
+
"""
|
|
246
|
+
Extract line number from traceback.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
stderr: Stderr text from subprocess
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Line number or None
|
|
253
|
+
"""
|
|
254
|
+
match = re.search(r'line (\d+)', stderr)
|
|
255
|
+
if match:
|
|
256
|
+
return int(match.group(1))
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def validate_generated(generated_code: Any) -> ValidationResult:
|
|
261
|
+
"""
|
|
262
|
+
Validate a GeneratedCode object.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
generated_code: GeneratedCode object to validate
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
ValidationResult
|
|
269
|
+
"""
|
|
270
|
+
validator = CodeValidator()
|
|
271
|
+
return validator.validate(generated_code.source)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
__all__ = [
|
|
275
|
+
'ValidationResult',
|
|
276
|
+
'CodeValidator',
|
|
277
|
+
'validate_generated',
|
|
278
|
+
]
|
package/src/intent.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Intent detection and routing for AlgoMath.
|
|
3
|
+
|
|
4
|
+
This module provides natural language intent detection and workflow routing,
|
|
5
|
+
enabling users to interact with the system using conversational language.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from enum import Enum, auto
|
|
9
|
+
from typing import Tuple, List, Dict, Optional
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class IntentType(Enum):
|
|
14
|
+
"""Enumeration of supported user intents."""
|
|
15
|
+
EXTRACT = auto() # Extract algorithm from text
|
|
16
|
+
GENERATE = auto() # Generate code from steps
|
|
17
|
+
RUN = auto() # Execute generated code
|
|
18
|
+
VERIFY = auto() # Verify execution results
|
|
19
|
+
STATUS = auto() # Check current state
|
|
20
|
+
LIST = auto() # List algorithms
|
|
21
|
+
HELP = auto() # Show help
|
|
22
|
+
UNKNOWN = auto() # Could not determine intent
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Keyword mappings for intent detection
|
|
26
|
+
INTENT_KEYWORDS: Dict[IntentType, List[str]] = {
|
|
27
|
+
IntentType.EXTRACT: [
|
|
28
|
+
"extract", "parse", "get steps", "analyze text", "pull out",
|
|
29
|
+
"find algorithm", "identify", "get algorithm", "from text",
|
|
30
|
+
"steps from", "parse this", "analyze", "read", "interpret"
|
|
31
|
+
],
|
|
32
|
+
IntentType.GENERATE: [
|
|
33
|
+
"generate", "create code", "write python", "implement",
|
|
34
|
+
"code this", "make code", "turn into code", "convert to code",
|
|
35
|
+
"python code", "write code", "produce code", "build code"
|
|
36
|
+
],
|
|
37
|
+
IntentType.RUN: [
|
|
38
|
+
"run", "execute", "execute code", "test", "run code",
|
|
39
|
+
"run it", "execute it", "try it", "run the", "execute the",
|
|
40
|
+
"run algorithm", "execute algorithm", "test code", "try code"
|
|
41
|
+
],
|
|
42
|
+
IntentType.VERIFY: [
|
|
43
|
+
"verify", "check", "validate", "explain", "confirm",
|
|
44
|
+
"is this correct", "does this work", "validate results",
|
|
45
|
+
"check output", "verify results", "explain why", "how does this work"
|
|
46
|
+
],
|
|
47
|
+
IntentType.STATUS: [
|
|
48
|
+
"status", "state", "progress", "where am i", "what's next",
|
|
49
|
+
"current state", "show status", "check status", "where are we",
|
|
50
|
+
"what step", "what phase", "how far", "progress so far"
|
|
51
|
+
],
|
|
52
|
+
IntentType.LIST: [
|
|
53
|
+
"list", "show algorithms", "what do i have", "saved algorithms",
|
|
54
|
+
"my algorithms", "view algorithms", "list algorithms", "show all",
|
|
55
|
+
"what algorithms", "saved", "previous algorithms"
|
|
56
|
+
],
|
|
57
|
+
IntentType.HELP: [
|
|
58
|
+
"help", "how to", "what can you do", "commands", "how do i",
|
|
59
|
+
"show help", "list commands", "usage", "instructions",
|
|
60
|
+
"what commands", "available commands", "command list"
|
|
61
|
+
],
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _calculate_confidence(user_input: str, keywords: List[str]) -> float:
|
|
66
|
+
"""
|
|
67
|
+
Calculate confidence score based on keyword matches.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
user_input: The user's input text (lowercased)
|
|
71
|
+
keywords: List of keywords for this intent
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
float: Confidence score between 0.0 and 1.0
|
|
75
|
+
"""
|
|
76
|
+
if not user_input or not keywords:
|
|
77
|
+
return 0.0
|
|
78
|
+
|
|
79
|
+
matches = 0
|
|
80
|
+
matched_phrases = []
|
|
81
|
+
|
|
82
|
+
for keyword in keywords:
|
|
83
|
+
if keyword in user_input:
|
|
84
|
+
# Phrase matches get higher weight
|
|
85
|
+
if len(keyword.split()) > 1:
|
|
86
|
+
matches += 2.0
|
|
87
|
+
else:
|
|
88
|
+
matches += 1.0
|
|
89
|
+
matched_phrases.append(keyword)
|
|
90
|
+
|
|
91
|
+
if matches == 0:
|
|
92
|
+
return 0.0
|
|
93
|
+
|
|
94
|
+
# Confidence based on number of matches relative to keyword list
|
|
95
|
+
# and weighted by phrase matches
|
|
96
|
+
base_confidence = matches / (len(keywords) * 0.3)
|
|
97
|
+
|
|
98
|
+
# Boost confidence for direct phrase matches
|
|
99
|
+
if any(phrase in user_input for phrase in matched_phrases if len(phrase.split()) > 1):
|
|
100
|
+
base_confidence = min(base_confidence * 1.3, 1.0)
|
|
101
|
+
|
|
102
|
+
return min(base_confidence, 1.0)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def detect_intent(user_input: str) -> Tuple[IntentType, float]:
|
|
106
|
+
"""
|
|
107
|
+
Detect user intent from natural language input.
|
|
108
|
+
|
|
109
|
+
Uses keyword matching to classify the user's intent and returns
|
|
110
|
+
both the intent type and a confidence score.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
user_input: The natural language input from the user
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Tuple of (IntentType, confidence_score)
|
|
117
|
+
confidence_score is between 0.0 and 1.0
|
|
118
|
+
|
|
119
|
+
Examples:
|
|
120
|
+
>>> detect_intent("extract the algorithm from this text")
|
|
121
|
+
(IntentType.EXTRACT, 0.85)
|
|
122
|
+
|
|
123
|
+
>>> detect_intent("generate code for this")
|
|
124
|
+
(IntentType.GENERATE, 0.92)
|
|
125
|
+
|
|
126
|
+
>>> detect_intent("run it")
|
|
127
|
+
(IntentType.RUN, 0.45) # Low confidence - needs context
|
|
128
|
+
|
|
129
|
+
>>> detect_intent("can you help me")
|
|
130
|
+
(IntentType.HELP, 0.90)
|
|
131
|
+
"""
|
|
132
|
+
if not user_input or not isinstance(user_input, str):
|
|
133
|
+
return IntentType.UNKNOWN, 0.0
|
|
134
|
+
|
|
135
|
+
normalized = user_input.lower().strip()
|
|
136
|
+
|
|
137
|
+
# Remove punctuation for matching
|
|
138
|
+
normalized = re.sub(r'[^\w\s]', ' ', normalized)
|
|
139
|
+
normalized = re.sub(r'\s+', ' ', normalized).strip()
|
|
140
|
+
|
|
141
|
+
scores: Dict[IntentType, float] = {}
|
|
142
|
+
|
|
143
|
+
for intent_type, keywords in INTENT_KEYWORDS.items():
|
|
144
|
+
if intent_type == IntentType.UNKNOWN:
|
|
145
|
+
continue
|
|
146
|
+
scores[intent_type] = _calculate_confidence(normalized, keywords)
|
|
147
|
+
|
|
148
|
+
# Find highest scoring intent
|
|
149
|
+
if not scores:
|
|
150
|
+
return IntentType.UNKNOWN, 0.0
|
|
151
|
+
|
|
152
|
+
best_intent = max(scores, key=scores.get)
|
|
153
|
+
best_score = scores[best_intent]
|
|
154
|
+
|
|
155
|
+
# Check for ambiguity (multiple high scores)
|
|
156
|
+
high_scores = [s for s in scores.values() if s > 0.5]
|
|
157
|
+
if len(high_scores) > 1:
|
|
158
|
+
# Reduce confidence if multiple intents match
|
|
159
|
+
best_score *= 0.8
|
|
160
|
+
|
|
161
|
+
# Unknown if below threshold
|
|
162
|
+
if best_score < 0.2:
|
|
163
|
+
return IntentType.UNKNOWN, best_score
|
|
164
|
+
|
|
165
|
+
return best_intent, round(best_score, 2)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def route_to_workflow(intent: IntentType) -> Optional[str]:
|
|
169
|
+
"""
|
|
170
|
+
Return the workflow module path for the given intent.
|
|
171
|
+
|
|
172
|
+
Maps detected intents to their corresponding workflow modules.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
intent: The detected intent type
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Module path string or None if no workflow exists
|
|
179
|
+
|
|
180
|
+
Examples:
|
|
181
|
+
>>> route_to_workflow(IntentType.EXTRACT)
|
|
182
|
+
'src.workflows.extract'
|
|
183
|
+
|
|
184
|
+
>>> route_to_workflow(IntentType.UNKNOWN)
|
|
185
|
+
None
|
|
186
|
+
"""
|
|
187
|
+
workflow_map: Dict[IntentType, Optional[str]] = {
|
|
188
|
+
IntentType.EXTRACT: 'src.workflows.extract',
|
|
189
|
+
IntentType.GENERATE: 'src.workflows.generate',
|
|
190
|
+
IntentType.RUN: 'src.workflows.run',
|
|
191
|
+
IntentType.VERIFY: 'src.workflows.verify',
|
|
192
|
+
IntentType.STATUS: None, # Handled by context directly
|
|
193
|
+
IntentType.LIST: None, # Handled by context directly
|
|
194
|
+
IntentType.HELP: None, # Handled by command system
|
|
195
|
+
IntentType.UNKNOWN: None,
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return workflow_map.get(intent)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def suggest_next_steps(current_state: str) -> List[str]:
|
|
202
|
+
"""
|
|
203
|
+
Suggest next actions based on current workflow state.
|
|
204
|
+
|
|
205
|
+
Provides context-aware recommendations for what the user can do next.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
current_state: Current state identifier (e.g., 'TEXT_EXTRACTED',
|
|
209
|
+
'CODE_GENERATED', 'EXECUTION_COMPLETE', etc.)
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
List of actionable suggestions
|
|
213
|
+
|
|
214
|
+
Examples:
|
|
215
|
+
>>> suggest_next_steps('TEXT_EXTRACTED')
|
|
216
|
+
['Generate code with /algo-generate', 'Edit extracted text', 'Start over with /algo-extract']
|
|
217
|
+
|
|
218
|
+
>>> suggest_next_steps('CODE_GENERATED')
|
|
219
|
+
['Run the code with /algo-run', 'Review code before running', 'Regenerate if needed']
|
|
220
|
+
|
|
221
|
+
>>> suggest_next_steps('EXECUTION_COMPLETE')
|
|
222
|
+
['Verify results with /algo-verify', 'Run with different inputs', 'Modify and regenerate']
|
|
223
|
+
"""
|
|
224
|
+
if not current_state:
|
|
225
|
+
return [
|
|
226
|
+
"Extract an algorithm with /algo-extract",
|
|
227
|
+
"List saved algorithms with /algo-list",
|
|
228
|
+
"Show help with /algo-help"
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
suggestions: Dict[str, List[str]] = {
|
|
232
|
+
'NO_SESSION': [
|
|
233
|
+
"Start by extracting an algorithm: /algo-extract",
|
|
234
|
+
"Or load a saved algorithm: /algo-extract [name]",
|
|
235
|
+
"See all commands: /algo-help"
|
|
236
|
+
],
|
|
237
|
+
'TEXT_SAVED': [
|
|
238
|
+
"Extract steps from the text: /algo-extract (auto-detects)",
|
|
239
|
+
"View current text: /algo-status",
|
|
240
|
+
"Start over: /algo-extract"
|
|
241
|
+
],
|
|
242
|
+
'TEXT_EXTRACTED': [
|
|
243
|
+
"Generate code with /algo-generate",
|
|
244
|
+
"Review extracted steps: /algo-status",
|
|
245
|
+
"Edit the algorithm text",
|
|
246
|
+
"Extract a different algorithm: /algo-extract"
|
|
247
|
+
],
|
|
248
|
+
'STEPS_STRUCTURED': [
|
|
249
|
+
"Generate Python code: /algo-generate",
|
|
250
|
+
"Review structured steps: /algo-status",
|
|
251
|
+
"Go back and edit: /algo-extract",
|
|
252
|
+
"View all saved: /algo-list"
|
|
253
|
+
],
|
|
254
|
+
'CODE_GENERATED': [
|
|
255
|
+
"Run the code: /algo-run",
|
|
256
|
+
"Review code before running: /algo-verify",
|
|
257
|
+
"Regenerate if needed: /algo-generate",
|
|
258
|
+
"Check current status: /algo-status"
|
|
259
|
+
],
|
|
260
|
+
'CODE_SAVED': [
|
|
261
|
+
"Execute the code: /algo-run",
|
|
262
|
+
"Run with specific input: /algo-run [input]",
|
|
263
|
+
"Review the code: /algo-status"
|
|
264
|
+
],
|
|
265
|
+
'EXECUTION_COMPLETE': [
|
|
266
|
+
"Verify results: /algo-verify",
|
|
267
|
+
"Run with different inputs: /algo-run",
|
|
268
|
+
"Check execution details: /algo-status",
|
|
269
|
+
"Start new algorithm: /algo-extract"
|
|
270
|
+
],
|
|
271
|
+
'EXECUTION_FAILED': [
|
|
272
|
+
"Review the error: /algo-status",
|
|
273
|
+
"Regenerate code: /algo-generate",
|
|
274
|
+
"Check inputs and try again: /algo-run",
|
|
275
|
+
"Verify code logic: /algo-verify"
|
|
276
|
+
],
|
|
277
|
+
'VERIFICATION_COMPLETE': [
|
|
278
|
+
"Extract another algorithm: /algo-extract",
|
|
279
|
+
"List all saved algorithms: /algo-list",
|
|
280
|
+
"Start fresh: /algo-extract"
|
|
281
|
+
],
|
|
282
|
+
'DEFAULT': [
|
|
283
|
+
"Check current status: /algo-status",
|
|
284
|
+
"Show available commands: /algo-help",
|
|
285
|
+
"List saved algorithms: /algo-list"
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return suggestions.get(current_state, suggestions['DEFAULT'])
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def get_intent_description(intent: IntentType) -> str:
|
|
293
|
+
"""
|
|
294
|
+
Get a human-readable description of an intent type.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
intent: The intent type
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
Description string
|
|
301
|
+
"""
|
|
302
|
+
descriptions: Dict[IntentType, str] = {
|
|
303
|
+
IntentType.EXTRACT: "Extract algorithm from mathematical text",
|
|
304
|
+
IntentType.GENERATE: "Generate executable code from algorithm steps",
|
|
305
|
+
IntentType.RUN: "Execute generated code",
|
|
306
|
+
IntentType.VERIFY: "Verify execution results and explain behavior",
|
|
307
|
+
IntentType.STATUS: "Show current algorithm state and progress",
|
|
308
|
+
IntentType.LIST: "List all saved algorithms",
|
|
309
|
+
IntentType.HELP: "Show help and available commands",
|
|
310
|
+
IntentType.UNKNOWN: "Unknown intent - could not determine",
|
|
311
|
+
}
|
|
312
|
+
return descriptions.get(intent, "Unknown")
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
# Exports
|
|
316
|
+
__all__ = [
|
|
317
|
+
'IntentType',
|
|
318
|
+
'detect_intent',
|
|
319
|
+
'route_to_workflow',
|
|
320
|
+
'suggest_next_steps',
|
|
321
|
+
'get_intent_description',
|
|
322
|
+
'INTENT_KEYWORDS',
|
|
323
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Verification module for AlgoMath.
|
|
2
|
+
|
|
3
|
+
Provides execution verification, comparison, explanation, and edge case detection.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# From checker.py (existing)
|
|
7
|
+
from .checker import (
|
|
8
|
+
ExecutionChecker,
|
|
9
|
+
VerificationResult,
|
|
10
|
+
VerificationStatus,
|
|
11
|
+
verify_execution,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# From comparison.py (existing)
|
|
15
|
+
from .comparison import (
|
|
16
|
+
ComparisonResult,
|
|
17
|
+
ComparisonStatus,
|
|
18
|
+
OutputComparator,
|
|
19
|
+
compare_outputs,
|
|
20
|
+
prompt_for_expected,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# From explainer.py (new)
|
|
24
|
+
from .explainer import (
|
|
25
|
+
AlgorithmExplainer,
|
|
26
|
+
ExplanationResult,
|
|
27
|
+
ExplanationLevel,
|
|
28
|
+
StepExplanation,
|
|
29
|
+
explain_algorithm,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# From static_analysis.py (new)
|
|
33
|
+
from .static_analysis import (
|
|
34
|
+
EdgeCaseDetector,
|
|
35
|
+
EdgeCase,
|
|
36
|
+
EdgeCaseSeverity,
|
|
37
|
+
detect_edge_cases,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
# Checker
|
|
42
|
+
'ExecutionChecker',
|
|
43
|
+
'VerificationResult',
|
|
44
|
+
'VerificationStatus',
|
|
45
|
+
'verify_execution',
|
|
46
|
+
# Comparison
|
|
47
|
+
'ComparisonResult',
|
|
48
|
+
'ComparisonStatus',
|
|
49
|
+
'OutputComparator',
|
|
50
|
+
'compare_outputs',
|
|
51
|
+
'prompt_for_expected',
|
|
52
|
+
# Explainer
|
|
53
|
+
'AlgorithmExplainer',
|
|
54
|
+
'ExplanationResult',
|
|
55
|
+
'ExplanationLevel',
|
|
56
|
+
'StepExplanation',
|
|
57
|
+
'explain_algorithm',
|
|
58
|
+
# Edge Case Detection
|
|
59
|
+
'EdgeCaseDetector',
|
|
60
|
+
'EdgeCase',
|
|
61
|
+
'EdgeCaseSeverity',
|
|
62
|
+
'detect_edge_cases',
|
|
63
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|