diff --git a/packaging/jr-sql-ai-gui.desktop b/packaging/jr-sql-ai-gui.desktop new file mode 100644 index 0000000..e2e80f3 --- /dev/null +++ b/packaging/jr-sql-ai-gui.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=JR SQL AI GUI +Comment=Ollama GUI für SQL-Modelle (Markdown + Copy SQL) +Exec=/home/johannes/localdev/sql-ai-gui/scripts/sql-ai-gui +Icon=utilities-terminal +Terminal=false +Categories=Development;Utility; +StartupNotify=true diff --git a/scripts/sql-ai-gui b/scripts/sql-ai-gui new file mode 100755 index 0000000..4ded80e --- /dev/null +++ b/scripts/sql-ai-gui @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +# Wayland bevorzugen (Hyprland) +export QT_QPA_PLATFORM="${QT_QPA_PLATFORM:-wayland}" + +# Optional: Wenn du einen venv nutzt +if [[ -x "$APP_DIR/.venv/bin/python" ]]; then + exec "$APP_DIR/.venv/bin/python" "$APP_DIR/sql_ai_gui.py" +else + exec python "$APP_DIR/sql_ai_gui.py" +fi diff --git a/sql_ai_gui.py b/sql_ai_gui.py old mode 100755 new mode 100644 index b68f489..13d9e73 --- a/sql_ai_gui.py +++ b/sql_ai_gui.py @@ -4,13 +4,11 @@ JR SQL AI GUI (Ollama) - lightweight Arch/Hyprland friendly GUI. - Left: Prompt/context - Right: Rendered Markdown answer + raw markdown -- Buttons: Send, Copy, Copy SQL only, Model pull, Ollama runtime update (Docker) +- Buttons: Send, Copy, Copy SQL only, Model pull """ import json import os import re -import shutil -import subprocess import sys from dataclasses import dataclass from typing import Optional, List @@ -41,7 +39,6 @@ from PySide6.QtWidgets import ( # ----------------------------- DEFAULT_OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://127.0.0.1:11434") DEFAULT_MODEL = os.environ.get("OLLAMA_MODEL", "jr-sql-expert:latest") -DEFAULT_DOCKER_CONTAINER_NAME = os.environ.get("OLLAMA_DOCKER_CONTAINER", "ollama") # ----------------------------- @@ -51,17 +48,6 @@ def is_docker_available() -> bool: return shutil.which("docker") is not None -def run_cmd(cmd: list[str], timeout: int = 600) -> tuple[int, str, str]: - proc = subprocess.run( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - timeout=timeout, - text=True, - ) - return proc.returncode, proc.stdout, proc.stderr - - def human_error(e: Exception) -> str: return f"{type(e).__name__}: {e}" @@ -191,36 +177,6 @@ class PullModelWorker(QThread): self.error.emit(human_error(e)) -class UpdateOllamaDockerWorker(QThread): - status = Signal(str) - done = Signal() - error = Signal(str) - - def __init__(self, container_name: str): - super().__init__() - self.container_name = container_name - - def run(self) -> None: - try: - if not is_docker_available(): - raise RuntimeError("docker not found in PATH") - - self.status.emit("docker pull ollama/ollama:latest …") - code, out, err = run_cmd(["docker", "pull", "ollama/ollama:latest"], timeout=1800) - if code != 0: - raise RuntimeError(err.strip() or out.strip() or f"docker pull failed (code {code})") - - self.status.emit(f"Restarting container '{self.container_name}' …") - code, out, err = run_cmd(["docker", "restart", self.container_name], timeout=120) - if code != 0: - raise RuntimeError(err.strip() or out.strip() or f"docker restart failed (code {code})") - - self.status.emit("Done.") - self.done.emit() - except Exception as e: - self.error.emit(human_error(e)) - - # ----------------------------- # Main Window # ----------------------------- @@ -231,7 +187,6 @@ class MainWindow(QMainWindow): self._gen_worker: Optional[GenerateWorker] = None self._pull_worker: Optional[PullModelWorker] = None - self._update_worker: Optional[UpdateOllamaDockerWorker] = None self._raw_markdown: str = "" self._render_timer = QTimer(self) @@ -324,11 +279,6 @@ class MainWindow(QMainWindow): self.btn_model_pull.clicked.connect(self.on_pull_model) right_btn_row.addWidget(self.btn_model_pull) - self.btn_runtime_update = QPushButton("Ollama Runtime updaten") - self.btn_runtime_update.clicked.connect(self.on_update_runtime) - self.btn_runtime_update.setEnabled(is_docker_available()) - right_btn_row.addWidget(self.btn_runtime_update) - right_l.addLayout(right_btn_row) splitter.addWidget(left) @@ -343,7 +293,7 @@ class MainWindow(QMainWindow): # -------------- UI helpers -------------- def ui_busy(self, busy: bool) -> None: - for w in [self.btn_send, self.btn_model_pull, self.btn_refresh_models, self.btn_runtime_update, self.btn_copy_sql]: + for w in [self.btn_send, self.btn_model_pull, self.btn_refresh_models, self.btn_copy_sql]: w.setEnabled(not busy) self.prompt.setEnabled(not busy) self.base_url.setEnabled(not busy) @@ -482,39 +432,6 @@ class MainWindow(QMainWindow): def _on_pull_err(self, err: str) -> None: self.ui_busy(False) self.msg_error("Model pull fehlgeschlagen", err) - - # -------------- Update runtime (Docker) -------------- - def on_update_runtime(self) -> None: - if not is_docker_available(): - self.msg_info("Nicht verfügbar", "docker ist nicht im PATH gefunden.") - return - - msg = ( - "Das führt aus:\n" - " docker pull ollama/ollama:latest\n" - " docker restart ollama\n\n" - "Hinweis: Du brauchst Docker-Rechte (docker group).\n" - "Fortfahren?" - ) - if QMessageBox.question(self, "Ollama updaten?", msg) != QMessageBox.Yes: - return - - self.ui_busy(True) - self.status.showMessage("Ollama Runtime Update …") - - self._update_worker = UpdateOllamaDockerWorker(DEFAULT_DOCKER_CONTAINER_NAME) - self._update_worker.status.connect(lambda s: self.status.showMessage(s)) - self._update_worker.done.connect(self._on_update_done) - self._update_worker.error.connect(self._on_update_err) - self._update_worker.start() - - def _on_update_done(self) -> None: - self.ui_busy(False) - self.status.showMessage("Ollama Runtime Update fertig.", 5000) - QTimer.singleShot(700, self.refresh_models) - - def _on_update_err(self, err: str) -> None: - self.ui_busy(False) self.msg_error("Ollama Runtime Update fehlgeschlagen", err)