File size: 9,348 Bytes
4346e8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9438a57
 
 
 
4346e8f
9438a57
4346e8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9438a57
 
 
 
4346e8f
 
 
 
 
9438a57
 
 
 
 
 
 
 
 
 
 
 
4346e8f
 
 
9438a57
 
 
 
 
 
 
 
4346e8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9438a57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4346e8f
 
 
9438a57
 
4346e8f
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/env bash
# TestAjanı başlatıcı (macOS + Linux).
# Kullanım: ./scripts/start.sh [--rebuild] [--model MODEL] [--no-browser]
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"

MODEL="${OLLAMA_MODEL:-qwen2.5-coder:7b}"
REBUILD=0
OPEN_BROWSER=1
APP_URL="http://localhost:8000"
while [ $# -gt 0 ]; do
  case "$1" in
    --rebuild) REBUILD=1 ;;
    --model) MODEL="$2"; shift ;;
    --no-browser) OPEN_BROWSER=0 ;;
    -h|--help) sed -n '2,3p' "$0"; exit 0 ;;
    *) echo "Bilinmeyen seçenek: $1"; exit 2 ;;
  esac
  shift
done

OS="$(uname -s)"
step() { printf '\n\033[1;34m==>\033[0m %s\n' "$1"; }
ok()   { printf '    \033[32m✓\033[0m %s\n' "$1"; }
fail() { printf '\n\033[31mHATA:\033[0m %s\n' "$1"; exit 1; }

http_ok() { curl -fsS -m 3 -o /dev/null "$1" 2>/dev/null; }

# ---------------------------------------------------------------- 1. Docker kurulu mu?
step "Docker kontrol ediliyor"
if ! command -v docker >/dev/null 2>&1; then
  if [ "$OS" = "Darwin" ]; then
    echo "    Docker Desktop kurulu değil."
    if command -v brew >/dev/null 2>&1; then
      read -r -p "    Homebrew ile şimdi kurulsun mu? [E/h] " ans
      if [[ ! "$ans" =~ ^[Hh] ]]; then
        brew install --cask docker
      else
        fail "Docker Desktop'ı https://www.docker.com/products/docker-desktop/ adresinden kurup tekrar deneyin."
      fi
    else
      fail "Docker Desktop'ı https://www.docker.com/products/docker-desktop/ adresinden kurup tekrar deneyin."
    fi
  else
    fail "Docker kurulu değil. Kurulum: https://docs.docker.com/engine/install/ (hızlı yol: curl -fsSL https://get.docker.com | sh)"
  fi
fi
ok "docker komutu bulundu"

# ---------------------------------------------------------------- 2. Docker çalışıyor mu?
if ! docker info >/dev/null 2>&1; then
  if [ "$OS" = "Darwin" ]; then
    echo "    Docker Desktop başlatılıyor…"
    open -a Docker || fail "Docker Desktop açılamadı. Uygulamalar klasöründen elle açın."
  elif docker info 2>&1 | grep -qi "permission denied"; then
    fail "Docker'a erişim izni yok. Şunu çalıştırıp oturumu kapatıp açın: sudo usermod -aG docker \$USER"
  else
    echo "    Docker servisi başlatılıyor (sudo parolası istenebilir)…"
    sudo systemctl start docker 2>/dev/null || true
  fi
  for _ in $(seq 1 60); do
    docker info >/dev/null 2>&1 && break
    sleep 3
  done
  docker info >/dev/null 2>&1 || fail "Docker 3 dakikada başlamadı. Docker'ı elle açıp tekrar deneyin."
fi
docker compose version >/dev/null 2>&1 || fail "'docker compose' bulunamadı. Docker'ı güncelleyin (Compose v2 gerekli)."
ok "Docker çalışıyor"

