Compare commits
3 commits
main
...
fix/factor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2fd5ef1e3 | ||
|
|
0517158812 | ||
|
|
0dc3a5e901 |
2 changed files with 151 additions and 12 deletions
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
from typing import Literal, Any, Optional
|
from typing import Literal, Any, Optional
|
||||||
|
|
||||||
# Define button variant type
|
# Define button variant type
|
||||||
|
|
@ -20,6 +22,7 @@ from ..managers.docling_manager import DoclingManager
|
||||||
from ..utils.platform import RuntimeType
|
from ..utils.platform import RuntimeType
|
||||||
from ..widgets.command_modal import CommandOutputModal
|
from ..widgets.command_modal import CommandOutputModal
|
||||||
from ..widgets.flow_backup_warning_modal import FlowBackupWarningModal
|
from ..widgets.flow_backup_warning_modal import FlowBackupWarningModal
|
||||||
|
from ..widgets.factory_reset_warning_modal import FactoryResetWarningModal
|
||||||
from ..widgets.version_mismatch_warning_modal import VersionMismatchWarningModal
|
from ..widgets.version_mismatch_warning_modal import VersionMismatchWarningModal
|
||||||
from ..widgets.upgrade_instructions_modal import UpgradeInstructionsModal
|
from ..widgets.upgrade_instructions_modal import UpgradeInstructionsModal
|
||||||
from ..widgets.diagnostics_notification import notify_with_diagnostics
|
from ..widgets.diagnostics_notification import notify_with_diagnostics
|
||||||
|
|
@ -34,7 +37,7 @@ class MonitorScreen(Screen):
|
||||||
("s", "start", "Start Services"),
|
("s", "start", "Start Services"),
|
||||||
("t", "stop", "Stop Services"),
|
("t", "stop", "Stop Services"),
|
||||||
("u", "upgrade", "Upgrade"),
|
("u", "upgrade", "Upgrade"),
|
||||||
("x", "reset", "Reset"),
|
("x", "reset", "Factory Reset"),
|
||||||
("l", "logs", "View Logs"),
|
("l", "logs", "View Logs"),
|
||||||
("g", "toggle_mode", "Toggle GPU/CPU"),
|
("g", "toggle_mode", "Toggle GPU/CPU"),
|
||||||
("j", "cursor_down", "Move Down"),
|
("j", "cursor_down", "Move Down"),
|
||||||
|
|
@ -63,13 +66,6 @@ class MonitorScreen(Screen):
|
||||||
self._container_manager = self.app.container_manager
|
self._container_manager = self.app.container_manager
|
||||||
return self._container_manager
|
return self._container_manager
|
||||||
|
|
||||||
def on_unmount(self) -> None:
|
|
||||||
"""Clean up when the screen is unmounted."""
|
|
||||||
if hasattr(self, "docling_manager"):
|
|
||||||
self.docling_manager.cleanup()
|
|
||||||
super().on_unmount()
|
|
||||||
self._follow_service = None
|
|
||||||
self._logs_buffer = []
|
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
"""Create the monitoring screen layout."""
|
"""Create the monitoring screen layout."""
|
||||||
|
|
@ -164,6 +160,12 @@ class MonitorScreen(Screen):
|
||||||
self.refresh_timer.stop()
|
self.refresh_timer.stop()
|
||||||
# Stop following logs if running
|
# Stop following logs if running
|
||||||
self._stop_follow()
|
self._stop_follow()
|
||||||
|
# Clean up docling manager
|
||||||
|
if hasattr(self, "docling_manager"):
|
||||||
|
self.docling_manager.cleanup()
|
||||||
|
# Reset follow state (already done in _stop_follow, but ensure clean state)
|
||||||
|
self._follow_service = None
|
||||||
|
self._logs_buffer = []
|
||||||
|
|
||||||
async def on_screen_resume(self) -> None:
|
async def on_screen_resume(self) -> None:
|
||||||
"""Called when the screen is resumed (e.g., after a modal is closed)."""
|
"""Called when the screen is resumed (e.g., after a modal is closed)."""
|
||||||
|
|
@ -455,9 +457,17 @@ class MonitorScreen(Screen):
|
||||||
self.operation_in_progress = False
|
self.operation_in_progress = False
|
||||||
|
|
||||||
async def _reset_services(self) -> None:
|
async def _reset_services(self) -> None:
|
||||||
"""Reset services with progress updates."""
|
"""Factory reset: clear config and opensearch-data, then reset services."""
|
||||||
self.operation_in_progress = True
|
self.operation_in_progress = True
|
||||||
try:
|
try:
|
||||||
|
# Show factory reset warning modal first
|
||||||
|
should_continue = await self.app.push_screen_wait(
|
||||||
|
FactoryResetWarningModal()
|
||||||
|
)
|
||||||
|
if not should_continue:
|
||||||
|
self.notify("Factory reset cancelled", severity="information")
|
||||||
|
return
|
||||||
|
|
||||||
# Check for flow backups before resetting
|
# Check for flow backups before resetting
|
||||||
if self._check_flow_backups():
|
if self._check_flow_backups():
|
||||||
# Show warning modal and wait for user decision
|
# Show warning modal and wait for user decision
|
||||||
|
|
@ -465,13 +475,40 @@ class MonitorScreen(Screen):
|
||||||
FlowBackupWarningModal(operation="reset")
|
FlowBackupWarningModal(operation="reset")
|
||||||
)
|
)
|
||||||
if not should_continue:
|
if not should_continue:
|
||||||
self.notify("Reset cancelled", severity="information")
|
self.notify("Factory reset cancelled", severity="information")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Clear config, opensearch-data folders, and conversations.json
|
||||||
|
try:
|
||||||
|
config_path = Path("config")
|
||||||
|
opensearch_data_path = Path("opensearch-data")
|
||||||
|
conversations_file = Path("conversations.json")
|
||||||
|
|
||||||
|
if config_path.exists():
|
||||||
|
shutil.rmtree(config_path)
|
||||||
|
# Recreate empty config directory
|
||||||
|
config_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
if opensearch_data_path.exists():
|
||||||
|
shutil.rmtree(opensearch_data_path)
|
||||||
|
# Recreate empty opensearch-data directory
|
||||||
|
opensearch_data_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
if conversations_file.exists():
|
||||||
|
conversations_file.unlink()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.notify(
|
||||||
|
f"Error clearing folders: {str(e)}",
|
||||||
|
severity="error",
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
# Show command output in modal dialog
|
# Show command output in modal dialog
|
||||||
command_generator = self.container_manager.reset_services()
|
command_generator = self.container_manager.reset_services()
|
||||||
modal = CommandOutputModal(
|
modal = CommandOutputModal(
|
||||||
"Resetting Services",
|
"Factory Resetting Services",
|
||||||
command_generator,
|
command_generator,
|
||||||
on_complete=None, # We'll refresh in on_screen_resume instead
|
on_complete=None, # We'll refresh in on_screen_resume instead
|
||||||
)
|
)
|
||||||
|
|
@ -774,7 +811,7 @@ class MonitorScreen(Screen):
|
||||||
controls.mount(
|
controls.mount(
|
||||||
Button("Upgrade", variant="warning", id=f"upgrade-btn{suffix}")
|
Button("Upgrade", variant="warning", id=f"upgrade-btn{suffix}")
|
||||||
)
|
)
|
||||||
controls.mount(Button("Reset", variant="error", id=f"reset-btn{suffix}"))
|
controls.mount(Button("Factory Reset", variant="error", id=f"reset-btn{suffix}"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
notify_with_diagnostics(
|
notify_with_diagnostics(
|
||||||
|
|
|
||||||
102
src/tui/widgets/factory_reset_warning_modal.py
Normal file
102
src/tui/widgets/factory_reset_warning_modal.py
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
"""Factory reset warning modal for OpenRAG TUI."""
|
||||||
|
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.containers import Container, Horizontal
|
||||||
|
from textual.screen import ModalScreen
|
||||||
|
from textual.widgets import Button, Static, Label
|
||||||
|
|
||||||
|
|
||||||
|
class FactoryResetWarningModal(ModalScreen[bool]):
|
||||||
|
"""Modal dialog to warn about factory reset consequences."""
|
||||||
|
|
||||||
|
DEFAULT_CSS = """
|
||||||
|
FactoryResetWarningModal {
|
||||||
|
align: center middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dialog {
|
||||||
|
width: 70;
|
||||||
|
height: auto;
|
||||||
|
border: solid #3f3f46;
|
||||||
|
background: #27272a;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title {
|
||||||
|
background: #dc2626;
|
||||||
|
color: #fafafa;
|
||||||
|
padding: 1 2;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
text-style: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#message {
|
||||||
|
padding: 2;
|
||||||
|
color: #fafafa;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#button-row {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
align: center middle;
|
||||||
|
padding: 1;
|
||||||
|
margin-top: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#button-row Button {
|
||||||
|
margin: 0 1;
|
||||||
|
min-width: 16;
|
||||||
|
background: #27272a;
|
||||||
|
color: #fafafa;
|
||||||
|
border: round #52525b;
|
||||||
|
text-style: none;
|
||||||
|
tint: transparent 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#button-row Button:hover {
|
||||||
|
background: #27272a !important;
|
||||||
|
color: #fafafa !important;
|
||||||
|
border: round #52525b;
|
||||||
|
tint: transparent 0%;
|
||||||
|
text-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#button-row Button:focus {
|
||||||
|
background: #27272a !important;
|
||||||
|
color: #fafafa !important;
|
||||||
|
border: round #ec4899;
|
||||||
|
tint: transparent 0%;
|
||||||
|
text-style: none;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
"""Create the modal dialog layout."""
|
||||||
|
with Container(id="dialog"):
|
||||||
|
yield Label("⚠ Factory Reset Warning", id="title")
|
||||||
|
yield Static(
|
||||||
|
"This action will permanently delete:\n\n"
|
||||||
|
"• All ingested knowledge and documents\n"
|
||||||
|
"• All conversation history\n"
|
||||||
|
"• All provider settings and configuration\n\n"
|
||||||
|
"This cannot be undone.\n\n"
|
||||||
|
"Do you want to continue?",
|
||||||
|
id="message"
|
||||||
|
)
|
||||||
|
with Horizontal(id="button-row"):
|
||||||
|
yield Button("Cancel", id="cancel-btn")
|
||||||
|
yield Button("Factory Reset", id="continue-btn", variant="error")
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
"""Focus the cancel button by default for safety."""
|
||||||
|
self.query_one("#cancel-btn", Button).focus()
|
||||||
|
|
||||||
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
|
"""Handle button presses."""
|
||||||
|
if event.button.id == "continue-btn":
|
||||||
|
self.dismiss(True) # User wants to continue
|
||||||
|
else:
|
||||||
|
self.dismiss(False) # User cancelled
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue