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,339 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI Commands for AlgoMath - User-facing command implementations.
|
|
3
|
+
|
|
4
|
+
This module provides command functions that can be called directly
|
|
5
|
+
or through the intent routing system. Commands follow a consistent
|
|
6
|
+
return format with status, progress, message, and next_steps.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
# Add project root to Python path
|
|
16
|
+
project_root = Path(__file__).parent.parent.parent
|
|
17
|
+
if str(project_root) not in sys.path:
|
|
18
|
+
sys.path.insert(0, str(project_root))
|
|
19
|
+
|
|
20
|
+
from algomath.context import ContextManager
|
|
21
|
+
from algomath.state import WorkflowState
|
|
22
|
+
from src.workflows.run import run_execution
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def extract_command(text: str, name: Optional[str] = None) -> Dict[str, Any]:
|
|
26
|
+
"""
|
|
27
|
+
Extract algorithm from mathematical text.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
text: Mathematical text describing an algorithm
|
|
31
|
+
name: Optional name for the algorithm
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Dict with extraction status and results
|
|
35
|
+
"""
|
|
36
|
+
from src.workflows.extract import extract_algorithm
|
|
37
|
+
|
|
38
|
+
ctx = ContextManager()
|
|
39
|
+
ctx.start_session()
|
|
40
|
+
|
|
41
|
+
if name:
|
|
42
|
+
ctx.create_algorithm(name)
|
|
43
|
+
|
|
44
|
+
result = extract_algorithm(ctx, text)
|
|
45
|
+
return result
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def generate_command() -> Dict[str, Any]:
|
|
49
|
+
"""
|
|
50
|
+
Generate code from extracted algorithm steps.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Dict with generation status and results
|
|
54
|
+
"""
|
|
55
|
+
from src.workflows.generate import generate_code
|
|
56
|
+
|
|
57
|
+
ctx = ContextManager()
|
|
58
|
+
ctx.start_session()
|
|
59
|
+
|
|
60
|
+
result = generate_code(ctx)
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def run_command(
|
|
65
|
+
skip: bool = False,
|
|
66
|
+
inputs: Optional[Dict[str, Any]] = None
|
|
67
|
+
) -> Dict[str, Any]:
|
|
68
|
+
"""
|
|
69
|
+
Execute generated code with /algo-run.
|
|
70
|
+
|
|
71
|
+
Per D-21: Auto-triggered after code approval
|
|
72
|
+
Per D-25: Can skip with skip=True to proceed directly to verification
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
skip: If True, skip execution and proceed to verification
|
|
76
|
+
inputs: Optional input data for the algorithm
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Dict with execution status and results
|
|
80
|
+
"""
|
|
81
|
+
ctx = ContextManager()
|
|
82
|
+
ctx.start_session()
|
|
83
|
+
|
|
84
|
+
current = ctx.get_current()
|
|
85
|
+
|
|
86
|
+
# Check current state
|
|
87
|
+
if current.current_state == WorkflowState.EXECUTION_COMPLETE:
|
|
88
|
+
# Already executed - show results
|
|
89
|
+
results = current.data.get('results', {})
|
|
90
|
+
return {
|
|
91
|
+
'status': 'already_executed',
|
|
92
|
+
'message': 'Algorithm already executed. Run again to re-execute.',
|
|
93
|
+
'results': results,
|
|
94
|
+
'next_steps': [
|
|
95
|
+
'Verify results with /algo-verify',
|
|
96
|
+
'Re-run with /algo-run',
|
|
97
|
+
'Regenerate with /algo-generate'
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if current.current_state != WorkflowState.CODE_GENERATED:
|
|
102
|
+
return {
|
|
103
|
+
'status': 'invalid_state',
|
|
104
|
+
'message': f"Cannot execute: current state is {current.current_state.value}",
|
|
105
|
+
'required_state': 'code_generated',
|
|
106
|
+
'next_steps': [
|
|
107
|
+
'Generate code first with /algo-generate',
|
|
108
|
+
'Check status with /algo-status'
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Check if skip requested per D-25
|
|
113
|
+
if skip:
|
|
114
|
+
# Skip execution, proceed to verification
|
|
115
|
+
ctx.save_results({
|
|
116
|
+
'status': 'skipped',
|
|
117
|
+
'message': 'Execution skipped by user',
|
|
118
|
+
'timestamp': datetime.now().isoformat()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
'status': 'skipped',
|
|
123
|
+
'message': 'Execution skipped. Proceeding to verification.',
|
|
124
|
+
'next_steps': [
|
|
125
|
+
'Verify with /algo-verify',
|
|
126
|
+
'Run later with /algo-run'
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# Execute
|
|
131
|
+
print(f"Executing algorithm: {current.current_algorithm or '(unnamed)'}...")
|
|
132
|
+
result = run_execution(ctx, inputs=inputs)
|
|
133
|
+
|
|
134
|
+
return result
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def verify_command(
|
|
138
|
+
expected: Optional[Any] = None,
|
|
139
|
+
step: Optional[int] = None,
|
|
140
|
+
detailed: bool = False,
|
|
141
|
+
diagnostic: bool = False
|
|
142
|
+
) -> Dict[str, Any]:
|
|
143
|
+
"""
|
|
144
|
+
Verify execution results with /algo-verify.
|
|
145
|
+
|
|
146
|
+
Per D-01: Quick inline summary shown automatically
|
|
147
|
+
Per D-02: Full verification via this command
|
|
148
|
+
Per D-03: Diagnostic mode for failed executions
|
|
149
|
+
Per D-06: Detailed explanation with --detailed
|
|
150
|
+
Per D-09: Interactive expected results prompt
|
|
151
|
+
Per D-22: Diagnostic mode with --diagnostic
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
expected: Optional expected output for comparison
|
|
155
|
+
step: Optional step ID for detailed explanation (VER-05)
|
|
156
|
+
detailed: If True, generate detailed step-by-step explanation
|
|
157
|
+
diagnostic: If True, run diagnostic mode for failed executions
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Dict with verification status and results
|
|
161
|
+
"""
|
|
162
|
+
from src.workflows.verify import run_verification, verify_step
|
|
163
|
+
|
|
164
|
+
ctx = ContextManager()
|
|
165
|
+
ctx.start_session()
|
|
166
|
+
|
|
167
|
+
current = ctx.get_current()
|
|
168
|
+
|
|
169
|
+
# Check current state per D-02, D-25
|
|
170
|
+
if current.current_state == WorkflowState.VERIFIED:
|
|
171
|
+
# Already verified - show cached report
|
|
172
|
+
algorithm_data = ctx.store.load_session()
|
|
173
|
+
return {
|
|
174
|
+
'status': 'already_verified',
|
|
175
|
+
'message': 'Algorithm already verified. Run again for fresh verification.',
|
|
176
|
+
'last_verification': algorithm_data.get('verification', {}),
|
|
177
|
+
'next_steps': [
|
|
178
|
+
'Re-verify with /algo-verify',
|
|
179
|
+
'Extract new with /algo-extract',
|
|
180
|
+
'Check status with /algo-status'
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if current.current_state not in [WorkflowState.EXECUTION_COMPLETE, WorkflowState.CODE_GENERATED]:
|
|
185
|
+
return {
|
|
186
|
+
'status': 'not_ready',
|
|
187
|
+
'message': f"Cannot verify: current state is {current.current_state.value}",
|
|
188
|
+
'required_state': 'execution_complete',
|
|
189
|
+
'next_steps': [
|
|
190
|
+
'Run code first with /algo-run',
|
|
191
|
+
'Check status with /algo-status'
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# Handle step-specific explanation per VER-05
|
|
196
|
+
if step is not None:
|
|
197
|
+
result = verify_step(ctx, step)
|
|
198
|
+
return result
|
|
199
|
+
|
|
200
|
+
# Interactive prompt for expected results per D-09
|
|
201
|
+
if expected is None and not diagnostic:
|
|
202
|
+
# Note: In actual CLI, this would prompt user
|
|
203
|
+
# For now, proceed without comparison
|
|
204
|
+
pass
|
|
205
|
+
|
|
206
|
+
# Run full verification per D-02
|
|
207
|
+
print(f"Verifying algorithm: {current.current_algorithm or '(unnamed)'}...")
|
|
208
|
+
result = run_verification(
|
|
209
|
+
ctx,
|
|
210
|
+
expected=expected,
|
|
211
|
+
detailed=detailed,
|
|
212
|
+
diagnostic=diagnostic
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
return result
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def status_command() -> Dict[str, Any]:
|
|
219
|
+
"""
|
|
220
|
+
Show current algorithm status and progress.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Dict with current state information
|
|
224
|
+
"""
|
|
225
|
+
ctx = ContextManager()
|
|
226
|
+
ctx.start_session()
|
|
227
|
+
|
|
228
|
+
current = ctx.get_current()
|
|
229
|
+
progress = ctx.get_progress()
|
|
230
|
+
progress_bar = ctx.get_progress_bar()
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
'status': 'success',
|
|
234
|
+
'algorithm': progress['algorithm_name'],
|
|
235
|
+
'state': current.current_state.value,
|
|
236
|
+
'progress_bar': progress_bar,
|
|
237
|
+
'steps_completed': progress['steps_completed'],
|
|
238
|
+
'steps_total': progress['steps_total'],
|
|
239
|
+
'data_status': progress['data_status'],
|
|
240
|
+
'has_text': 'text' in current.data,
|
|
241
|
+
'has_steps': 'steps' in current.data,
|
|
242
|
+
'has_code': 'code' in current.data,
|
|
243
|
+
'has_results': 'results' in current.data,
|
|
244
|
+
'next_steps': [
|
|
245
|
+
f"Current: {current.current_state.value}",
|
|
246
|
+
"Continue with workflow commands",
|
|
247
|
+
"Use /algo-help for command list"
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def list_command() -> Dict[str, Any]:
|
|
253
|
+
"""
|
|
254
|
+
List all saved algorithms.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
Dict with list of algorithms
|
|
258
|
+
"""
|
|
259
|
+
ctx = ContextManager()
|
|
260
|
+
algorithms = ctx.list_algorithms()
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
'status': 'success',
|
|
264
|
+
'count': len(algorithms),
|
|
265
|
+
'algorithms': [
|
|
266
|
+
{'name': name, 'updated': updated}
|
|
267
|
+
for name, updated in algorithms
|
|
268
|
+
],
|
|
269
|
+
'next_steps': [
|
|
270
|
+
'Load algorithm with /algo-extract [name]',
|
|
271
|
+
'Start new with /algo-extract',
|
|
272
|
+
'Check status with /algo-status'
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def help_command() -> Dict[str, Any]:
|
|
278
|
+
"""
|
|
279
|
+
Show help and available commands.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Dict with command reference
|
|
283
|
+
"""
|
|
284
|
+
commands = [
|
|
285
|
+
('/algo-extract "text" [name]', 'Extract algorithm from text', 'Initial'),
|
|
286
|
+
('/algo-generate', 'Generate code from steps', 'STEPS_STRUCTURED'),
|
|
287
|
+
('/algo-run', 'Execute generated code', 'CODE_GENERATED'),
|
|
288
|
+
('/algo-run --skip', 'Skip execution (proceed to verify)', 'CODE_GENERATED'),
|
|
289
|
+
('/algo-verify', 'Verify execution results', 'EXECUTION_COMPLETE'),
|
|
290
|
+
('/algo-verify --step N', 'Explain step N in detail', 'EXECUTION_COMPLETE'),
|
|
291
|
+
('/algo-verify --detailed', 'Show detailed explanation', 'EXECUTION_COMPLETE'),
|
|
292
|
+
('/algo-verify --diagnostic', 'Diagnose failed execution', 'EXECUTION_COMPLETE'),
|
|
293
|
+
('/algo-status', 'Show current state and progress', 'Any'),
|
|
294
|
+
('/algo-list', 'List saved algorithms', 'Any'),
|
|
295
|
+
('/algo-help', 'Show this help', 'Any'),
|
|
296
|
+
]
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
'status': 'success',
|
|
300
|
+
'commands': [
|
|
301
|
+
{
|
|
302
|
+
'command': cmd,
|
|
303
|
+
'description': desc,
|
|
304
|
+
'requires': req
|
|
305
|
+
}
|
|
306
|
+
for cmd, desc, req in commands
|
|
307
|
+
],
|
|
308
|
+
'tip': 'Commands can be used naturally, e.g., "extract algorithm from this text"',
|
|
309
|
+
'next_steps': [
|
|
310
|
+
'Try /algo-extract to start',
|
|
311
|
+
'Check /algo-status any time',
|
|
312
|
+
'Ask for /algo-help when needed'
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
# Command map for routing
|
|
318
|
+
COMMAND_MAP = {
|
|
319
|
+
'extract': extract_command,
|
|
320
|
+
'generate': generate_command,
|
|
321
|
+
'run': run_command,
|
|
322
|
+
'verify': verify_command,
|
|
323
|
+
'status': status_command,
|
|
324
|
+
'list': list_command,
|
|
325
|
+
'help': help_command,
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
# Exports
|
|
330
|
+
__all__ = [
|
|
331
|
+
'COMMAND_MAP',
|
|
332
|
+
'extract_command',
|
|
333
|
+
'generate_command',
|
|
334
|
+
'run_command',
|
|
335
|
+
'verify_command',
|
|
336
|
+
'status_command',
|
|
337
|
+
'list_command',
|
|
338
|
+
'help_command',
|
|
339
|
+
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Execution module for AlgoMath.
|
|
2
|
+
|
|
3
|
+
Provides safe, sandboxed code execution with resource limits,
|
|
4
|
+
timeout protection, and comprehensive output capture.
|
|
5
|
+
|
|
6
|
+
Per Phase 4 decisions D-01 through D-30:
|
|
7
|
+
- D-01: Subprocess-based isolation
|
|
8
|
+
- D-02: Resource limits (CPU, memory)
|
|
9
|
+
- D-05: 30-second default timeout
|
|
10
|
+
- D-09, D-10: Temp directory sandbox with auto-cleanup
|
|
11
|
+
- D-16: Execution metadata capture
|
|
12
|
+
- D-17: Error categorization
|
|
13
|
+
- D-28: Import restrictions
|
|
14
|
+
- D-29: Input handling
|
|
15
|
+
- D-30: Return value capture
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
>>> from src.execution import execute_code, ExecutionConfig
|
|
19
|
+
>>> config = ExecutionConfig(timeout=60, max_memory_mb=1024)
|
|
20
|
+
>>> result = execute_code('print("Hello")', config=config)
|
|
21
|
+
>>> print(result.stdout)
|
|
22
|
+
"Hello"
|
|
23
|
+
>>> print(result.status)
|
|
24
|
+
"success"
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# Core sandbox and executor components
|
|
28
|
+
from .sandbox import SandboxExecutor, ExecutionResult, ExecutionStatus, execute_sandboxed
|
|
29
|
+
from .executor import execute_code, ExecutionConfig, format_results_for_context, build_execution_response
|
|
30
|
+
|
|
31
|
+
# Error handling and display (from existing modules)
|
|
32
|
+
from .errors import (
|
|
33
|
+
ExecutionError,
|
|
34
|
+
ErrorTranslator,
|
|
35
|
+
ErrorDetails,
|
|
36
|
+
categorize_error,
|
|
37
|
+
extract_line_number,
|
|
38
|
+
)
|
|
39
|
+
from .display import (
|
|
40
|
+
ExecutionFormatter,
|
|
41
|
+
FormattedResult,
|
|
42
|
+
truncate_output,
|
|
43
|
+
show_progress,
|
|
44
|
+
show_execution_summary,
|
|
45
|
+
format_execution_log,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
__version__ = "0.1.0"
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
# Sandbox components
|
|
52
|
+
'SandboxExecutor',
|
|
53
|
+
'ExecutionResult',
|
|
54
|
+
'ExecutionStatus',
|
|
55
|
+
'execute_sandboxed',
|
|
56
|
+
# High-level interface
|
|
57
|
+
'execute_code',
|
|
58
|
+
'ExecutionConfig',
|
|
59
|
+
'format_results_for_context',
|
|
60
|
+
'build_execution_response',
|
|
61
|
+
# Error handling
|
|
62
|
+
'ExecutionError',
|
|
63
|
+
'ErrorTranslator',
|
|
64
|
+
'ErrorDetails',
|
|
65
|
+
'categorize_error',
|
|
66
|
+
'extract_line_number',
|
|
67
|
+
# Display formatting
|
|
68
|
+
'ExecutionFormatter',
|
|
69
|
+
'FormattedResult',
|
|
70
|
+
'truncate_output',
|
|
71
|
+
'show_progress',
|
|
72
|
+
'show_execution_summary',
|
|
73
|
+
'format_execution_log',
|
|
74
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""Output formatting and display for execution results.
|
|
2
|
+
|
|
3
|
+
Covers EXE-05 (status reporting) and output formatting per D-13 through D-16.
|
|
4
|
+
Reuses progress indicator pattern from Phase 1 (D-09).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional, Any
|
|
10
|
+
|
|
11
|
+
from .errors import ErrorDetails, ExecutionError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Constants per D-15
|
|
15
|
+
MAX_INLINE_LINES = 50
|
|
16
|
+
TRUNCATION_MESSAGE = "\n... ({} more lines - see full log for details)\n"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class FormattedResult:
|
|
21
|
+
"""Formatted execution result per D-14, D-16.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
display_text: Formatted text for display
|
|
25
|
+
status_emoji: Visual status indicator
|
|
26
|
+
status_text: Text status (Success/Failed/Timed Out)
|
|
27
|
+
execution_time: Human-readable execution time
|
|
28
|
+
output_truncated: Whether output was truncated
|
|
29
|
+
full_log_path: Path to full log file (if exists)
|
|
30
|
+
"""
|
|
31
|
+
display_text: str
|
|
32
|
+
status_emoji: str
|
|
33
|
+
status_text: str
|
|
34
|
+
execution_time: str
|
|
35
|
+
output_truncated: bool
|
|
36
|
+
full_log_path: Optional[str]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ExecutionFormatter:
|
|
40
|
+
"""Format execution results for display per D-14, D-15, D-16.
|
|
41
|
+
|
|
42
|
+
Takes raw execution results and formats them for user presentation
|
|
43
|
+
with status indicators, truncated output, and log references.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, algorithm_name: Optional[str] = None):
|
|
47
|
+
"""Initialize formatter with optional algorithm name.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
algorithm_name: Name of the algorithm (for log path)
|
|
51
|
+
"""
|
|
52
|
+
self.algorithm_name = algorithm_name
|
|
53
|
+
self.log_dir = Path(".algomath/algorithms") / algorithm_name if algorithm_name else None
|
|
54
|
+
|
|
55
|
+
def format_results(self, result: Any) -> FormattedResult:
|
|
56
|
+
"""Format execution result for display per D-14, D-15, D-16.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
result: Execution result object with status, stdout, stderr, runtime_seconds
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
FormattedResult with display-ready text
|
|
63
|
+
"""
|
|
64
|
+
# Status indicators per EXE-05
|
|
65
|
+
if result.status == 'success':
|
|
66
|
+
emoji = '✓'
|
|
67
|
+
status = 'Success'
|
|
68
|
+
elif result.status == 'timeout':
|
|
69
|
+
emoji = '⏱'
|
|
70
|
+
status = 'Timed Out'
|
|
71
|
+
else:
|
|
72
|
+
emoji = '✗'
|
|
73
|
+
status = 'Failed'
|
|
74
|
+
|
|
75
|
+
# Build display text
|
|
76
|
+
lines = []
|
|
77
|
+
lines.append(f"{emoji} Execution: {status}")
|
|
78
|
+
|
|
79
|
+
# Execution time per D-16
|
|
80
|
+
runtime = getattr(result, 'runtime_seconds', 0)
|
|
81
|
+
lines.append(f"Time: {runtime:.3f}s")
|
|
82
|
+
|
|
83
|
+
# Output per D-13 (dual capture) and D-15 (truncation)
|
|
84
|
+
stdout = getattr(result, 'stdout', '') or ''
|
|
85
|
+
stderr = getattr(result, 'stderr', '') or ''
|
|
86
|
+
|
|
87
|
+
if stdout:
|
|
88
|
+
lines.append("\n--- Output ---")
|
|
89
|
+
output = truncate_output(stdout, MAX_INLINE_LINES)
|
|
90
|
+
lines.append(output)
|
|
91
|
+
|
|
92
|
+
# Errors (fewer lines shown)
|
|
93
|
+
if stderr:
|
|
94
|
+
lines.append("\n--- Errors ---")
|
|
95
|
+
errors = truncate_output(stderr, 20) # Fewer error lines
|
|
96
|
+
lines.append(errors)
|
|
97
|
+
|
|
98
|
+
# Full log path per D-14
|
|
99
|
+
log_path = str(self.log_dir / "execution.log") if self.log_dir else None
|
|
100
|
+
output_truncated = len(stdout.split('\n')) > MAX_INLINE_LINES if stdout else False
|
|
101
|
+
|
|
102
|
+
if log_path and output_truncated:
|
|
103
|
+
lines.append(f"\nFull log: {log_path}")
|
|
104
|
+
|
|
105
|
+
return FormattedResult(
|
|
106
|
+
display_text='\n'.join(lines),
|
|
107
|
+
status_emoji=emoji,
|
|
108
|
+
status_text=status,
|
|
109
|
+
execution_time=f"{runtime:.3f}s",
|
|
110
|
+
output_truncated=output_truncated,
|
|
111
|
+
full_log_path=log_path
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def truncate_output(text: str, max_lines: int = MAX_INLINE_LINES) -> str:
|
|
116
|
+
"""Truncate output to max_lines with summary per D-15.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
text: Output text to truncate
|
|
120
|
+
max_lines: Maximum number of lines to show
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Truncated text with summary message if needed
|
|
124
|
+
"""
|
|
125
|
+
if not text:
|
|
126
|
+
return text
|
|
127
|
+
|
|
128
|
+
lines = text.split('\n')
|
|
129
|
+
if len(lines) <= max_lines:
|
|
130
|
+
return text
|
|
131
|
+
|
|
132
|
+
truncated = '\n'.join(lines[:max_lines])
|
|
133
|
+
remaining = len(lines) - max_lines
|
|
134
|
+
return truncated + TRUNCATION_MESSAGE.format(remaining)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def show_progress(phase: str, current: int, total: int) -> str:
|
|
138
|
+
"""Generate progress bar string per Phase 1 D-09.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
phase: Name of the current phase
|
|
142
|
+
current: Current step number
|
|
143
|
+
total: Total number of steps
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Formatted progress bar string like "Execute: █████░░░░░ 50%"
|
|
147
|
+
"""
|
|
148
|
+
if total <= 0:
|
|
149
|
+
return f"{phase}: ░░░░░░░░░░ 0%"
|
|
150
|
+
|
|
151
|
+
filled = int(10 * current / total)
|
|
152
|
+
filled = max(0, min(filled, 10)) # Clamp to 0-10 range
|
|
153
|
+
bar = '█' * filled + '░' * (10 - filled)
|
|
154
|
+
pct = int(100 * current / total)
|
|
155
|
+
return f"{phase}: {bar} {pct}%"
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def show_execution_summary(
|
|
159
|
+
result: Any,
|
|
160
|
+
error_details: Optional[ErrorDetails] = None
|
|
161
|
+
) -> str:
|
|
162
|
+
"""Show execution summary with error translation if needed per EXE-05, EXE-06.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
result: Execution result object
|
|
166
|
+
error_details: Optional error translation details
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Formatted summary string
|
|
170
|
+
"""
|
|
171
|
+
lines = []
|
|
172
|
+
|
|
173
|
+
# Status line per EXE-05
|
|
174
|
+
runtime = getattr(result, 'runtime_seconds', 0)
|
|
175
|
+
if result.status == 'success':
|
|
176
|
+
lines.append(f"✓ Algorithm executed successfully in {runtime:.3f}s")
|
|
177
|
+
else:
|
|
178
|
+
lines.append(f"✗ Execution {result.status}")
|
|
179
|
+
|
|
180
|
+
# Error translation per EXE-06
|
|
181
|
+
if error_details:
|
|
182
|
+
lines.append(f"\n{error_details.user_message}")
|
|
183
|
+
lines.append(f"\n💡 {error_details.hint}")
|
|
184
|
+
|
|
185
|
+
# Technical details in collapsed section per D-19
|
|
186
|
+
if error_details.technical_details:
|
|
187
|
+
lines.append(f"\n<details>")
|
|
188
|
+
lines.append(f"<summary>Technical Details (for debugging)</summary>")
|
|
189
|
+
lines.append(f"\n```\n{error_details.technical_details}\n```")
|
|
190
|
+
lines.append(f"</details>")
|
|
191
|
+
|
|
192
|
+
return '\n'.join(lines)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def format_execution_log(
|
|
196
|
+
algorithm_name: str,
|
|
197
|
+
status: str,
|
|
198
|
+
runtime_seconds: float,
|
|
199
|
+
stdout: str,
|
|
200
|
+
stderr: str,
|
|
201
|
+
error_type: Optional[str] = None,
|
|
202
|
+
error_message: Optional[str] = None
|
|
203
|
+
) -> str:
|
|
204
|
+
"""Format complete execution log for file persistence per D-14.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
algorithm_name: Name of the algorithm
|
|
208
|
+
status: Execution status
|
|
209
|
+
runtime_seconds: Execution time in seconds
|
|
210
|
+
stdout: Standard output
|
|
211
|
+
stderr: Standard error
|
|
212
|
+
error_type: Type of error if failed
|
|
213
|
+
error_message: Error message if failed
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
Formatted log content for saving to file
|
|
217
|
+
"""
|
|
218
|
+
from datetime import datetime
|
|
219
|
+
|
|
220
|
+
lines = [
|
|
221
|
+
f"Algorithm: {algorithm_name}",
|
|
222
|
+
f"Status: {status}",
|
|
223
|
+
f"Timestamp: {datetime.now().isoformat()}",
|
|
224
|
+
f"Runtime: {runtime_seconds:.3f}s",
|
|
225
|
+
"",
|
|
226
|
+
"=" * 50,
|
|
227
|
+
"OUTPUT",
|
|
228
|
+
"=" * 50,
|
|
229
|
+
stdout if stdout else "(no output)",
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
if stderr:
|
|
233
|
+
lines.extend([
|
|
234
|
+
"",
|
|
235
|
+
"=" * 50,
|
|
236
|
+
"ERRORS",
|
|
237
|
+
"=" * 50,
|
|
238
|
+
stderr
|
|
239
|
+
])
|
|
240
|
+
|
|
241
|
+
if error_type:
|
|
242
|
+
lines.extend([
|
|
243
|
+
"",
|
|
244
|
+
"=" * 50,
|
|
245
|
+
"ERROR DETAILS",
|
|
246
|
+
"=" * 50,
|
|
247
|
+
f"Type: {error_type}",
|
|
248
|
+
f"Message: {error_message or '(no message)'}"
|
|
249
|
+
])
|
|
250
|
+
|
|
251
|
+
return '\n'.join(lines)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
__all__ = [
|
|
255
|
+
'ExecutionFormatter',
|
|
256
|
+
'FormattedResult',
|
|
257
|
+
'truncate_output',
|
|
258
|
+
'show_progress',
|
|
259
|
+
'show_execution_summary',
|
|
260
|
+
'format_execution_log',
|
|
261
|
+
]
|