# ---------------------------------------------------------------- 3. Sandbox imajları
step "Kod çalıştırma ortamları (sandbox imajları)"
for lang in python node java ui; do
  img="lta-sandbox-$lang"
  # Dockerfile değiştiyse (ör. yeni kütüphane) imaj yeniden derlenir: özet imaj etiketinde tutulur
  hash="$( (sha256sum "sandbox/docker/$lang.Dockerfile" 2>/dev/null || shasum -a 256 "sandbox/docker/$lang.Dockerfile") | cut -c1-16)"
  have="$(docker image inspect "$img" --format '{{ index .Config.Labels "lta.hash" }}' 2>/dev/null || true)"
  if [ "$REBUILD" = 1 ] || [ "$have" != "$hash" ]; then
    echo "    $img derleniyor (ilk seferde birkaç dakika sürer)…"
    docker build -q --label "lta.hash=$hash" -f "sandbox/docker/$lang.Dockerfile" -t "$img" sandbox >/dev/null
  fi
  ok "$img"
done

# ---------------------------------------------------------------- 4. Ollama: yerel mi, konteyner mi?
step "Dil modeli sunucusu (Ollama)"
PROFILE_ARGS=()
if http_ok "http://localhost:11434/api/tags" && ! docker compose ps --services --status running 2>/dev/null | grep -qx ollama; then
  export OLLAMA_URL="http://host.docker.internal:11434"
  OLLAMA_MODE=host
  ok "Bilgisayardaki Ollama kullanılacak"
else
  export OLLAMA_URL="http://ollama:11434"
  OLLAMA_MODE=docker
  PROFILE_ARGS=(--profile ollama)
  ok "Ollama konteynerde çalışacak"
  if command -v nvidia-smi >/dev/null || [ "$(uname)" = "Darwin" ]; then
    echo "  [UYARI] Konteynerdeki Ollama ekran kartını (Mac'te Metal) genellikle kullanmaz; çok yavaş olur."
    echo "          Hız için Ollama'yı bilgisayara kurun: https://ollama.com/download — sonra yeniden başlatın."
  fi
fi
export OLLAMA_MODEL="$MODEL"

# ---------------------------------------------------------------- 5. Uygulamayı başlat
step "Uygulama başlatılıyor"
EXPECTED="$(head -n1 VERSION 2>/dev/null | tr -d '[:space:]')"
# Başka bir klasörden (eski sürümden) başlatılmış ve 8000 portunu tutan uygulama konteynerini kaldır
docker ps -a --filter "publish=8000" --format '{{.ID}}|{{.Names}}|{{.Labels}}' 2>/dev/null | while IFS='|' read -r cid cname clabels; do
  proj="$(printf '%s' "$clabels" | tr ',' '\n' | sed -n 's/^com\.docker\.compose\.project=//p')"
  if [ -n "$cid" ] && [ "$proj" != "llm-test-agent" ]; then
    echo "    Eski uygulama konteyneri kaldırılıyor: $cname (proje: ${proj:-yok})"
    docker rm -f "$cid" >/dev/null 2>&1 || true
  fi
done
# Her başlatmada imaj güncellenir; değişiklik yoksa Docker önbelleği sayesinde birkaç saniye sürer
BUILD_ARGS=(--build)
if [ -z "$(docker compose images -q app 2>/dev/null)" ]; then
  echo "    Uygulama imajı derleniyor (ilk seferde birkaç dakika sürer)…"
