Updated code to use local ollama only.

This commit is contained in:
2026-02-24 09:11:12 +01:00
parent c28cce4086
commit 6df3686baa
3 changed files with 25 additions and 85 deletions

View File

@@ -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

14
scripts/sql-ai-gui Executable file
View File

@@ -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

87
sql_ai_gui.py Executable file → Normal file
View File

@@ -4,13 +4,11 @@ JR SQL AI GUI (Ollama) - lightweight Arch/Hyprland friendly GUI.
- Left: Prompt/context - Left: Prompt/context
- Right: Rendered Markdown answer + raw markdown - 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 json
import os import os
import re import re
import shutil
import subprocess
import sys import sys
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional, List 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_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_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 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: def human_error(e: Exception) -> str:
return f"{type(e).__name__}: {e}" return f"{type(e).__name__}: {e}"
@@ -191,36 +177,6 @@ class PullModelWorker(QThread):
self.error.emit(human_error(e)) 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 # Main Window
# ----------------------------- # -----------------------------
@@ -231,7 +187,6 @@ class MainWindow(QMainWindow):
self._gen_worker: Optional[GenerateWorker] = None self._gen_worker: Optional[GenerateWorker] = None
self._pull_worker: Optional[PullModelWorker] = None self._pull_worker: Optional[PullModelWorker] = None
self._update_worker: Optional[UpdateOllamaDockerWorker] = None
self._raw_markdown: str = "" self._raw_markdown: str = ""
self._render_timer = QTimer(self) self._render_timer = QTimer(self)
@@ -324,11 +279,6 @@ class MainWindow(QMainWindow):
self.btn_model_pull.clicked.connect(self.on_pull_model) self.btn_model_pull.clicked.connect(self.on_pull_model)
right_btn_row.addWidget(self.btn_model_pull) 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) right_l.addLayout(right_btn_row)
splitter.addWidget(left) splitter.addWidget(left)
@@ -343,7 +293,7 @@ class MainWindow(QMainWindow):
# -------------- UI helpers -------------- # -------------- UI helpers --------------
def ui_busy(self, busy: bool) -> None: 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) w.setEnabled(not busy)
self.prompt.setEnabled(not busy) self.prompt.setEnabled(not busy)
self.base_url.setEnabled(not busy) self.base_url.setEnabled(not busy)
@@ -482,39 +432,6 @@ class MainWindow(QMainWindow):
def _on_pull_err(self, err: str) -> None: def _on_pull_err(self, err: str) -> None:
self.ui_busy(False) self.ui_busy(False)
self.msg_error("Model pull fehlgeschlagen", err) 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) self.msg_error("Ollama Runtime Update fehlgeschlagen", err)