Initial commit: local SQL expert AI (Ollama + CLI + prompts + systemd timer)
This commit is contained in:
130
bin/sqlai
Executable file
130
bin/sqlai
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ENV_FILE="${ROOT}/.env"
|
||||
[[ -f "$ENV_FILE" ]] && source "$ENV_FILE"
|
||||
|
||||
: "${OLLAMA_URL:=http://127.0.0.1:11434}"
|
||||
: "${EXPERT_MODEL:=jr-sql-expert}"
|
||||
|
||||
ts() { date -Is; }
|
||||
|
||||
log_dir="${ROOT}/logs"
|
||||
mkdir -p "$log_dir"
|
||||
log_file="${log_dir}/sqlai-$(date -I).log"
|
||||
|
||||
# log everything (stdout+stderr)
|
||||
exec > >(tee -a "$log_file") 2>&1
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage:
|
||||
sqlai <mode> [--file path]
|
||||
Modes:
|
||||
analyze-tsql
|
||||
analyze-proc
|
||||
analyze-view
|
||||
analyze-plan
|
||||
utf8-migration
|
||||
|
||||
Examples:
|
||||
cat query.sql | sqlai analyze-tsql
|
||||
sqlai analyze-plan --file showplan.xml
|
||||
EOF
|
||||
}
|
||||
|
||||
mode="${1:-}"
|
||||
[[ -z "$mode" ]] && { usage; exit 1; }
|
||||
shift || true
|
||||
|
||||
file=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--file) file="$2"; shift 2;;
|
||||
*) echo "[$(ts)] ERROR: Unknown arg: $1"; usage; exit 2;;
|
||||
esac
|
||||
done
|
||||
|
||||
input=""
|
||||
if [[ -n "$file" ]]; then
|
||||
if [[ ! -f "$file" ]]; then
|
||||
echo "[$(ts)] ERROR: file not found: $file"
|
||||
exit 3
|
||||
fi
|
||||
input="$(cat "$file")"
|
||||
else
|
||||
input="$(cat)"
|
||||
fi
|
||||
|
||||
if [[ -z "${input//[[:space:]]/}" ]]; then
|
||||
echo "[$(ts)] ERROR: empty input"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
case "$mode" in
|
||||
analyze-tsql)
|
||||
instruction="Analysiere das folgende T-SQL (SQL Server 2022). Finde Performance-Probleme, SARGability, Indizes, Statistiken, Parameter Sniffing Risiken und gib konkrete Verbesserungen."
|
||||
;;
|
||||
analyze-proc)
|
||||
instruction="Analysiere die folgende Stored Procedure (SQL Server 2022). Finde Performance-/Correctness-Risiken, Transaktions-/Locking-Themen, Parameter Sniffing, fehlende Indizes. Gib konkrete Refactorings."
|
||||
;;
|
||||
analyze-view)
|
||||
instruction="Analysiere die folgende View (SQL Server 2022). Prüfe SARGability, Expand/Inlining-Effekte, mögliche Indexing-Optionen (z.B. indexed view falls sinnvoll) und Plan-Auswirkungen."
|
||||
;;
|
||||
analyze-plan)
|
||||
instruction="Analysiere den folgenden SQL Server Execution Plan (XML Showplan oder Text). Identifiziere teure Operatoren, Spills, Warnungen, Kardinalitätsfehler, fehlende Indizes und gib konkrete Fixes."
|
||||
;;
|
||||
utf8-migration)
|
||||
instruction="Erstelle einen Migrationsplan, um Tabellen/Spalten auf UTF-8 umzustellen (UTF-8 enabled collations mit _UTF8). Berücksichtige Abhängigkeiten (FK/PK/Indexes/Computed/Triggers), Risiken und gib eine Schritt-für-Schritt Checkliste."
|
||||
;;
|
||||
*)
|
||||
echo "[$(ts)] ERROR: unknown mode: $mode"
|
||||
usage
|
||||
exit 5
|
||||
;;
|
||||
esac
|
||||
|
||||
prompt=$(cat <<EOF
|
||||
${instruction}
|
||||
|
||||
---BEGIN INPUT---
|
||||
${input}
|
||||
---END INPUT---
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "[$(ts)] sqlai: MODE=$mode MODEL=$EXPERT_MODEL OLLAMA_URL=$OLLAMA_URL"
|
||||
echo "[$(ts)] sqlai: INPUT_BYTES=$(printf "%s" "$input" | wc -c)"
|
||||
|
||||
payload="$(python - <<'PY'
|
||||
import json, os, sys
|
||||
model=os.environ.get("EXPERT_MODEL","jr-sql-expert")
|
||||
prompt=sys.stdin.read()
|
||||
print(json.dumps({"model": model, "prompt": prompt, "stream": False}, ensure_ascii=False))
|
||||
PY
|
||||
<<<"$prompt")"
|
||||
|
||||
echo "[$(ts)] sqlai: sending request..."
|
||||
resp="$(curl -sS -X POST "${OLLAMA_URL}/api/generate" -H 'Content-Type: application/json' --data-binary "$payload")" || {
|
||||
rc=$?
|
||||
echo "[$(ts)] sqlai: ERROR: curl failed rc=$rc"
|
||||
exit 10
|
||||
}
|
||||
|
||||
python - <<'PY'
|
||||
import json,sys
|
||||
obj=json.loads(sys.stdin.read())
|
||||
print("\n" + (obj.get("response","").rstrip()) + "\n")
|
||||
md = {
|
||||
"total_duration": obj.get("total_duration"),
|
||||
"load_duration": obj.get("load_duration"),
|
||||
"prompt_eval_count": obj.get("prompt_eval_count"),
|
||||
"prompt_eval_duration": obj.get("prompt_eval_duration"),
|
||||
"eval_count": obj.get("eval_count"),
|
||||
"eval_duration": obj.get("eval_duration"),
|
||||
}
|
||||
print("METRICS=" + json.dumps(md, ensure_ascii=False))
|
||||
PY <<<"$resp"
|
||||
|
||||
echo "[$(ts)] sqlai: done"
|
||||
Reference in New Issue
Block a user