x-ipe 1.0.24__py3-none-any.whl → 1.0.25__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.
- x_ipe/app.py +25 -3
- x_ipe/handlers/terminal_handlers.py +6 -0
- x_ipe/handlers/voice_handlers.py +5 -0
- x_ipe/resources/copilot-instructions.md +19 -6
- x_ipe/resources/skills/lesson-learned/SKILL.md +208 -0
- x_ipe/resources/skills/lesson-learned/references/examples.md +238 -0
- x_ipe/resources/skills/project-quality-board-management/SKILL.md +135 -298
- x_ipe/resources/skills/project-quality-board-management/references/evaluation-principles.md +213 -0
- x_ipe/resources/skills/project-quality-board-management/references/evaluation-procedures.md +214 -0
- x_ipe/resources/skills/project-quality-board-management/templates/quality-report.md +70 -18
- x_ipe/resources/skills/task-execution-guideline/SKILL.md +2 -2
- x_ipe/resources/skills/task-execution-guideline/templates/task-record.yaml +1 -1
- x_ipe/resources/skills/task-type-code-implementation/SKILL.md +72 -270
- x_ipe/resources/skills/task-type-code-implementation/references/implementation-guidelines.md +432 -0
- x_ipe/resources/skills/task-type-code-refactor-v2/SKILL.md +127 -353
- x_ipe/resources/skills/task-type-code-refactor-v2/references/refactoring-techniques.md +373 -0
- x_ipe/resources/skills/task-type-feature-breakdown/SKILL.md +31 -243
- x_ipe/resources/skills/task-type-feature-breakdown/references/breakdown-guidelines.md +330 -0
- x_ipe/resources/skills/task-type-feature-refinement/SKILL.md +27 -180
- x_ipe/resources/skills/task-type-feature-refinement/references/specification-writing-guide.md +267 -0
- x_ipe/resources/skills/task-type-idea-mockup/SKILL.md +38 -276
- x_ipe/resources/skills/task-type-idea-mockup/references/mockup-guidelines.md +299 -0
- x_ipe/resources/skills/task-type-idea-to-architecture/SKILL.md +20 -218
- x_ipe/resources/skills/task-type-idea-to-architecture/references/architecture-patterns.md +342 -0
- x_ipe/resources/skills/task-type-ideation/SKILL.md +10 -266
- x_ipe/resources/skills/task-type-ideation/references/folder-naming-guide.md +55 -0
- x_ipe/resources/skills/task-type-ideation/references/tool-usage-guide.md +236 -0
- x_ipe/resources/skills/task-type-ideation-v2/SKILL.md +488 -0
- x_ipe/resources/skills/task-type-ideation-v2/references/examples.md +377 -0
- x_ipe/resources/skills/task-type-ideation-v2/references/folder-naming-guide.md +74 -0
- x_ipe/resources/skills/task-type-ideation-v2/references/tool-usage-guide.md +145 -0
- x_ipe/resources/skills/task-type-ideation-v2/references/visualization-guide.md +160 -0
- x_ipe/resources/skills/task-type-ideation-v2/templates/idea-summary.md +86 -0
- x_ipe/resources/skills/task-type-refactoring-analysis/SKILL.md +83 -145
- x_ipe/resources/skills/task-type-refactoring-analysis/references/output-schema.md +172 -0
- x_ipe/resources/skills/task-type-technical-design/SKILL.md +28 -214
- x_ipe/resources/skills/task-type-technical-design/references/design-templates.md +422 -0
- x_ipe/resources/skills/task-type-test-generation/SKILL.md +47 -332
- x_ipe/resources/skills/task-type-test-generation/references/test-patterns.md +368 -0
- x_ipe/resources/skills/tool-tracing-creator/SKILL.md +312 -0
- x_ipe/resources/skills/tool-tracing-creator/references/examples.md +324 -0
- x_ipe/resources/skills/tool-tracing-instrumentation/SKILL.md +373 -0
- x_ipe/resources/skills/tool-tracing-instrumentation/references/examples.md +264 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/SKILL.md +486 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/10. example-gate-conditions.md +73 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/11. reference-quality-standards.md +127 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/2. reference-section-order.md +127 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/3. example-step-based-code-review.md +84 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/4. example-step-based-feature-implementation.md +113 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/5. example-function-based-validation.md +73 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/6. example-function-based-analysis.md +94 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/7. example-task-io-code-implementation.md +36 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/8. example-structured-summary.md +43 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/9. example-dor-dod.md +77 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/examples.md +429 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/skill-general-guidelines-v2.md +611 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-meta.md +153 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-task-based.md +324 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-task-category.md +109 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-tool.md +205 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-meta.md +334 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-task-based.md +279 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-tool.md +175 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-workflow-orchestration.md +329 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/SKILL.md +487 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/references/examples.md +377 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/references/folder-naming-guide.md +74 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/references/tool-usage-guide.md +145 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/references/visualization-guide.md +160 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/templates/idea-summary.md +86 -0
- x_ipe/routes/__init__.py +2 -0
- x_ipe/routes/ideas_routes.py +17 -0
- x_ipe/routes/kb_routes.py +80 -0
- x_ipe/routes/main_routes.py +18 -0
- x_ipe/routes/project_routes.py +7 -0
- x_ipe/routes/proxy_routes.py +2 -0
- x_ipe/routes/quality_evaluation_routes.py +193 -0
- x_ipe/routes/settings_routes.py +6 -0
- x_ipe/routes/tools_routes.py +6 -0
- x_ipe/routes/tracing_routes.py +232 -0
- x_ipe/routes/uiux_feedback_routes.py +30 -0
- x_ipe/services/__init__.py +5 -0
- x_ipe/services/config_service.py +6 -0
- x_ipe/services/file_service.py +20 -0
- x_ipe/services/homepage_service.py +160 -0
- x_ipe/services/ideas_service.py +19 -0
- x_ipe/services/kb_service.py +378 -0
- x_ipe/services/proxy_service.py +4 -0
- x_ipe/services/settings_service.py +13 -0
- x_ipe/services/skills_service.py +4 -0
- x_ipe/services/terminal_service.py +24 -0
- x_ipe/services/themes_service.py +4 -0
- x_ipe/services/tools_config_service.py +4 -0
- x_ipe/services/tracing_service.py +333 -0
- x_ipe/services/uiux_feedback_service.py +32 -0
- x_ipe/services/voice_input_service_v2.py +11 -0
- x_ipe/static/css/base.css +7 -0
- x_ipe/static/css/homepage-infinity.css +330 -0
- x_ipe/static/css/kb-core.css +301 -0
- x_ipe/static/css/quality-evaluation.css +345 -0
- x_ipe/static/css/sidebar.css +14 -4
- x_ipe/static/css/terminal.css +1 -0
- x_ipe/static/css/tracing-dashboard.css +796 -0
- x_ipe/static/css/workplace.css +20 -0
- x_ipe/static/img/homepage-infinity-loop.png +0 -0
- x_ipe/static/js/features/homepage-infinity.js +314 -0
- x_ipe/static/js/features/kb-core.js +371 -0
- x_ipe/static/js/features/quality-evaluation.js +387 -0
- x_ipe/static/js/features/sidebar.js +255 -12
- x_ipe/static/js/features/tracing-dashboard.js +855 -0
- x_ipe/static/js/features/tracing-graph.js +1031 -0
- x_ipe/static/js/features/tree-search.js +6 -2
- x_ipe/static/js/features/workplace.js +200 -6
- x_ipe/static/js/init.js +76 -0
- x_ipe/static/js/uiux-feedback.js +18 -2
- x_ipe/templates/base.html +19 -0
- x_ipe/templates/index.html +7 -1
- x_ipe/templates/knowledge-base.html +110 -0
- x_ipe/templates/workplace.html +4 -0
- x_ipe/tracing/__init__.py +37 -0
- x_ipe/tracing/buffer.py +135 -0
- x_ipe/tracing/context.py +125 -0
- x_ipe/tracing/decorator.py +288 -0
- x_ipe/tracing/middleware.py +197 -0
- x_ipe/tracing/parser.py +235 -0
- x_ipe/tracing/redactor.py +111 -0
- x_ipe/tracing/writer.py +122 -0
- {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/METADATA +2 -2
- {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/RECORD +132 -62
- x_ipe/resources/skills/x-ipe-skill-creator/SKILL.md +0 -329
- x_ipe/resources/skills/x-ipe-skill-creator/references/output-patterns.md +0 -169
- x_ipe/resources/skills/x-ipe-skill-creator/references/skill-structure.md +0 -162
- x_ipe/resources/skills/x-ipe-skill-creator/references/workflows.md +0 -110
- x_ipe/resources/skills/x-ipe-skill-creator/templates/references/examples.md +0 -113
- x_ipe/resources/skills/x-ipe-skill-creator/templates/skill-category-skill.md +0 -296
- x_ipe/resources/skills/x-ipe-skill-creator/templates/task-type-skill.md +0 -269
- {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/WHEEL +0 -0
- {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/entry_points.txt +0 -0
- {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,6 +11,8 @@ import copy
|
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from typing import Dict, Any
|
|
13
13
|
|
|
14
|
+
from x_ipe.tracing import x_ipe_tracing
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
CONFIG_DIR = 'x-ipe-docs/config'
|
|
16
18
|
CONFIG_FILE = 'tools.json'
|
|
@@ -56,6 +58,7 @@ class ToolsConfigService:
|
|
|
56
58
|
self.config_path = self.config_dir / CONFIG_FILE
|
|
57
59
|
self.legacy_path = self.project_root / LEGACY_PATH
|
|
58
60
|
|
|
61
|
+
@x_ipe_tracing()
|
|
59
62
|
def load(self) -> Dict[str, Any]:
|
|
60
63
|
"""
|
|
61
64
|
Load config, migrating from legacy if needed.
|
|
@@ -76,6 +79,7 @@ class ToolsConfigService:
|
|
|
76
79
|
|
|
77
80
|
return self._create_default()
|
|
78
81
|
|
|
82
|
+
@x_ipe_tracing()
|
|
79
83
|
def save(self, config: Dict[str, Any]) -> bool:
|
|
80
84
|
"""
|
|
81
85
|
Save config to file.
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FEATURE-023: Application Action Tracing - Core
|
|
3
|
+
|
|
4
|
+
TracingService for managing tracing configuration and lifecycle.
|
|
5
|
+
|
|
6
|
+
Provides high-level API for starting/stopping tracing, reading
|
|
7
|
+
configuration from tools.json, and cleaning up old log files.
|
|
8
|
+
"""
|
|
9
|
+
from datetime import datetime, timedelta, timezone
|
|
10
|
+
from typing import Dict, List, Any, Optional
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from x_ipe.services.tools_config_service import ToolsConfigService
|
|
14
|
+
from x_ipe.tracing.writer import TraceLogWriter
|
|
15
|
+
from x_ipe.tracing.parser import TraceLogParser
|
|
16
|
+
from x_ipe.tracing import x_ipe_tracing
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TracingService:
|
|
20
|
+
"""
|
|
21
|
+
Service for managing tracing configuration and lifecycle.
|
|
22
|
+
|
|
23
|
+
Integrates with tools.json for configuration persistence and
|
|
24
|
+
provides methods for controlling tracing state.
|
|
25
|
+
|
|
26
|
+
Usage:
|
|
27
|
+
service = TracingService("/path/to/project")
|
|
28
|
+
|
|
29
|
+
# Check status
|
|
30
|
+
config = service.get_config()
|
|
31
|
+
is_active = service.is_active()
|
|
32
|
+
|
|
33
|
+
# Control tracing
|
|
34
|
+
service.start(duration_minutes=15)
|
|
35
|
+
service.stop()
|
|
36
|
+
|
|
37
|
+
# List/cleanup logs
|
|
38
|
+
logs = service.list_logs()
|
|
39
|
+
deleted = service.cleanup_on_startup()
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, project_root: str):
|
|
43
|
+
"""
|
|
44
|
+
Initialize TracingService.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
project_root: Path to the project root directory
|
|
48
|
+
"""
|
|
49
|
+
self.project_root = Path(project_root)
|
|
50
|
+
self.tools_config = ToolsConfigService(str(project_root))
|
|
51
|
+
|
|
52
|
+
@x_ipe_tracing()
|
|
53
|
+
def get_config(self) -> Dict[str, Any]:
|
|
54
|
+
"""
|
|
55
|
+
Get current tracing configuration.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Dictionary with tracing settings:
|
|
59
|
+
- enabled: bool
|
|
60
|
+
- stop_at: str or None (ISO timestamp)
|
|
61
|
+
- log_path: str
|
|
62
|
+
- retention_hours: int
|
|
63
|
+
- ignored_apis: list
|
|
64
|
+
"""
|
|
65
|
+
config = self.tools_config.load()
|
|
66
|
+
return {
|
|
67
|
+
"enabled": config.get("tracing_enabled", False),
|
|
68
|
+
"stop_at": config.get("tracing_stop_at"),
|
|
69
|
+
"log_path": config.get("tracing_log_path", "instance/traces/"),
|
|
70
|
+
"retention_hours": config.get("tracing_retention_hours", 24),
|
|
71
|
+
"ignored_apis": config.get("tracing_ignored_apis", [])
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@x_ipe_tracing()
|
|
75
|
+
def is_active(self) -> bool:
|
|
76
|
+
"""
|
|
77
|
+
Check if tracing is currently active.
|
|
78
|
+
|
|
79
|
+
Tracing is active if:
|
|
80
|
+
- tracing_enabled is True, OR
|
|
81
|
+
- tracing_stop_at is set and in the future
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
True if tracing should be performed
|
|
85
|
+
"""
|
|
86
|
+
config = self.get_config()
|
|
87
|
+
|
|
88
|
+
if config["enabled"]:
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
stop_at = config["stop_at"]
|
|
92
|
+
if stop_at:
|
|
93
|
+
try:
|
|
94
|
+
# Parse ISO timestamp - keep it timezone-aware
|
|
95
|
+
stop_time = datetime.fromisoformat(
|
|
96
|
+
stop_at.replace("Z", "+00:00")
|
|
97
|
+
)
|
|
98
|
+
return datetime.now(timezone.utc) < stop_time
|
|
99
|
+
except (ValueError, AttributeError):
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
@x_ipe_tracing()
|
|
105
|
+
def start(self, duration_minutes: int) -> Dict[str, Any]:
|
|
106
|
+
"""
|
|
107
|
+
Start tracing for specified duration.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
duration_minutes: Duration in minutes (must be 3, 15, or 30)
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Dictionary with success status and stop_at timestamp
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
ValueError: If duration is not 3, 15, or 30
|
|
117
|
+
"""
|
|
118
|
+
if duration_minutes not in [3, 15, 30]:
|
|
119
|
+
raise ValueError("Duration must be 3, 15, or 30 minutes")
|
|
120
|
+
|
|
121
|
+
stop_at = datetime.now(timezone.utc) + timedelta(minutes=duration_minutes)
|
|
122
|
+
# Convert +00:00 to Z for consistent format
|
|
123
|
+
stop_at_str = stop_at.isoformat().replace("+00:00", "Z")
|
|
124
|
+
|
|
125
|
+
config = self.tools_config.load()
|
|
126
|
+
config["tracing_stop_at"] = stop_at_str
|
|
127
|
+
self.tools_config.save(config)
|
|
128
|
+
|
|
129
|
+
return {"success": True, "stop_at": stop_at_str}
|
|
130
|
+
|
|
131
|
+
@x_ipe_tracing()
|
|
132
|
+
def stop(self) -> Dict[str, Any]:
|
|
133
|
+
"""
|
|
134
|
+
Stop tracing immediately.
|
|
135
|
+
|
|
136
|
+
Clears tracing_stop_at and sets tracing_enabled to False.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Dictionary with success status
|
|
140
|
+
"""
|
|
141
|
+
config = self.tools_config.load()
|
|
142
|
+
config["tracing_stop_at"] = None
|
|
143
|
+
config["tracing_enabled"] = False
|
|
144
|
+
self.tools_config.save(config)
|
|
145
|
+
|
|
146
|
+
return {"success": True}
|
|
147
|
+
|
|
148
|
+
@x_ipe_tracing()
|
|
149
|
+
def update_ignored_apis(self, patterns: List[str]) -> None:
|
|
150
|
+
"""
|
|
151
|
+
Update the list of ignored API patterns.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
patterns: List of API path patterns to ignore
|
|
155
|
+
"""
|
|
156
|
+
config = self.tools_config.load()
|
|
157
|
+
config["tracing_ignored_apis"] = patterns
|
|
158
|
+
self.tools_config.save(config)
|
|
159
|
+
|
|
160
|
+
@x_ipe_tracing()
|
|
161
|
+
def list_logs(self) -> List[Dict[str, Any]]:
|
|
162
|
+
"""
|
|
163
|
+
List all trace log files.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
List of log file metadata dictionaries:
|
|
167
|
+
- trace_id: str
|
|
168
|
+
- api: str (e.g., "GET /api/project/structure")
|
|
169
|
+
- filename: str
|
|
170
|
+
- size: int (bytes)
|
|
171
|
+
- timestamp: str (ISO format)
|
|
172
|
+
"""
|
|
173
|
+
config = self.get_config()
|
|
174
|
+
log_path = self.project_root / config["log_path"]
|
|
175
|
+
|
|
176
|
+
if not log_path.exists():
|
|
177
|
+
return []
|
|
178
|
+
|
|
179
|
+
logs = []
|
|
180
|
+
for filepath in sorted(log_path.glob("*.log"), reverse=True):
|
|
181
|
+
try:
|
|
182
|
+
# Parse filename: {timestamp}-{api}-{trace_id}.log
|
|
183
|
+
# Example: 20260202-072505-get-api-project-structure-a649c048-3d73.log
|
|
184
|
+
stem = filepath.stem
|
|
185
|
+
|
|
186
|
+
# Extract trace_id (last 2 UUID segments: xxxxxxxx-xxxx)
|
|
187
|
+
# Split and find the UUID pattern at the end
|
|
188
|
+
parts = stem.split("-")
|
|
189
|
+
if len(parts) >= 4:
|
|
190
|
+
# Last 2 parts form the trace_id (e.g., "a649c048-3d73")
|
|
191
|
+
trace_id = f"{parts[-2]}-{parts[-1]}"
|
|
192
|
+
# First 2 parts are timestamp (YYYYMMDD-HHMMSS)
|
|
193
|
+
# Middle parts are the API name
|
|
194
|
+
api_parts = parts[2:-2] # Skip timestamp and trace_id
|
|
195
|
+
api_name = "-".join(api_parts) if api_parts else "unknown"
|
|
196
|
+
# Convert api_name back to path format (e.g., "get-api-project-structure" -> "GET /api/project/structure")
|
|
197
|
+
api = self._filename_to_api(api_name)
|
|
198
|
+
else:
|
|
199
|
+
trace_id = stem
|
|
200
|
+
api = "/unknown"
|
|
201
|
+
|
|
202
|
+
stat = filepath.stat()
|
|
203
|
+
logs.append({
|
|
204
|
+
"trace_id": trace_id,
|
|
205
|
+
"api": api,
|
|
206
|
+
"filename": filepath.name,
|
|
207
|
+
"size": stat.st_size,
|
|
208
|
+
"timestamp": datetime.fromtimestamp(stat.st_mtime).isoformat()
|
|
209
|
+
})
|
|
210
|
+
except OSError:
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
return logs
|
|
214
|
+
|
|
215
|
+
def _filename_to_api(self, api_name: str) -> str:
|
|
216
|
+
"""
|
|
217
|
+
Convert sanitized API filename component back to API format.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
api_name: Sanitized name (e.g., "get-api-project-structure")
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
API string (e.g., "GET /api/project/structure")
|
|
224
|
+
"""
|
|
225
|
+
if not api_name or api_name == "unknown":
|
|
226
|
+
return "/unknown"
|
|
227
|
+
|
|
228
|
+
# Split by first hyphen to get method
|
|
229
|
+
parts = api_name.split("-", 1)
|
|
230
|
+
if len(parts) < 2:
|
|
231
|
+
return f"/{api_name}"
|
|
232
|
+
|
|
233
|
+
method = parts[0].upper()
|
|
234
|
+
path_part = parts[1]
|
|
235
|
+
|
|
236
|
+
# Convert hyphens back to slashes for path
|
|
237
|
+
path = "/" + path_part.replace("-", "/")
|
|
238
|
+
|
|
239
|
+
return f"{method} {path}"
|
|
240
|
+
|
|
241
|
+
@x_ipe_tracing()
|
|
242
|
+
def cleanup_on_startup(self) -> int:
|
|
243
|
+
"""
|
|
244
|
+
Clean up old log files on backend startup.
|
|
245
|
+
|
|
246
|
+
Uses retention_hours from configuration to determine
|
|
247
|
+
which files to delete.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Number of files deleted
|
|
251
|
+
"""
|
|
252
|
+
config = self.get_config()
|
|
253
|
+
log_path = self.project_root / config["log_path"]
|
|
254
|
+
|
|
255
|
+
writer = TraceLogWriter(str(log_path))
|
|
256
|
+
deleted = writer.cleanup(config["retention_hours"])
|
|
257
|
+
|
|
258
|
+
if deleted > 0:
|
|
259
|
+
print(f"[TRACING] Cleaned up {deleted} old trace log(s)")
|
|
260
|
+
|
|
261
|
+
return deleted
|
|
262
|
+
|
|
263
|
+
@x_ipe_tracing()
|
|
264
|
+
def delete_all_logs(self) -> int:
|
|
265
|
+
"""
|
|
266
|
+
Delete all trace log files.
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Number of files deleted
|
|
270
|
+
"""
|
|
271
|
+
config = self.get_config()
|
|
272
|
+
log_path = self.project_root / config["log_path"]
|
|
273
|
+
|
|
274
|
+
if not log_path.exists():
|
|
275
|
+
return 0
|
|
276
|
+
|
|
277
|
+
deleted = 0
|
|
278
|
+
for filepath in log_path.glob("*.log"):
|
|
279
|
+
try:
|
|
280
|
+
filepath.unlink()
|
|
281
|
+
deleted += 1
|
|
282
|
+
except OSError:
|
|
283
|
+
continue
|
|
284
|
+
|
|
285
|
+
return deleted
|
|
286
|
+
|
|
287
|
+
@x_ipe_tracing()
|
|
288
|
+
def get_trace(self, trace_id: str) -> Optional[Dict[str, Any]]:
|
|
289
|
+
"""
|
|
290
|
+
Get parsed trace data for visualization.
|
|
291
|
+
|
|
292
|
+
Searches for a log file matching the trace_id (exact or partial)
|
|
293
|
+
and parses it into visualization-ready structure.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
trace_id: Full or partial trace ID to search for
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
Parsed trace data or None if not found:
|
|
300
|
+
{
|
|
301
|
+
"trace_id": str,
|
|
302
|
+
"api": str,
|
|
303
|
+
"timestamp": str,
|
|
304
|
+
"total_time_ms": int,
|
|
305
|
+
"status": str,
|
|
306
|
+
"nodes": [...],
|
|
307
|
+
"edges": [...]
|
|
308
|
+
}
|
|
309
|
+
"""
|
|
310
|
+
config = self.get_config()
|
|
311
|
+
log_path = self.project_root / config["log_path"]
|
|
312
|
+
|
|
313
|
+
if not log_path.exists():
|
|
314
|
+
return None
|
|
315
|
+
|
|
316
|
+
# Search for matching file
|
|
317
|
+
matching_file = None
|
|
318
|
+
for filepath in log_path.glob("*.log"):
|
|
319
|
+
if trace_id in filepath.stem:
|
|
320
|
+
matching_file = filepath
|
|
321
|
+
break
|
|
322
|
+
|
|
323
|
+
if not matching_file:
|
|
324
|
+
return None
|
|
325
|
+
|
|
326
|
+
# Parse the file
|
|
327
|
+
parser = TraceLogParser()
|
|
328
|
+
result = parser.parse(matching_file)
|
|
329
|
+
|
|
330
|
+
# Add filename for reference
|
|
331
|
+
result["filename"] = matching_file.name
|
|
332
|
+
|
|
333
|
+
return result
|
|
@@ -9,6 +9,8 @@ import re
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from datetime import datetime, timedelta
|
|
11
11
|
|
|
12
|
+
from x_ipe.tracing import x_ipe_tracing
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
class UiuxFeedbackService:
|
|
14
16
|
"""Service for saving UI/UX feedback to file system"""
|
|
@@ -17,6 +19,7 @@ class UiuxFeedbackService:
|
|
|
17
19
|
self.project_root = Path(project_root)
|
|
18
20
|
self.feedback_dir = self.project_root / 'x-ipe-docs' / 'uiux-feedback'
|
|
19
21
|
|
|
22
|
+
@x_ipe_tracing()
|
|
20
23
|
def list_feedback(self, days: int = 2) -> list:
|
|
21
24
|
"""
|
|
22
25
|
List feedback entries from the last N days.
|
|
@@ -99,6 +102,34 @@ class UiuxFeedbackService:
|
|
|
99
102
|
except Exception:
|
|
100
103
|
return None
|
|
101
104
|
|
|
105
|
+
@x_ipe_tracing()
|
|
106
|
+
def delete_feedback(self, feedback_id: str) -> dict:
|
|
107
|
+
"""
|
|
108
|
+
Delete a feedback folder by ID.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
feedback_id: The feedback folder name/ID
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
dict with success (or error on failure)
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
folder_path = self.feedback_dir / feedback_id
|
|
118
|
+
|
|
119
|
+
if not folder_path.exists():
|
|
120
|
+
return {'success': False, 'error': 'Feedback not found'}
|
|
121
|
+
|
|
122
|
+
if not folder_path.is_dir():
|
|
123
|
+
return {'success': False, 'error': 'Invalid feedback ID'}
|
|
124
|
+
|
|
125
|
+
# Delete the folder
|
|
126
|
+
shutil.rmtree(folder_path)
|
|
127
|
+
|
|
128
|
+
return {'success': True}
|
|
129
|
+
except Exception as e:
|
|
130
|
+
return {'success': False, 'error': str(e)}
|
|
131
|
+
|
|
132
|
+
@x_ipe_tracing()
|
|
102
133
|
def cleanup_old_feedback(self, days: int = 7) -> int:
|
|
103
134
|
"""
|
|
104
135
|
Delete feedback folders older than N days.
|
|
@@ -130,6 +161,7 @@ class UiuxFeedbackService:
|
|
|
130
161
|
|
|
131
162
|
return deleted
|
|
132
163
|
|
|
164
|
+
@x_ipe_tracing()
|
|
133
165
|
def save_feedback(self, data: dict) -> dict:
|
|
134
166
|
"""
|
|
135
167
|
Save feedback entry to file system.
|
|
@@ -24,6 +24,8 @@ from dashscope.audio.asr import (
|
|
|
24
24
|
TranslationResult,
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
+
from x_ipe.tracing import x_ipe_tracing
|
|
28
|
+
|
|
27
29
|
|
|
28
30
|
# Constants
|
|
29
31
|
VOICE_MAX_DURATION = 30 # Maximum recording duration in seconds
|
|
@@ -225,6 +227,7 @@ class VoiceInputService:
|
|
|
225
227
|
self.sessions: Dict[str, VoiceSession] = {}
|
|
226
228
|
print(f"[Voice] VoiceInputService initialized")
|
|
227
229
|
|
|
230
|
+
@x_ipe_tracing()
|
|
228
231
|
def create_session(
|
|
229
232
|
self,
|
|
230
233
|
socket_sid: str,
|
|
@@ -260,10 +263,12 @@ class VoiceInputService:
|
|
|
260
263
|
print(f"[Voice] 📦 Session created: {session_id} for socket {socket_sid}")
|
|
261
264
|
return session_id
|
|
262
265
|
|
|
266
|
+
@x_ipe_tracing()
|
|
263
267
|
def get_session(self, session_id: str) -> Optional[VoiceSession]:
|
|
264
268
|
"""Get session by ID."""
|
|
265
269
|
return self.sessions.get(session_id)
|
|
266
270
|
|
|
271
|
+
@x_ipe_tracing()
|
|
267
272
|
def start_recognition(self, session_id: str) -> bool:
|
|
268
273
|
"""
|
|
269
274
|
Start speech recognition for a session.
|
|
@@ -333,6 +338,7 @@ class VoiceInputService:
|
|
|
333
338
|
session.on_error(str(e))
|
|
334
339
|
return False
|
|
335
340
|
|
|
341
|
+
@x_ipe_tracing()
|
|
336
342
|
def send_audio(self, session_id: str, audio_data: bytes) -> None:
|
|
337
343
|
"""
|
|
338
344
|
Send audio data to recognizer.
|
|
@@ -366,6 +372,7 @@ class VoiceInputService:
|
|
|
366
372
|
else:
|
|
367
373
|
print(f"[Voice] ⚠️ send_audio: no recognizer for session {session_id}")
|
|
368
374
|
|
|
375
|
+
@x_ipe_tracing()
|
|
369
376
|
def stop_recognition(self, session_id: str) -> str:
|
|
370
377
|
"""
|
|
371
378
|
Stop recognition and get final result.
|
|
@@ -409,6 +416,7 @@ class VoiceInputService:
|
|
|
409
416
|
print(f"[Voice] ✅ Recognition stopped, transcription result: '{result}'")
|
|
410
417
|
return result
|
|
411
418
|
|
|
419
|
+
@x_ipe_tracing()
|
|
412
420
|
def cancel_recognition(self, session_id: str) -> None:
|
|
413
421
|
"""
|
|
414
422
|
Cancel recognition without getting result.
|
|
@@ -434,6 +442,7 @@ class VoiceInputService:
|
|
|
434
442
|
session.partial_text = ""
|
|
435
443
|
session.translation_text = ""
|
|
436
444
|
|
|
445
|
+
@x_ipe_tracing()
|
|
437
446
|
def remove_session(self, session_id: str) -> None:
|
|
438
447
|
"""
|
|
439
448
|
Remove a session.
|
|
@@ -448,6 +457,7 @@ class VoiceInputService:
|
|
|
448
457
|
except Exception:
|
|
449
458
|
pass
|
|
450
459
|
|
|
460
|
+
@x_ipe_tracing()
|
|
451
461
|
def is_session_expired(self, session_id: str) -> bool:
|
|
452
462
|
"""
|
|
453
463
|
Check if session has exceeded max duration.
|
|
@@ -465,6 +475,7 @@ class VoiceInputService:
|
|
|
465
475
|
elapsed = (datetime.now() - session.created_at).total_seconds()
|
|
466
476
|
return elapsed > VOICE_MAX_DURATION
|
|
467
477
|
|
|
478
|
+
@x_ipe_tracing()
|
|
468
479
|
def process_transcription(self, session_id: str, text: str) -> Optional[str]:
|
|
469
480
|
"""
|
|
470
481
|
Process transcription result.
|
x_ipe/static/css/base.css
CHANGED