fi
# Not: macOS'taki bash 3.2 boş dizileri "set -u" altında sorun sayar; bu yüzden ${a[@]+...} kalıbı
# NVIDIA kartı varsa uygulama konteynerine GPU izleme erişimi verilir (gerçek zamanlı GPU göstergesi için)
GPU_FILES=()
command -v nvidia-smi >/dev/null && GPU_FILES=(-f docker-compose.yml -f docker-compose.gpu.yml)
if ! docker compose ${GPU_FILES[@]+"${GPU_FILES[@]}"} ${PROFILE_ARGS[@]+"${PROFILE_ARGS[@]}"} up -d ${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}; then
  [ ${#GPU_FILES[@]} -gt 0 ] || fail "docker compose up başarısız oldu. Ayrıntı: docker compose logs"
  echo "  [UYARI] Konteynere GPU erişimi verilemedi; GPU göstergesi yalnızca Ollama bilgisini gösterecek."
  docker compose ${PROFILE_ARGS[@]+"${PROFILE_ARGS[@]}"} up -d ${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"} || fail "docker compose up başarısız oldu."
fi
ok "Konteynerler ayakta"

# ---------------------------------------------------------------- 6. Model
step "Model: $MODEL"
for _ in $(seq 1 40); do http_ok "http://localhost:11434/api/tags" && break; sleep 2; done
if curl -fsS -m 5 "http://localhost:11434/api/tags" 2>/dev/null | grep -q "\"name\":\"$MODEL"; then
  ok "Model zaten indirilmiş"
else
  echo "    Model indiriliyor (yalnızca ilk seferde; ~5 GB)…"
  if [ "$OLLAMA_MODE" = host ]; then
    ollama pull "$MODEL"
  else
    docker compose --profile ollama exec -T ollama ollama pull "$MODEL"
  fi
  ok "Model hazır"
fi

# ---------------------------------------------------------------- 7. Hazır olmasını bekle, tarayıcıyı aç
step "Arayüz hazırlanıyor"
# Hafif /api/ping kullanılır (Ollama'ya dokunmaz); eski sürümde ping yoksa /api/health'e uzun zaman aşımıyla bakılır.
app_version() {
  local v
  v="$(curl -fsS -m 5 "$1/api/ping" 2>/dev/null | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')"
  [ -n "$v" ] || v="$(curl -fsS -m 30 "$1/api/health" 2>/dev/null | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')"
  printf '%s' "$v"
}
wait_app() {
  local v u
  for _ in $(seq 1 60); do
    for u in "http://localhost:8000" "http://127.0.0.1:8000"; do
      v="$(app_version "$u")"
      if [ -n "$v" ]; then APP_URL="$u"; RUNNING="$v"; return 0; fi
    done
    sleep 2
  done
  RUNNING=""; return 1
}
show_diag() {
  printf '\n\033[33m---- docker compose ps ----\033[0m\n'; docker compose ps -a || true
  printf '\033[33m---- docker compose logs app (son 60 satır) ----\033[0m\n'; docker compose logs app --tail 60 || true
}
wait_app || { show_diag; fail "Uygulama yanıt vermiyor. Yukarıdaki satırların ekran görüntüsünü paylaşın."; }
if [ -n "$EXPECTED" ] && [ "$RUNNING" != "$EXPECTED" ]; then
  echo "    Çalışan sürüm '${RUNNING}', beklenen '${EXPECTED}'. Uygulama önbelleksiz yeniden derleniyor…"
  docker compose ${GPU_FILES[@]+"${GPU_FILES[@]}"} build --no-cache app || docker compose build --no-cache app
  docker compose ${GPU_FILES[@]+"${GPU_FILES[@]}"} ${PROFILE_ARGS[@]+"${PROFILE_ARGS[@]}"} up -d --force-recreate app \
    || docker compose ${PROFILE_ARGS[@]+"${PROFILE_ARGS[@]}"} up -d --force-recreate app
  wait_app || true
  [ "$RUNNING" = "$EXPECTED" ] || { show_diag; fail "Uygulama hâlâ eski sürümde ('${RUNNING}'). 'docker ps' ile 8000 portunu kullanan konteyneri kontrol edin."; }
fi
if ! curl -fsS -m 30 "$APP_URL/api/health" 2>/dev/null | grep -q '"ollama":{"ok":true'; then
  printf '    \033[33m!\033[0m Uygulama Ollama’ya ulaşamıyor (%s).\n' "$OLLAMA_URL"
  echo "      Ollama'yı yeniden başlatın; sürerse OLLAMA_HOST=0.0.0.0 ile başlatın (Linux: systemctl edit ollama)."
  echo "      Arayüz yine de açılıyor; Ollama düzelince üst çubuktaki durum yeşile döner."
fi
ok "Sürüm: $RUNNING"
ok "Hazır: $APP_URL"

if [ "$OPEN_BROWSER" = 1 ]; then
  if [ "$OS" = "Darwin" ]; then open "$APP_URL/?v=$RUNNING"
  elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$APP_URL/?v=$RUNNING" >/dev/null 2>&1 &
  fi
fi
echo
echo "Durdurmak için: ./scripts/stop.sh  (veya Durdur kısayolu)"