From ce0e022f112c9b14cc287af088010d9d15f7f3c2 Mon Sep 17 00:00:00 2001 From: Christopher Clendening <10941493+Netherwarlord@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:40:57 -0400 Subject: [PATCH] Remove unused modules and dialogs to streamline the application --- diff_viewer.py | 42 ---- index.py | 489 ------------------------------------------- model_downloader.py | 193 ----------------- prompt_playground.py | 39 ---- results_chart.py | 46 ---- settings_dialog.py | 135 ------------ test_editor.py | 34 --- 7 files changed, 978 deletions(-) delete mode 100644 diff_viewer.py delete mode 100644 index.py delete mode 100644 model_downloader.py delete mode 100644 prompt_playground.py delete mode 100644 results_chart.py delete mode 100644 settings_dialog.py delete mode 100644 test_editor.py diff --git a/diff_viewer.py b/diff_viewer.py deleted file mode 100644 index 4ecadd2..0000000 --- a/diff_viewer.py +++ /dev/null @@ -1,42 +0,0 @@ -import tkinter as tk -from tkinter import ttk -import difflib - -class DiffViewerDialog(tk.Toplevel): - def __init__(self, parent, expected: str, actual: str, title: str = "Output Diff"): - super().__init__(parent) - self.title(title) - self.geometry("900x600") - self.transient(parent) - self.grab_set() - - paned = ttk.PanedWindow(self, orient=tk.HORIZONTAL) - paned.pack(fill="both", expand=True, padx=10, pady=10) - - left_frame = ttk.Frame(paned) - right_frame = ttk.Frame(paned) - paned.add(left_frame, weight=1) - paned.add(right_frame, weight=1) - - ttk.Label(left_frame, text="Expected Output", font=("Helvetica", 12, "bold")).pack(anchor="w") - self.expected_text = tk.Text(left_frame, wrap="word", bg="#f8f8f8") - self.expected_text.pack(fill="both", expand=True) - self.expected_text.insert("1.0", expected) - self.expected_text.configure(state="disabled") - - ttk.Label(right_frame, text="Actual Output", font=("Helvetica", 12, "bold")).pack(anchor="w") - self.actual_text = tk.Text(right_frame, wrap="word", bg="#f8f8f8") - self.actual_text.pack(fill="both", expand=True) - self.actual_text.insert("1.0", actual) - self.actual_text.configure(state="disabled") - - # Diff summary at the bottom - diff = list(difflib.unified_diff( - expected.splitlines(), actual.splitlines(), lineterm='', n=3, - fromfile='expected', tofile='actual')) - diff_str = '\n'.join(diff) if diff else 'No differences.' - ttk.Label(self, text="Diff Summary", font=("Helvetica", 11, "bold")).pack(anchor="w", padx=10) - diff_box = tk.Text(self, height=8, wrap="none", bg="#f0f0f0") - diff_box.pack(fill="x", padx=10, pady=(0,10)) - diff_box.insert("1.0", diff_str) - diff_box.configure(state="disabled") diff --git a/index.py b/index.py deleted file mode 100644 index ade07e8..0000000 --- a/index.py +++ /dev/null @@ -1,489 +0,0 @@ -from __future__ import annotations - -import os -import subprocess -import sys -import threading -import time -from pathlib import Path -from typing import Dict, List, Tuple -from tkinter import filedialog, messagebox, scrolledtext, ttk -import tkinter as tk - -CORE_DIR = Path(__file__).resolve().parent / ".core" -if str(CORE_DIR) not in sys.path: - sys.path.insert(0, str(CORE_DIR)) - -from config import RESULTS_DIR, TESTS_DIR # type: ignore # noqa: E402 -from providers import ( # type: ignore # noqa: E402 - LocalEngineProvider, - ProviderError, - get_provider, - list_provider_names, -) -from reporting import TemplateNotFoundError # type: ignore # noqa: E402 -from runner import TestRunError, run_suite # type: ignore # noqa: E402 -from settings import get_local_model_paths, set_local_model_paths # type: ignore # noqa: E402 - - -def open_path(path: Path) -> None: - if sys.platform.startswith("darwin"): - subprocess.run(["open", str(path)], check=False) - elif os.name == "nt": # Windows - os.startfile(str(path)) # type: ignore[attr-defined] - else: - subprocess.run(["xdg-open", str(path)], check=False) - - -class App(tk.Tk): - def open_settings_dialog(self): - try: - from settings_dialog import SettingsDialog - except ImportError: - messagebox.showerror("Error", "Settings dialog module not found.") - return - SettingsDialog(self) - def open_prompt_playground(self): - try: - from prompt_playground import PlaygroundDialog - except ImportError: - messagebox.showerror("Error", "Prompt playground module not found.") - return - def send_callback(prompt): - provider_name = self.provider_var.get() - model_display = self.model_var.get() - if not provider_name or not model_display: - return "Please select a provider and model." - try: - provider = get_provider(provider_name, **self._provider_kwargs(provider_name)) - model_id = self._model_id_for_display(model_display) - if not model_id: - return "Model not found." - # Use temperature from the GUI - response = provider.run_inference(model_id, prompt, temperature=self.temperature_var.get()) - return response - except Exception as e: - return f"Error: {e}" - PlaygroundDialog(self, send_callback) - def load_plugins(self): - try: - from plugin_system import PluginManager - self.plugin_manager = PluginManager() - self.plugin_manager.run_hook('on_app_start', self) - except Exception as e: - print(f"Plugin system error: {e}") - def open_results_chart(self): - try: - from results_chart import ResultsChartDialog - except ImportError: - messagebox.showerror("Error", "Results chart module not found.") - return - # Find the latest report - if not RESULTS_DIR.exists(): - messagebox.showinfo("No Reports", "The results directory does not exist yet.") - return - reports = list(RESULTS_DIR.glob("*.md")) - if not reports: - messagebox.showinfo("No Reports", "No markdown reports found yet.") - return - latest_report = max(reports, key=lambda p: p.stat().st_mtime) - ResultsChartDialog(self, latest_report) - def open_diff_viewer(self): - try: - from diff_viewer import DiffViewerDialog - except ImportError: - messagebox.showerror("Error", "Diff viewer module not found.") - return - # Try to get the last test's expected and actual output from the log or a result file - # For now, just prompt the user for both (can be improved to auto-load from last run) - expected = "" - actual = "" - def get_text(title): - d = tk.Toplevel(self) - d.title(f"{title} - LM-Gambit") - d.geometry("600x400") - t = tk.Text(d, wrap="word") - t.pack(fill="both", expand=True) - result = {} - def ok(): - result['text'] = t.get("1.0", "end-1c") - d.destroy() - ttk.Button(d, text="OK", command=ok).pack() - self.wait_window(d) - return result.get('text', "") - expected = get_text("Paste Expected Output") - actual = get_text("Paste Actual Output") - if expected and actual: - DiffViewerDialog(self, expected, actual) - else: - messagebox.showinfo("Diff Viewer", "Both expected and actual output are required.") - def configure_model_paths(self) -> None: - dialog = tk.Toplevel(self) - dialog.title("Configure Model Paths - LM-Gambit") - dialog.geometry("520x320") - dialog.transient(self) - dialog.grab_set() - - paths: List[str] = get_local_model_paths() - - listbox = tk.Listbox(dialog, selectmode=tk.SINGLE, width=60, height=10) - for entry in paths: - listbox.insert("end", entry) - listbox.pack(fill="both", expand=True, padx=12, pady=(12, 6)) - - button_frame = ttk.Frame(dialog) - button_frame.pack(fill="x", padx=12, pady=6) - - def add_path() -> None: - selection = filedialog.askdirectory(parent=dialog) - if selection and selection not in paths: - paths.append(selection) - listbox.insert("end", selection) - - def remove_path() -> None: - selection = listbox.curselection() - if not selection: - return - index = selection[0] - listbox.delete(index) - paths.pop(index) - - ttk.Button(button_frame, text="Add Path", command=add_path).pack(side="left", padx=(0, 8)) - ttk.Button(button_frame, text="Remove Selected", command=remove_path).pack(side="left") - ttk.Button(button_frame, text="Save", command=lambda: (set_local_model_paths(paths), dialog.destroy())).pack(side="right") - def open_test_editor(self): - try: - from test_editor import TestEditorDialog - except ImportError: - messagebox.showerror("Error", "Test editor module not found.") - return - TestEditorDialog(self, TESTS_DIR) - - def open_latest_report(self) -> None: - if not RESULTS_DIR.exists(): - messagebox.showinfo("No Reports", "The results directory does not exist yet.") - return - reports = list(RESULTS_DIR.glob("*.md")) - if not reports: - messagebox.showinfo("No Reports", "No markdown reports found yet.") - return - latest_report = max(reports, key=lambda p: p.stat().st_mtime) - open_path(latest_report) - self._append_log(f"Opened latest report: {latest_report.name}") - def open_model_downloader(self): - try: - from model_downloader import DownloadModelDialog - except ImportError: - messagebox.showerror("Error", "Model downloader module not found.") - return - paths = get_local_model_paths() - import os - model_dir = Path(paths[0]).expanduser() if paths else (Path(os.getcwd()) / "models") - model_dir.mkdir(parents=True, exist_ok=True) - DownloadModelDialog(self, model_dir) - def __init__(self) -> None: - super().__init__() - self.title("LM-Gambit v1.0.0") - self.geometry("880x620") - self.minsize(760, 520) - - self.provider_names = list_provider_names() - # Always default to Local Engine if available - default_provider = LocalEngineProvider.name if LocalEngineProvider.name in self.provider_names else (self.provider_names[0] if self.provider_names else "") - self.provider_var = tk.StringVar(value=default_provider) - self.model_var = tk.StringVar() - self.temperature_var = tk.DoubleVar(value=0.1) - self.status_var = tk.StringVar(value="Idle") - - self.available_models: List[Tuple[str, str]] = [] # (id, display name) - self.running = False - - self.load_plugins() - - self._build_ui() - self._build_menu() - if self.provider_var.get(): - self.fetch_models_async() - - def _build_ui(self) -> None: - padding = {"padx": 12, "pady": 8} - - header = ttk.Label(self, text="Automated Diagnostic Test Runner", font=("Helvetica", 18, "bold")) - header.pack(anchor="w", **padding) - - form_frame = ttk.Frame(self) - form_frame.pack(fill="x", **padding) - - ttk.Label(form_frame, text="Provider:").grid(row=0, column=0, sticky="w") - self.provider_combo = ttk.Combobox( - form_frame, - textvariable=self.provider_var, - values=self.provider_names, - state="readonly", - ) - self.provider_combo.grid(row=0, column=1, sticky="ew", padx=(6, 18)) - self.provider_combo.bind("<>", lambda event: self.fetch_models_async()) - - ttk.Label(form_frame, text="Model:").grid(row=0, column=2, sticky="w") - self.model_combo = ttk.Combobox(form_frame, textvariable=self.model_var, state="readonly") - self.model_combo.grid(row=0, column=3, sticky="ew", padx=(6, 18)) - - ttk.Label(form_frame, text="Temperature:").grid(row=0, column=4, sticky="w") - self.temperature_spin = ttk.Spinbox( - form_frame, - textvariable=self.temperature_var, - from_=0.0, - to=1.0, - increment=0.05, - width=6, - ) - self.temperature_spin.grid(row=0, column=5, sticky="w") - - form_frame.columnconfigure(1, weight=1) - form_frame.columnconfigure(3, weight=1) - - button_frame = ttk.Frame(self) - button_frame.pack(fill="x", **padding) - - self.run_button = ttk.Button(button_frame, text="Run Tests", command=self.start_run) - self.run_button.pack(side="left") - - ttk.Button(button_frame, text="Refresh Models", command=self.fetch_models_async).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="New Test", command=self.open_test_editor).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="Compare Outputs", command=self.open_diff_viewer).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="View Results Chart", command=self.open_results_chart).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="Prompt Playground", command=self.open_prompt_playground).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="Settings", command=self.open_settings_dialog).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="Download Model", command=self.open_model_downloader).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="Edit Tests", command=lambda: open_path(TESTS_DIR)).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="Open Results", command=lambda: open_path(RESULTS_DIR)).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="View Latest Report", command=self.open_latest_report).pack(side="left", padx=(12, 0)) - - ttk.Label(self, textvariable=self.status_var).pack(anchor="w", **padding) - - self.log = scrolledtext.ScrolledText(self, wrap="word", height=22) - self.log.pack(fill="both", expand=True, padx=12, pady=(0, 12)) - self.log.configure(state="disabled") - - def _build_menu(self) -> None: - menubar = tk.Menu(self) - settings_menu = tk.Menu(menubar, tearoff=0) - settings_menu.add_command(label="Settings…", command=self.open_settings_dialog) - menubar.add_cascade(label="Settings", menu=settings_menu) - self.config(menu=menubar) - self._settings_menu = settings_menu - - def fetch_models_async(self) -> None: - if self.running: - return - - provider_name = self.provider_var.get() - if not provider_name: - return - - self._set_status(f"Fetching models for {provider_name}…") - self.model_combo.configure(values=()) - self.model_var.set("") - - thread = threading.Thread(target=self._fetch_models, daemon=True) - thread.start() - - def _fetch_models(self) -> None: - provider_name = self.provider_var.get() - try: - provider = get_provider(provider_name, **self._provider_kwargs(provider_name)) - models = provider.list_models() - except ProviderError as exc: - message = str(exc) - self.after(0, lambda m=message: self._on_model_fetch_error(m)) - return - - model_pairs = [(model.id, model.display_name) for model in models] - self.after(0, lambda: self._on_models_loaded(model_pairs)) - - def open_test_editor(self): - try: - from test_editor import TestEditorDialog - except ImportError: - messagebox.showerror("Error", "Test editor module not found.") - return - TestEditorDialog(self, TESTS_DIR) - def _on_model_fetch_error(self, message: str) -> None: - self._set_status("Model fetch failed") - messagebox.showerror("Model Load Error", message) - - def _on_models_loaded(self, model_pairs: List[Tuple[str, str]]) -> None: - self.available_models = model_pairs - options = [display for _, display in model_pairs] - self.model_combo.configure(values=options) - if options: - self.model_var.set(options[0]) - self._set_status(f"Loaded {len(options)} models.") - else: - self.model_var.set("") - self._set_status("No models found.") - - def start_run(self) -> None: - if self.running: - return - - provider_name = self.provider_var.get() - model_display = self.model_var.get() - - if not provider_name: - messagebox.showwarning("Missing Provider", "Please select a provider before running tests.") - return - - if not model_display: - messagebox.showwarning("Missing Model", "Please select a model before running tests.") - return - - model_id = self._model_id_for_display(model_display) - if not model_id: - messagebox.showerror("Model Error", "Unable to resolve the selected model.") - return - - temperature = float(self.temperature_var.get()) - self.running = True - self._set_status("Running tests…") - self._append_log(f"Starting run with provider '{provider_name}' and model '{model_display}'.") - self._toggle_controls(state="disabled") - - thread = threading.Thread( - target=self._run_tests_thread, - args=(provider_name, model_id, temperature), - daemon=True, - ) - thread.start() - - def _run_tests_thread(self, provider_name: str, model_id: str, temperature: float) -> None: - start_time = time.time() - - def progress_callback(index: int, total: int, prompt: Dict[str, str], result: Dict[str, object]) -> None: - status = "FAILED" if "error" in result else "DONE" - title = prompt.get("title", f"Test {index}") - filename = prompt.get("filename", f"test{index}") - message = f"[{index}/{total}] {title} ({filename}) -> {status}" - self.after(0, lambda: self._append_log(message)) - - try: - report_path = run_suite( - provider_name=provider_name, - model_id=model_id, - temperature=temperature, - progress_callback=progress_callback, - ) - except (TestRunError, TemplateNotFoundError) as exc: - message = str(exc) - self.after(0, lambda m=message: self._handle_run_error(m)) - return - - elapsed = time.time() - start_time - self.after(0, lambda: self._handle_run_success(report_path, elapsed)) - - def _handle_run_error(self, message: str) -> None: - self.running = False - self._toggle_controls(state="normal") - self._set_status("Run failed") - self._append_log(f"Error: {message}") - messagebox.showerror("Run Failed", message) - - def _handle_run_success(self, report_path: Path, elapsed: float) -> None: - self.running = False - self._toggle_controls(state="normal") - self._set_status("Run complete") - self._append_log(f"Run complete in {elapsed:.2f}s. Report saved to {report_path.name}.") - messagebox.showinfo("Run Complete", f"Report saved to {report_path}") - - def _toggle_controls(self, *, state: str) -> None: - widgets = [ - self.run_button, - self.provider_combo, - self.model_combo, - self.temperature_spin, - ] - for widget in widgets: - widget.configure(state=state) - - def _append_log(self, message: str) -> None: - timestamp = time.strftime("%H:%M:%S") - self.log.configure(state="normal") - self.log.insert("end", f"[{timestamp}] {message}\n") - self.log.see("end") - self.log.configure(state="disabled") - - def _set_status(self, message: str) -> None: - self.status_var.set(message) - - def _model_id_for_display(self, display_name: str) -> str | None: - for model_id, display in self.available_models: - if display == display_name: - return model_id - return None - - def _provider_kwargs(self, provider_name: str) -> Dict[str, object]: - if provider_name == LocalEngineProvider.name: - custom_paths = [Path(p).expanduser() for p in get_local_model_paths()] - return {"search_paths": [path for path in custom_paths if path]} - return {} - - def _build_ui(self) -> None: - padding = {"padx": 12, "pady": 8} - - header = ttk.Label(self, text="Automated Diagnostic Test Runner", font=("Helvetica", 18, "bold")) - header.pack(anchor="w", **padding) - - form_frame = ttk.Frame(self) - form_frame.pack(fill="x", **padding) - - ttk.Label(form_frame, text="Provider:").grid(row=0, column=0, sticky="w") - self.provider_combo = ttk.Combobox( - form_frame, - textvariable=self.provider_var, - values=self.provider_names, - state="readonly", - ) - self.provider_combo.grid(row=0, column=1, sticky="ew", padx=(6, 18)) - self.provider_combo.bind("<>", lambda event: self.fetch_models_async()) - - ttk.Label(form_frame, text="Model:").grid(row=0, column=2, sticky="w") - self.model_combo = ttk.Combobox(form_frame, textvariable=self.model_var, state="readonly") - self.model_combo.grid(row=0, column=3, sticky="ew", padx=(6, 18)) - - ttk.Label(form_frame, text="Temperature:").grid(row=0, column=4, sticky="w") - self.temperature_spin = ttk.Spinbox( - form_frame, - textvariable=self.temperature_var, - from_=0.0, - to=1.0, - increment=0.05, - width=6, - ) - self.temperature_spin.grid(row=0, column=5, sticky="w") - - form_frame.columnconfigure(1, weight=1) - form_frame.columnconfigure(3, weight=1) - - button_frame = ttk.Frame(self) - button_frame.pack(fill="x", **padding) - - self.run_button = ttk.Button(button_frame, text="Run Tests", command=self.start_run) - self.run_button.pack(side="left") - - ttk.Button(button_frame, text="Refresh Models", command=self.fetch_models_async).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="New Test", command=self.open_test_editor).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="Download Model", command=self.open_model_downloader).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="Edit Tests", command=lambda: open_path(TESTS_DIR)).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="Open Results", command=lambda: open_path(RESULTS_DIR)).pack(side="left", padx=(12, 0)) - ttk.Button(button_frame, text="View Latest Report", command=self.open_latest_report).pack(side="left", padx=(12, 0)) - - ttk.Label(self, textvariable=self.status_var).pack(anchor="w", **padding) - - self.log = scrolledtext.ScrolledText(self, wrap="word", height=22) - self.log.pack(fill="both", expand=True, padx=12, pady=(0, 12)) - self.log.configure(state="disabled") - - -if __name__ == "__main__": - app = App() - app.mainloop() diff --git a/model_downloader.py b/model_downloader.py deleted file mode 100644 index 8cd11fd..0000000 --- a/model_downloader.py +++ /dev/null @@ -1,193 +0,0 @@ -import tkinter as tk -from tkinter import simpledialog, messagebox -from pathlib import Path -import urllib.request -import shutil -import threading - -class DownloadModelDialog(simpledialog.Dialog): - def __init__(self, parent, model_dir: Path): - self.model_dir = model_dir - self.active_downloads = {} # filename -> {progress, thread, cancel_flag} - super().__init__(parent, title="Download Model") - - - def body(self, master): - tk.Label(master, text="Model URL:").grid(row=0, column=0, sticky="w") - self.url_entry = tk.Entry(master, width=60) - self.url_entry.grid(row=0, column=1, padx=6, pady=4) - - # Browse Hugging Face button - browse_btn = tk.Button(master, text="Browse Hugging Face...", command=self.open_hf_browser, bg="#e0e0e0", fg="#111", width=22) - browse_btn.grid(row=1, column=0, columnspan=2, sticky="w", padx=(0, 0), pady=(2, 2)) - - # Comprehensive caption/instruction at the bottom - caption_text = ( - "Paste a direct download URL for a model file. " - "\n\n" - "Where to find models:\n" - "- Hugging Face: https://huggingface.co (search for GGUF or compatible models)\n" - "- Official model provider pages\n" - "\n" - "The URL should point directly to a downloadable file (e.g., .gguf, .bin).\n" - "Example: https://huggingface.co/ORG/MODEL/resolve/main/model-file.gguf\n" - "\n" - "Downloaded files will be saved to your selected model directory and will appear in the model selection list." - ) - caption = tk.Label( - master, - text=caption_text, - justify="left", - anchor="w", - wraplength=440, - fg="#fff", - bg="#444", - font=(None, 10, "italic") - ) - caption.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(16, 2)) - - # Download cards frame (below caption) - self.cards_frame = tk.Frame(master, bg="#222") - self.cards_frame.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(8, 2)) - self.refresh_cards() - return self.url_entry - def open_hf_browser(self): - # Popup window with embedded browser to Hugging Face - try: - from tkhtmlview import HtmlFrame - except ImportError: - messagebox.showerror("Missing Dependency", "tkhtmlview is required for the integrated browser. Please install it with 'pip install tkhtmlview'.") - return - popup = tk.Toplevel(self) - popup.title("Hugging Face Model Catalogue") - popup.geometry("1000x700") - # Info label - tk.Label(popup, text="Browse models. Right-click a download link and select 'Copy Link', then paste it in the URL field.", bg="#222", fg="#fff").pack(fill="x") - # Embedded browser (read-only, not full browser) - frame = HtmlFrame(popup, horizontal_scrollbar="auto", vertical_scrollbar="auto") - frame.pack(fill="both", expand=True) - frame.load_website("https://huggingface.co/models") - - # Comprehensive caption/instruction at the bottom - caption_text = ( - "Paste a direct download URL for a model file. " - "\n\n" - "Where to find models:\n" - "- Hugging Face: https://huggingface.co (search for GGUF or compatible models)\n" - "- Official model provider pages\n" - "\n" - "The URL should point directly to a downloadable file (e.g., .gguf, .bin).\n" - "Example: https://huggingface.co/ORG/MODEL/resolve/main/model-file.gguf\n" - "\n" - "Downloaded files will be saved to your selected model directory and will appear in the model selection list." - ) - caption = tk.Label( - master, - text=caption_text, - justify="left", - anchor="w", - wraplength=440, - fg="#fff", - bg="#444", - font=(None, 10, "italic") - ) - caption.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(16, 2)) - - # Download cards frame (below caption) - self.cards_frame = tk.Frame(master, bg="#222") - self.cards_frame.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(8, 2)) - self.refresh_cards() - return self.url_entry - - def refresh_cards(self): - # Clear previous cards - for widget in self.cards_frame.winfo_children(): - widget.destroy() - if not self.active_downloads: - placeholder = tk.Label( - self.cards_frame, - text="No downloads in progress.", - bg="#222", - fg="#aaa", - font=(None, 10, "italic") - ) - placeholder.pack(fill="x", pady=12) - else: - row = 0 - for fname, info in self.active_downloads.items(): - card = tk.Frame(self.cards_frame, bg="#333", bd=2, relief="groove") - card.grid(row=row, column=0, sticky="ew", pady=4, padx=2) - # File name - tk.Label(card, text=fname, bg="#333", fg="#fff", font=(None, 10, "bold")).pack(anchor="w", padx=8, pady=(4,0)) - bar_frame = tk.Frame(card, bg="#333") - bar_frame.pack(fill="x", padx=8, pady=(2,6)) - # Percentage label - percent_var = info.get('percent_var') - if not percent_var: - percent_var = tk.StringVar(value="0%") - info['percent_var'] = percent_var - percent_label = tk.Label(bar_frame, textvariable=percent_var, width=5, anchor="e", bg="#333", fg="#fff") - percent_label.pack(side="left") - # Progress bar - progress_var = info.get('progress_var') - if not progress_var: - progress_var = tk.DoubleVar(value=0) - info['progress_var'] = progress_var - bar = tk.ttk.Progressbar(bar_frame, variable=progress_var, maximum=100, length=220) - bar.pack(side="left", fill="x", expand=True, padx=(6,6)) - # Cancel button - cancel_btn = tk.Button(bar_frame, text="Cancel", command=lambda f=fname: self.cancel_download(f), bg="#a33", fg="#fff") - cancel_btn.pack(side="right", padx=(6,0)) - row += 1 - - def apply(self): - url = self.url_entry.get().strip() - if not url: - messagebox.showerror("Error", "Model URL is required.") - return - filename = url.split("/")[-1] - dest_path = self.model_dir / filename - cancel_flag = threading.Event() - info = { - 'progress_var': tk.DoubleVar(value=0), - 'percent_var': tk.StringVar(value="0%"), - 'cancel_flag': cancel_flag, - } - self.active_downloads[filename] = info - self.refresh_cards() - def download(): - try: - with urllib.request.urlopen(url) as response, open(dest_path, 'wb') as out_file: - total = int(response.getheader('Content-Length', 0)) - downloaded = 0 - chunk = 8192 - while True: - if cancel_flag.is_set(): - break - data = response.read(chunk) - if not data: - break - out_file.write(data) - downloaded += len(data) - percent = int((downloaded / total) * 100) if total else 0 - info['progress_var'].set(percent) - info['percent_var'].set(f"{percent}%") - self.cards_frame.after(50, self.refresh_cards) - if not cancel_flag.is_set(): - messagebox.showinfo("Success", f"Model downloaded to {dest_path}") - except Exception as e: - if not cancel_flag.is_set(): - messagebox.showerror("Download Failed", str(e)) - finally: - del self.active_downloads[filename] - self.cards_frame.after(100, self.refresh_cards) - t = threading.Thread(target=download, daemon=True) - info['thread'] = t - t.start() - - def cancel_download(self, filename): - info = self.active_downloads.get(filename) - if info: - info['cancel_flag'].set() - del self.active_downloads[filename] - self.refresh_cards() diff --git a/prompt_playground.py b/prompt_playground.py deleted file mode 100644 index 85851f4..0000000 --- a/prompt_playground.py +++ /dev/null @@ -1,39 +0,0 @@ -import tkinter as tk -from tkinter import ttk, scrolledtext, messagebox - -class PlaygroundDialog(tk.Toplevel): - def __init__(self, parent, send_callback): - super().__init__(parent) - self.title("Prompt Playground") - self.geometry("700x600") - self.transient(parent) - self.grab_set() - self.send_callback = send_callback - - ttk.Label(self, text="Prompt:").pack(anchor="w", padx=10, pady=(10,0)) - self.prompt_text = scrolledtext.ScrolledText(self, height=8, wrap="word") - self.prompt_text.pack(fill="x", padx=10, pady=(0,10)) - - ttk.Button(self, text="Send", command=self.send_prompt).pack(pady=(0,10)) - - ttk.Label(self, text="Response:").pack(anchor="w", padx=10) - self.response_text = scrolledtext.ScrolledText(self, height=18, wrap="word") - self.response_text.pack(fill="both", expand=True, padx=10, pady=(0,10)) - self.response_text.configure(state="disabled") - - def send_prompt(self): - prompt = self.prompt_text.get("1.0", "end-1c").strip() - if not prompt: - messagebox.showwarning("Prompt Required", "Please enter a prompt.") - return - self.response_text.configure(state="normal") - self.response_text.delete("1.0", "end") - self.response_text.insert("1.0", "Running...") - self.response_text.update() - try: - response = self.send_callback(prompt) - except Exception as e: - response = f"Error: {e}" - self.response_text.delete("1.0", "end") - self.response_text.insert("1.0", response) - self.response_text.configure(state="disabled") diff --git a/results_chart.py b/results_chart.py deleted file mode 100644 index 817ab2d..0000000 --- a/results_chart.py +++ /dev/null @@ -1,46 +0,0 @@ -import tkinter as tk -from tkinter import ttk -from pathlib import Path -import matplotlib -matplotlib.use('Agg') -import matplotlib.pyplot as plt -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -import re - -def parse_report(report_path: Path): - # Simple parser for markdown report: looks for lines like 'Pass: 8/10', 'Fail: 2/10', etc. - pass_count = 0 - fail_count = 0 - total = 0 - with open(report_path, 'r', encoding='utf-8') as f: - for line in f: - m = re.match(r'Pass: (\d+)/(\d+)', line) - if m: - pass_count = int(m.group(1)) - total = int(m.group(2)) - m = re.match(r'Fail: (\d+)/(\d+)', line) - if m: - fail_count = int(m.group(1)) - return pass_count, fail_count, total - -class ResultsChartDialog(tk.Toplevel): - def __init__(self, parent, report_path: Path): - super().__init__(parent) - self.title(f"Test Results Chart: {report_path.name}") - self.geometry("600x500") - self.transient(parent) - self.grab_set() - - pass_count, fail_count, total = parse_report(report_path) - fig, ax = plt.subplots(figsize=(5,4)) - ax.bar(['Pass', 'Fail'], [pass_count, fail_count], color=['#4caf50', '#f44336']) - ax.set_ylabel('Count') - ax.set_title(f"Test Results ({pass_count+fail_count}/{total} completed)") - for i, v in enumerate([pass_count, fail_count]): - ax.text(i, v + 0.1, str(v), ha='center', va='bottom', fontweight='bold') - fig.tight_layout() - - canvas = FigureCanvasTkAgg(fig, master=self) - canvas.draw() - canvas.get_tk_widget().pack(fill="both", expand=True) - ttk.Button(self, text="Close", command=self.destroy).pack(pady=8) diff --git a/settings_dialog.py b/settings_dialog.py deleted file mode 100644 index 6a3548a..0000000 --- a/settings_dialog.py +++ /dev/null @@ -1,135 +0,0 @@ -import tkinter as tk -from tkinter import ttk, messagebox, filedialog -import sys -import os -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '.core')) -from settings import load_settings, save_settings -from config import DEFAULT_TEMPERATURE, DEFAULT_PROVIDER_NAME -from providers import list_provider_names - -class SettingsDialog(tk.Toplevel): - def __init__(self, parent): - super().__init__(parent) - self.title("Settings") - self.geometry("500x400") - self.transient(parent) - self.grab_set() - self.settings = load_settings() - - notebook = ttk.Notebook(self) - notebook.pack(fill="both", expand=True, padx=10, pady=10) - - # General tab - general_frame = ttk.Frame(notebook) - notebook.add(general_frame, text="General") - - ttk.Label(general_frame, text="Default Provider:").grid(row=0, column=0, sticky="w", pady=6) - self.provider_var = tk.StringVar(value=self.settings.get("default_provider", DEFAULT_PROVIDER_NAME)) - self.provider_combo = ttk.Combobox(general_frame, textvariable=self.provider_var, values=list_provider_names(), state="readonly", width=32) - self.provider_combo.grid(row=0, column=1, sticky="ew", padx=8) - - ttk.Label(general_frame, text="Default Temperature:").grid(row=1, column=0, sticky="w", pady=6) - self.temp_var = tk.DoubleVar(value=self.settings.get("default_temperature", DEFAULT_TEMPERATURE)) - self.temp_entry = ttk.Entry(general_frame, textvariable=self.temp_var, width=8) - self.temp_entry.grid(row=1, column=1, sticky="w", padx=8) - - # Model paths tab - paths_frame = ttk.Frame(notebook) - notebook.add(paths_frame, text="Model Paths") - # Model paths now as list of dicts: {"nickname": str, "path": str} - self.paths = self.settings.get("local_model_paths", []) - if self.paths and isinstance(self.paths[0], str): - # Migrate from old format - self.paths = [{"nickname": os.path.basename(p.rstrip("/")), "path": p} for p in self.paths] - self.paths_list = tk.Listbox(paths_frame, selectmode=tk.SINGLE, width=60, height=8) - self.paths_list.pack(fill="x", padx=8, pady=(8,4)) - self.refresh_paths_list() - - btn_frame = ttk.Frame(paths_frame) - btn_frame.pack(fill="x", padx=8, pady=4) - ttk.Button(btn_frame, text="Add Path", command=self.add_path).pack(side="left") - ttk.Button(btn_frame, text="Edit Selected", command=self.edit_path).pack(side="left", padx=(8,0)) - ttk.Button(btn_frame, text="Remove Selected", command=self.remove_path).pack(side="left", padx=(8,0)) - - # Key/legend for nicknames - key_frame = ttk.Frame(paths_frame) - key_frame.pack(fill="x", padx=8, pady=(8, 4)) - key_text = ( - "Nickname: A short label for this model path. " - "You can use nicknames to quickly identify or group model folders. " - "For example, use 'Main', 'Backup', or 'Experimental'. " - "Nicknames are for your reference only and do not affect model loading." - ) - ttk.Label(key_frame, text=key_text, wraplength=420, foreground="#878787").pack(anchor="w") - - def refresh_paths_list(self): - self.paths_list.delete(0, "end") - for entry in self.paths: - display = f"{entry.get('nickname','')} — {entry.get('path','')}" - self.paths_list.insert("end", display) - - def add_path(self): - self.edit_path(new=True) - - def edit_path(self, new=False): - idx = None - if not new: - sel = self.paths_list.curselection() - if not sel: - return - idx = sel[0] - entry = self.paths[idx] - else: - entry = {"nickname": "", "path": ""} - popup = tk.Toplevel(self) - popup.title("Edit Model Path") - popup.geometry("400x140") - tk.Label(popup, text="Nickname:").pack(anchor="w", padx=12, pady=(12,0)) - nick_var = tk.StringVar(value=entry.get("nickname", "")) - nick_entry = ttk.Entry(popup, textvariable=nick_var, width=40) - nick_entry.pack(fill="x", padx=12) - tk.Label(popup, text="Path:").pack(anchor="w", padx=12, pady=(8,0)) - path_var = tk.StringVar(value=entry.get("path", "")) - path_entry = ttk.Entry(popup, textvariable=path_var, width=40) - path_entry.pack(fill="x", padx=12) - def save_edit(): - new_entry = {"nickname": nick_var.get().strip(), "path": path_var.get().strip()} - if not new_entry["path"]: - messagebox.showerror("Error", "Path cannot be empty.", parent=popup) - return - if new: - self.paths.append(new_entry) - else: - self.paths[idx] = new_entry - self.refresh_paths_list() - popup.destroy() - btns = ttk.Frame(popup) - btns.pack(fill="x", pady=10) - ttk.Button(btns, text="Save", command=save_edit).pack(side="right", padx=12) - ttk.Button(btns, text="Cancel", command=popup.destroy).pack(side="right") - - # Save/Cancel - save_btn = ttk.Button(self, text="Save", command=self.save) - save_btn.pack(side="right", padx=10, pady=10) - cancel_btn = ttk.Button(self, text="Cancel", command=self.destroy) - cancel_btn.pack(side="right", pady=10) - - def add_path(self): - selection = filedialog.askdirectory(parent=self) - if selection and selection not in self.paths_list.get(0, "end"): - self.paths_list.insert("end", selection) - - def remove_path(self): - sel = self.paths_list.curselection() - if sel: - idx = sel[0] - del self.paths[idx] - self.refresh_paths_list() - - def save(self): - self.settings["default_provider"] = self.provider_var.get().strip() - self.settings["default_temperature"] = self.temp_var.get() - self.settings["local_model_paths"] = self.paths - save_settings(self.settings) - messagebox.showinfo("Settings", "Settings saved.") - self.destroy() diff --git a/test_editor.py b/test_editor.py deleted file mode 100644 index b96572b..0000000 --- a/test_editor.py +++ /dev/null @@ -1,34 +0,0 @@ -import tkinter as tk -from tkinter import simpledialog, messagebox -from pathlib import Path - -class TestEditorDialog(simpledialog.Dialog): - def __init__(self, parent, test_dir: Path): - self.test_dir = test_dir - super().__init__(parent, title="New/Edit Test") - - def body(self, master): - tk.Label(master, text="Test Filename:").grid(row=0, column=0, sticky="w") - self.filename_entry = tk.Entry(master, width=40) - self.filename_entry.grid(row=0, column=1, padx=6, pady=4) - - tk.Label(master, text="Prompt:").grid(row=1, column=0, sticky="nw") - self.prompt_text = tk.Text(master, width=60, height=10) - self.prompt_text.grid(row=1, column=1, padx=6, pady=4) - - tk.Label(master, text="Expected Output:").grid(row=2, column=0, sticky="nw") - self.output_text = tk.Text(master, width=60, height=10) - self.output_text.grid(row=2, column=1, padx=6, pady=4) - return self.filename_entry - - def apply(self): - filename = self.filename_entry.get().strip() - prompt = self.prompt_text.get("1.0", "end").strip() - output = self.output_text.get("1.0", "end").strip() - if not filename or not prompt: - messagebox.showerror("Error", "Filename and prompt are required.") - return - file_path = self.test_dir / filename - with open(file_path, "w", encoding="utf-8") as f: - f.write(f"PROMPT:\n{prompt}\n\nEXPECTED:\n{output}\n") - messagebox.showinfo("Saved", f"Test saved as {file_path.name}")