Qwen3.5 + Open WebUI 로컬 서빙 환경 셋업
Ollama 방식과 vllm-mlx(MLX) 방식 두 가지 셋업 스크립트 및 가이드 포함. transformers fast image processor 호환성 패치 자동 적용. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
fe00782db1
390
SETUP_MLX.md
Normal file
390
SETUP_MLX.md
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
# Qwen3.5 + Open WebUI — vllm-mlx (Mac 최적화)
|
||||||
|
|
||||||
|
> 환경: MacBook Pro M4 Pro 48GB / Docker Desktop / Python 3.10+
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 목차
|
||||||
|
|
||||||
|
1. [환경 준비](#1-환경-준비)
|
||||||
|
2. [서버 시작](#2-서버-시작)
|
||||||
|
3. [Open WebUI 연결](#3-open-webui-연결)
|
||||||
|
4. [종료 / 재시작](#4-종료--재시작)
|
||||||
|
5. [파라미터 레퍼런스](#5-파라미터-레퍼런스)
|
||||||
|
6. [트러블슈팅](#6-트러블슈팅)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. 환경 준비
|
||||||
|
|
||||||
|
> ⚠️ conda 환경에서 실행하면 MPICH와 MLX가 충돌합니다. 반드시 별도 venv에서 실행하세요.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
conda deactivate
|
||||||
|
|
||||||
|
python3 -m venv ~/mlx-env
|
||||||
|
source ~/mlx-env/bin/activate
|
||||||
|
|
||||||
|
pip install git+https://github.com/waybarrios/vllm-mlx.git
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. 서버 시작
|
||||||
|
|
||||||
|
모델은 첫 시작 시 HuggingFace에서 자동 다운로드됩니다.
|
||||||
|
|
||||||
|
## 텍스트 전용
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/mlx-env/bin/activate
|
||||||
|
|
||||||
|
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
|
||||||
|
--port 8090 \
|
||||||
|
--max-tokens 8192 \
|
||||||
|
--default-temperature 0.7 \
|
||||||
|
--default-top-p 0.9
|
||||||
|
```
|
||||||
|
|
||||||
|
## 텍스트 + 이미지 (멀티모달)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/mlx-env/bin/activate
|
||||||
|
|
||||||
|
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
|
||||||
|
--port 8090 \
|
||||||
|
--max-tokens 8192 \
|
||||||
|
--default-temperature 0.7 \
|
||||||
|
--default-top-p 0.9 \
|
||||||
|
--mllm
|
||||||
|
```
|
||||||
|
|
||||||
|
> `--mllm` 플래그 하나로 이미지 입력이 활성화됩니다.
|
||||||
|
>
|
||||||
|
> 포그라운드로 실행됩니다. **새 터미널 탭**을 열어서 다음 단계를 진행하세요.
|
||||||
|
|
||||||
|
## 용도별 추천 설정
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 문서 생성 (보수적)
|
||||||
|
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
|
||||||
|
--port 8090 --max-tokens 8192 \
|
||||||
|
--default-temperature 0.2 --default-top-p 0.95 --mllm
|
||||||
|
|
||||||
|
# 대화 / 창의적 응답
|
||||||
|
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
|
||||||
|
--port 8090 --max-tokens 8192 \
|
||||||
|
--default-temperature 1.0 --default-top-p 0.8 --mllm
|
||||||
|
|
||||||
|
# 동시 요청 최적화 (여러 사용자)
|
||||||
|
# ⚠️ --continuous-batching은 단일 사용자일 때 오히려 느려질 수 있음
|
||||||
|
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
|
||||||
|
--port 8090 --max-tokens 8192 \
|
||||||
|
--default-temperature 0.7 --mllm --continuous-batching
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Open WebUI 연결
|
||||||
|
|
||||||
|
### 3-1. 서버 동작 확인
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8090/v1/models
|
||||||
|
|
||||||
|
curl http://localhost:8090/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"model": "mlx-community/Qwen3.5-35B-A3B-4bit",
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"messages": [{"role": "user", "content": "안녕하세요"}]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3-2. Open WebUI 실행
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/PyCharmMiscProject/openwebui
|
||||||
|
docker compose -f docker-compose.mlx.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
> `docker-compose.mlx.yml`이 없으면 [부록](#부록-docker-composemlxyml-생성)을 참고하세요.
|
||||||
|
|
||||||
|
### 3-3. 브라우저 접속
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 첫 접속 시 회원가입 (로컬 전용, 첫 계정 = admin)
|
||||||
|
2. **설정 → Connections** → OpenAI API 연결 확인
|
||||||
|
- URL: `http://host.docker.internal:8090/v1`
|
||||||
|
- API Key: `none`
|
||||||
|
3. 상단 모델 선택 후 채팅 시작
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. 종료 / 재시작
|
||||||
|
|
||||||
|
### 종료
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# vllm-mlx 서버: Ctrl+C
|
||||||
|
|
||||||
|
# Open WebUI
|
||||||
|
cd ~/PyCharmMiscProject/openwebui
|
||||||
|
docker compose -f docker-compose.mlx.yml down
|
||||||
|
```
|
||||||
|
|
||||||
|
### 재시작
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 터미널 1: 서버
|
||||||
|
source ~/mlx-env/bin/activate
|
||||||
|
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
|
||||||
|
--port 8090 --max-tokens 8192 \
|
||||||
|
--default-temperature 0.7 --default-top-p 0.9 --mllm
|
||||||
|
|
||||||
|
# 터미널 2: Open WebUI
|
||||||
|
cd ~/PyCharmMiscProject/openwebui
|
||||||
|
docker compose -f docker-compose.mlx.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. 파라미터 레퍼런스
|
||||||
|
|
||||||
|
## 서버 시작 플래그
|
||||||
|
|
||||||
|
### 생성 파라미터
|
||||||
|
|
||||||
|
| 플래그 | 기본값 | 설명 |
|
||||||
|
|--------|--------|------|
|
||||||
|
| `--max-tokens` | 32768 | 최대 생성 토큰 수 (thinking + 응답 합산) |
|
||||||
|
| `--default-temperature` | 모델 기본값 | Temperature (0.0~2.0) |
|
||||||
|
| `--default-top-p` | 모델 기본값 | Nucleus Sampling |
|
||||||
|
| `--reasoning-parser qwen3` | off | thinking 내용을 별도 필드로 추출 |
|
||||||
|
|
||||||
|
### 모달리티
|
||||||
|
|
||||||
|
| 플래그 | 기본값 | 설명 |
|
||||||
|
|--------|--------|------|
|
||||||
|
| `--mllm` | off | 멀티모달 (이미지 입력) 활성화 |
|
||||||
|
|
||||||
|
### 성능
|
||||||
|
|
||||||
|
| 플래그 | 기본값 | 설명 |
|
||||||
|
|--------|--------|------|
|
||||||
|
| `--continuous-batching` | off | 동시 요청 배칭 (단일 사용자 시 오히려 느림) |
|
||||||
|
| `--prefill-step-size` | 2048 | 프리필 처리 단위 |
|
||||||
|
| `--stream-interval` | 1 | 스트리밍 배치 토큰 수 (1=부드러움, 클수록 처리량↑) |
|
||||||
|
|
||||||
|
### 메모리
|
||||||
|
|
||||||
|
| 플래그 | 기본값 | 설명 |
|
||||||
|
|--------|--------|------|
|
||||||
|
| `--kv-cache-quantization` | off | KV 캐시 양자화 활성화 (메모리 절약) |
|
||||||
|
| `--kv-cache-quantization-bits` | 8 | KV 캐시 양자화 비트 (4 또는 8) |
|
||||||
|
| `--cache-memory-percent` | 0.20 | RAM의 몇 %를 캐시에 할당 |
|
||||||
|
|
||||||
|
### 서버
|
||||||
|
|
||||||
|
| 플래그 | 기본값 | 설명 |
|
||||||
|
|--------|--------|------|
|
||||||
|
| `--port` | - | 서버 포트 |
|
||||||
|
| `--host` | - | 서버 호스트 |
|
||||||
|
| `--api-key` | - | API 키 (미설정 시 인증 없음) |
|
||||||
|
| `--rate-limit` | 0 | 분당 요청 제한 (0 = 무제한) |
|
||||||
|
| `--timeout` | 300 | 요청 타임아웃 (초) |
|
||||||
|
|
||||||
|
## 각 파라미터가 하는 일
|
||||||
|
|
||||||
|
### `--default-temperature` (Temperature)
|
||||||
|
|
||||||
|
모델의 출력 랜덤성을 조절합니다.
|
||||||
|
|
||||||
|
```
|
||||||
|
0.0 ←──────────────────────────→ 2.0
|
||||||
|
결정적 랜덤
|
||||||
|
(같은 질문 = 같은 답) (창의적, 횡설수설 가능)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `0.0` → 항상 가장 확률 높은 토큰 선택
|
||||||
|
- `0.7` → 일반 대화에 적합
|
||||||
|
- `1.0+` → 창의적 글쓰기
|
||||||
|
|
||||||
|
### `--default-top-p` (Nucleus Sampling)
|
||||||
|
|
||||||
|
누적 확률 상위 p%에 드는 토큰만 후보로 남깁니다.
|
||||||
|
|
||||||
|
```
|
||||||
|
예: top-p = 0.9
|
||||||
|
[토큰A: 50%] [토큰B: 30%] [토큰C: 10%] | [토큰D: 5%] [토큰E: 3%] ...
|
||||||
|
─────────── 상위 90% (후보) ────────── ──── 제외 ────
|
||||||
|
```
|
||||||
|
|
||||||
|
- `0.9` → 희귀한 토큰 배제, 안정적
|
||||||
|
- `1.0` → 필터 없음
|
||||||
|
|
||||||
|
### `--max-tokens`
|
||||||
|
|
||||||
|
한 요청에서 생성할 최대 토큰 수입니다.
|
||||||
|
|
||||||
|
- Qwen3.5는 **thinking 토큰 + 응답 토큰이 합산**됩니다
|
||||||
|
- 너무 크면 thinking이 끝없이 돌 수 있음
|
||||||
|
- **8192 권장**
|
||||||
|
|
||||||
|
### `--mllm`
|
||||||
|
|
||||||
|
멀티모달 모드를 활성화합니다. 이 플래그가 없으면 텍스트만 처리합니다.
|
||||||
|
|
||||||
|
### `--continuous-batching`
|
||||||
|
|
||||||
|
여러 요청을 동시에 처리합니다. **단일 사용자일 때는 오히려 느려지므로**, 동시 사용자가 있을 때만 사용하세요.
|
||||||
|
|
||||||
|
### `--reasoning-parser qwen3`
|
||||||
|
|
||||||
|
Qwen3.5의 `<think>...</think>` thinking 내용을 별도 `reasoning_content` 필드로 추출합니다. Open WebUI에서 thinking 과정을 보고 싶을 때 유용합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. 트러블슈팅
|
||||||
|
|
||||||
|
## 빠른 참조
|
||||||
|
|
||||||
|
| 증상 | 원인 | 해결 |
|
||||||
|
|------|------|------|
|
||||||
|
| thinking만 하고 응답 없음 | `--max-tokens`가 너무 큼 | 8192로 설정 후 서버 재시작 |
|
||||||
|
| conda에서 abort / MPI 에러 | MPICH 충돌 | `conda deactivate` 후 venv에서 실행 |
|
||||||
|
| 이미지 입력 안 됨 | `--mllm` 미설정 | 서버 시작 시 `--mllm` 추가 |
|
||||||
|
| 모델이 안 보임 | 서버 미실행 | `curl http://localhost:8090/v1/models` 확인 |
|
||||||
|
| 컨테이너 안 뜸 | Docker 문제 | `docker logs open-webui-mlx` 확인 |
|
||||||
|
| 포트 충돌 | 이미 사용 중 | `lsof -i :3000` / `lsof -i :8090` |
|
||||||
|
| 요청 타임아웃 | 기본 300초 초과 | `--timeout 600` 으로 늘리기 |
|
||||||
|
| 이미지 입력 시 PyTorch 텐서 에러 | transformers fast image processor 호환성 문제 | 아래 **[이미지 프로세서 호환성 문제](#이미지-프로세서-호환성-문제-transformers--mlx_vlm)** 참고 |
|
||||||
|
| 서버가 Ctrl+C로 안 꺼짐 | 프로세스가 응답 중 | `lsof -ti :8090 \| xargs kill -9` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 이미지 프로세서 호환성 문제 (transformers + mlx_vlm)
|
||||||
|
|
||||||
|
### 증상
|
||||||
|
|
||||||
|
`--mllm` 모드에서 이미지를 입력하면 아래 에러가 발생합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
ValueError: Failed to process inputs with error: Only returning PyTorch tensors is currently supported.
|
||||||
|
```
|
||||||
|
|
||||||
|
또는 Open WebUI에서:
|
||||||
|
|
||||||
|
```
|
||||||
|
Response payload is not completed: <TransferEncodingError: 400, message='Not enough data to satisfy transfer length header.'>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 원인
|
||||||
|
|
||||||
|
`vllm-mlx`는 내부적으로 `mlx_vlm`을 사용하여 이미지를 처리합니다.
|
||||||
|
`mlx_vlm`은 `transformers` 라이브러리의 이미지 프로세서를 호출하는데,
|
||||||
|
`transformers >= 5.x`에서는 **Fast Image Processor**가 기본으로 로드됩니다.
|
||||||
|
|
||||||
|
```
|
||||||
|
요청 흐름:
|
||||||
|
Open WebUI → vllm-mlx → mlx_vlm → transformers (이미지 프로세서) → 에러
|
||||||
|
```
|
||||||
|
|
||||||
|
문제의 핵심:
|
||||||
|
|
||||||
|
| | Fast 버전 | Slow 버전 |
|
||||||
|
|---|---|---|
|
||||||
|
| 파일 | `image_processing_qwen2_vl_fast.py` | `image_processing_qwen2_vl.py` |
|
||||||
|
| 반환 형식 | **PyTorch 텐서만** 지원 | numpy 등 다양한 형식 지원 |
|
||||||
|
| mlx_vlm 호환 | ❌ | ✅ |
|
||||||
|
|
||||||
|
`transformers`는 자동으로 fast 버전을 우선 로드하지만,
|
||||||
|
`mlx_vlm`은 PyTorch 텐서가 아닌 numpy/MLX 배열을 기대하므로 충돌이 발생합니다.
|
||||||
|
|
||||||
|
### 해결 방법
|
||||||
|
|
||||||
|
fast 파일을 slow 클래스로 리다이렉트합니다.
|
||||||
|
|
||||||
|
**1. fast 파일 위치 확인:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# venv 경로에 따라 다를 수 있음
|
||||||
|
FAST_FILE="$(python -c "import transformers; import os; print(os.path.join(os.path.dirname(transformers.__file__), 'models/qwen2_vl/image_processing_qwen2_vl_fast.py'))")"
|
||||||
|
echo "$FAST_FILE"
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. 원본 백업:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp "$FAST_FILE" "${FAST_FILE}.bak"
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. fast 파일을 slow 버전으로 리다이렉트:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > "$FAST_FILE" << 'EOF'
|
||||||
|
"""
|
||||||
|
Fast Image processor class for Qwen2-VL.
|
||||||
|
Patched: mlx_vlm 호환을 위해 slow 버전으로 폴백합니다.
|
||||||
|
원본: image_processing_qwen2_vl_fast.py.bak
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .image_processing_qwen2_vl import Qwen2VLImageProcessor as Qwen2VLImageProcessorFast
|
||||||
|
|
||||||
|
__all__ = ["Qwen2VLImageProcessorFast"]
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. 서버 재시작 후 이미지 테스트**
|
||||||
|
|
||||||
|
### 복원 방법
|
||||||
|
|
||||||
|
패치를 되돌리려면:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
FAST_FILE="$(python -c "import transformers; import os; print(os.path.join(os.path.dirname(transformers.__file__), 'models/qwen2_vl/image_processing_qwen2_vl_fast.py'))")"
|
||||||
|
cp "${FAST_FILE}.bak" "$FAST_FILE"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 언제 이 패치가 필요 없어지나
|
||||||
|
|
||||||
|
- `mlx_vlm`이 fast image processor를 지원하게 업데이트되면
|
||||||
|
- `transformers`가 PyTorch 외의 텐서 반환을 지원하면
|
||||||
|
- `vllm-mlx`가 자체 이미지 프로세싱을 구현하면
|
||||||
|
|
||||||
|
`pip install --upgrade mlx-vlm` 후 이미지 테스트가 정상이면 패치를 복원해도 됩니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 부록: docker-compose.mlx.yml 생성
|
||||||
|
|
||||||
|
> `~/PyCharmMiscProject/openwebui/docker-compose.mlx.yml`이 없을 때만 실행하세요.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/PyCharmMiscProject/openwebui
|
||||||
|
cd ~/PyCharmMiscProject/openwebui
|
||||||
|
|
||||||
|
cat > docker-compose.mlx.yml << 'EOF'
|
||||||
|
services:
|
||||||
|
open-webui:
|
||||||
|
image: ghcr.io/open-webui/open-webui:main
|
||||||
|
container_name: open-webui-mlx
|
||||||
|
ports:
|
||||||
|
- "3000:8080"
|
||||||
|
environment:
|
||||||
|
- OPENAI_API_BASE_URL=http://host.docker.internal:8090/v1
|
||||||
|
- OPENAI_API_KEY=none
|
||||||
|
- OLLAMA_BASE_URL=
|
||||||
|
volumes:
|
||||||
|
- open-webui-mlx-data:/app/backend/data
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
open-webui-mlx-data:
|
||||||
|
EOF
|
||||||
|
```
|
||||||
127
SETUP_OLLAMA.md
Normal file
127
SETUP_OLLAMA.md
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
# Qwen3.5 + Open WebUI — Ollama 방식 (간편)
|
||||||
|
|
||||||
|
> 환경: MacBook Pro M4 Pro 48GB / Docker Desktop / Homebrew
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 사전 요구사항
|
||||||
|
|
||||||
|
- Docker Desktop이 설치되어 있고 실행 중이어야 합니다
|
||||||
|
- Homebrew가 설치되어 있어야 합니다
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker --version
|
||||||
|
brew --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 1. Ollama 설치 및 시작
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install ollama
|
||||||
|
brew services start ollama
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Qwen3.5 모델 다운로드
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ollama pull qwen3.5:35b
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 모델 확인 및 테스트
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ollama list
|
||||||
|
ollama run qwen3.5:35b "안녕하세요, 자기소개 해주세요"
|
||||||
|
```
|
||||||
|
|
||||||
|
> 나가려면 `/bye` 입력
|
||||||
|
|
||||||
|
## 4. 프로젝트 디렉터리 생성
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/PyCharmMiscProject/openwebui
|
||||||
|
cd ~/PyCharmMiscProject/openwebui
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. docker-compose.yml 생성
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > docker-compose.yml << 'EOF'
|
||||||
|
services:
|
||||||
|
open-webui:
|
||||||
|
image: ghcr.io/open-webui/open-webui:main
|
||||||
|
container_name: open-webui
|
||||||
|
ports:
|
||||||
|
- "3000:8080"
|
||||||
|
environment:
|
||||||
|
- OLLAMA_BASE_URL=http://host.docker.internal:11434
|
||||||
|
volumes:
|
||||||
|
- open-webui-data:/app/backend/data
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
open-webui-data:
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Open WebUI 실행
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 확인
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps
|
||||||
|
curl http://localhost:11434/api/tags
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 브라우저 접속
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 첫 접속 시 회원가입 (로컬 전용, 첫 계정 = admin)
|
||||||
|
2. 상단 모델 선택에서 **qwen3.5:35b** 선택
|
||||||
|
3. 채팅 시작
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 종료
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/PyCharmMiscProject/openwebui && docker compose down
|
||||||
|
brew services stop ollama
|
||||||
|
```
|
||||||
|
|
||||||
|
## 재시작
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew services start ollama
|
||||||
|
cd ~/PyCharmMiscProject/openwebui && docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 트러블슈팅
|
||||||
|
|
||||||
|
### 모델이 안 보일 때
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew services list | grep ollama
|
||||||
|
curl http://localhost:11434/api/tags
|
||||||
|
```
|
||||||
|
|
||||||
|
### 컨테이너가 안 뜰 때
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs open-webui
|
||||||
|
```
|
||||||
|
|
||||||
|
### 포트 충돌 시
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lsof -i :3000
|
||||||
|
```
|
||||||
18
docker-compose.mlx.yml
Normal file
18
docker-compose.mlx.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
services:
|
||||||
|
open-webui:
|
||||||
|
image: ghcr.io/open-webui/open-webui:main
|
||||||
|
container_name: open-webui-mlx
|
||||||
|
ports:
|
||||||
|
- "3000:8080"
|
||||||
|
environment:
|
||||||
|
- OPENAI_API_BASE_URL=http://host.docker.internal:8090/v1
|
||||||
|
- OPENAI_API_KEY=none
|
||||||
|
- OLLAMA_BASE_URL=
|
||||||
|
volumes:
|
||||||
|
- open-webui-mlx-data:/app/backend/data
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
open-webui-mlx-data:
|
||||||
16
docker-compose.yml
Normal file
16
docker-compose.yml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
services:
|
||||||
|
open-webui:
|
||||||
|
image: ghcr.io/open-webui/open-webui:main
|
||||||
|
container_name: open-webui
|
||||||
|
ports:
|
||||||
|
- "3000:8080"
|
||||||
|
environment:
|
||||||
|
- OLLAMA_BASE_URL=http://host.docker.internal:11434
|
||||||
|
volumes:
|
||||||
|
- open-webui-data:/app/backend/data
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
open-webui-data:
|
||||||
219
setup-mlx.sh
Executable file
219
setup-mlx.sh
Executable file
@ -0,0 +1,219 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
# Qwen3.5 + Open WebUI (vllm-mlx) 원클릭 셋업
|
||||||
|
# 환경: Apple Silicon Mac (M1/M2/M3/M4) / Docker Desktop / Python 3.10+
|
||||||
|
#====================================================================
|
||||||
|
|
||||||
|
VENV_DIR="$HOME/mlx-env"
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
MODEL="mlx-community/Qwen3.5-35B-A3B-4bit"
|
||||||
|
PORT=8090
|
||||||
|
WEBUI_PORT=3000
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo " Qwen3.5 + Open WebUI (vllm-mlx) 셋업"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 1. 사전 요구사항 확인
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[1/6] 사전 요구사항 확인..."
|
||||||
|
|
||||||
|
# Python
|
||||||
|
if ! command -v python3 &>/dev/null; then
|
||||||
|
echo "❌ python3가 설치되어 있지 않습니다."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
PYTHON_VERSION=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
|
||||||
|
PYTHON_MAJOR=$(echo "$PYTHON_VERSION" | cut -d. -f1)
|
||||||
|
PYTHON_MINOR=$(echo "$PYTHON_VERSION" | cut -d. -f2)
|
||||||
|
if [ "$PYTHON_MAJOR" -lt 3 ] || { [ "$PYTHON_MAJOR" -eq 3 ] && [ "$PYTHON_MINOR" -lt 10 ]; }; then
|
||||||
|
echo "❌ Python 3.10 이상이 필요합니다. (현재: $PYTHON_VERSION)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✓ Python $PYTHON_VERSION"
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
if ! command -v docker &>/dev/null; then
|
||||||
|
echo "❌ Docker가 설치되어 있지 않습니다."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! docker info &>/dev/null; then
|
||||||
|
echo "❌ Docker Desktop이 실행 중이 아닙니다."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✓ Docker"
|
||||||
|
|
||||||
|
# Apple Silicon
|
||||||
|
if ! sysctl -n machdep.cpu.brand_string 2>/dev/null | grep -q "Apple"; then
|
||||||
|
echo "❌ Apple Silicon이 아닙니다. MLX는 Apple Silicon에서만 동작합니다."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✓ Apple Silicon"
|
||||||
|
|
||||||
|
# RAM
|
||||||
|
TOTAL_RAM_GB=$(sysctl -n hw.memsize | awk '{printf "%.0f", $1/1024/1024/1024}')
|
||||||
|
echo " ✓ RAM: ${TOTAL_RAM_GB}GB"
|
||||||
|
if [ "$TOTAL_RAM_GB" -lt 32 ]; then
|
||||||
|
echo " ⚠️ RAM이 32GB 미만입니다. 4bit 모델(~20GB)도 빡빡할 수 있습니다."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 2. 가상환경 생성 및 패키지 설치
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[2/6] 가상환경 및 패키지 설치..."
|
||||||
|
|
||||||
|
# conda 감지 경고
|
||||||
|
if [ -n "$CONDA_DEFAULT_ENV" ]; then
|
||||||
|
echo " ⚠️ conda 환경 감지 ($CONDA_DEFAULT_ENV)."
|
||||||
|
echo " MPICH 충돌 방지를 위해 별도 venv를 사용합니다."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$VENV_DIR" ]; then
|
||||||
|
python3 -m venv "$VENV_DIR"
|
||||||
|
echo " ✓ 가상환경 생성: $VENV_DIR"
|
||||||
|
else
|
||||||
|
echo " ✓ 기존 가상환경 사용: $VENV_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
source "$VENV_DIR/bin/activate"
|
||||||
|
|
||||||
|
# vllm-mlx
|
||||||
|
if ! pip show vllm-mlx &>/dev/null; then
|
||||||
|
echo " vllm-mlx 설치 중... (시간이 걸릴 수 있습니다)"
|
||||||
|
pip install -q git+https://github.com/waybarrios/vllm-mlx.git
|
||||||
|
echo " ✓ vllm-mlx 설치 완료"
|
||||||
|
else
|
||||||
|
echo " ✓ vllm-mlx 이미 설치됨"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# torch, torchvision
|
||||||
|
if ! pip show torch &>/dev/null; then
|
||||||
|
echo " torch, torchvision 설치 중..."
|
||||||
|
pip install -q torch torchvision
|
||||||
|
echo " ✓ torch 설치 완료"
|
||||||
|
else
|
||||||
|
echo " ✓ torch 이미 설치됨"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 3. transformers fast image processor 패치
|
||||||
|
# 원인: transformers >= 5.x의 fast image processor가 PyTorch 텐서만
|
||||||
|
# 반환하지만, mlx_vlm은 numpy/MLX 배열을 기대하여 충돌 발생.
|
||||||
|
# 해결: fast 클래스를 slow 클래스로 리다이렉트.
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[3/6] 이미지 프로세서 호환성 패치..."
|
||||||
|
|
||||||
|
FAST_FILE=$("$VENV_DIR/bin/python3" -c "
|
||||||
|
import transformers, os
|
||||||
|
print(os.path.join(os.path.dirname(transformers.__file__),
|
||||||
|
'models/qwen2_vl/image_processing_qwen2_vl_fast.py'))
|
||||||
|
")
|
||||||
|
|
||||||
|
if grep -q "mlx_vlm 호환" "$FAST_FILE" 2>/dev/null; then
|
||||||
|
echo " ✓ 이미 패치 적용됨"
|
||||||
|
else
|
||||||
|
cp "$FAST_FILE" "${FAST_FILE}.bak"
|
||||||
|
|
||||||
|
cat > "$FAST_FILE" << 'PATCH'
|
||||||
|
"""
|
||||||
|
Fast Image processor class for Qwen2-VL.
|
||||||
|
Patched: mlx_vlm 호환을 위해 slow 버전으로 폴백합니다.
|
||||||
|
원본: image_processing_qwen2_vl_fast.py.bak
|
||||||
|
|
||||||
|
배경:
|
||||||
|
transformers >= 5.x는 Fast Image Processor를 기본 로드합니다.
|
||||||
|
Fast 버전은 PyTorch 텐서만 반환하지만, mlx_vlm은 numpy/MLX 배열을 기대합니다.
|
||||||
|
이 패치는 fast 클래스를 slow 클래스로 대체하여 호환성을 확보합니다.
|
||||||
|
|
||||||
|
복원:
|
||||||
|
cp image_processing_qwen2_vl_fast.py.bak image_processing_qwen2_vl_fast.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .image_processing_qwen2_vl import Qwen2VLImageProcessor as Qwen2VLImageProcessorFast
|
||||||
|
|
||||||
|
__all__ = ["Qwen2VLImageProcessorFast"]
|
||||||
|
PATCH
|
||||||
|
|
||||||
|
echo " ✓ 패치 적용 완료 (원본 백업됨)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 4. Docker Compose 설정
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[4/6] Docker Compose 설정..."
|
||||||
|
|
||||||
|
if [ ! -f "$PROJECT_DIR/docker-compose.mlx.yml" ]; then
|
||||||
|
cat > "$PROJECT_DIR/docker-compose.mlx.yml" << EOF
|
||||||
|
services:
|
||||||
|
open-webui:
|
||||||
|
image: ghcr.io/open-webui/open-webui:main
|
||||||
|
container_name: open-webui-mlx
|
||||||
|
ports:
|
||||||
|
- "${WEBUI_PORT}:8080"
|
||||||
|
environment:
|
||||||
|
- OPENAI_API_BASE_URL=http://host.docker.internal:${PORT}/v1
|
||||||
|
- OPENAI_API_KEY=none
|
||||||
|
- OLLAMA_BASE_URL=
|
||||||
|
volumes:
|
||||||
|
- open-webui-mlx-data:/app/backend/data
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
open-webui-mlx-data:
|
||||||
|
EOF
|
||||||
|
echo " ✓ docker-compose.mlx.yml 생성"
|
||||||
|
else
|
||||||
|
echo " ✓ docker-compose.mlx.yml 이미 존재"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 5. Open WebUI 실행
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[5/6] Open WebUI 실행..."
|
||||||
|
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
docker compose -f docker-compose.mlx.yml up -d 2>&1 | grep -v "^$"
|
||||||
|
echo " ✓ Open WebUI 실행 중 (http://localhost:${WEBUI_PORT})"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 6. 완료
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[6/6] 셋업 완료!"
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " 다음 단계: 서버를 시작하세요"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo " # 텍스트 전용:"
|
||||||
|
echo " source $VENV_DIR/bin/activate"
|
||||||
|
echo " vllm-mlx serve $MODEL \\"
|
||||||
|
echo " --port $PORT --max-tokens 8192 \\"
|
||||||
|
echo " --default-temperature 0.7 --default-top-p 0.9"
|
||||||
|
echo ""
|
||||||
|
echo " # 텍스트 + 이미지 (멀티모달):"
|
||||||
|
echo " source $VENV_DIR/bin/activate"
|
||||||
|
echo " vllm-mlx serve $MODEL \\"
|
||||||
|
echo " --port $PORT --max-tokens 8192 \\"
|
||||||
|
echo " --default-temperature 0.7 --default-top-p 0.9 --mllm"
|
||||||
|
echo ""
|
||||||
|
echo " 서버 시작 후 브라우저에서 http://localhost:${WEBUI_PORT} 접속"
|
||||||
|
echo " (첫 접속 시 회원가입 → 첫 계정이 admin)"
|
||||||
|
echo ""
|
||||||
|
echo " 종료하려면: ./stop-mlx.sh"
|
||||||
|
echo "============================================"
|
||||||
117
setup-ollama.sh
Executable file
117
setup-ollama.sh
Executable file
@ -0,0 +1,117 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
# Qwen3.5 + Open WebUI (Ollama) 원클릭 셋업
|
||||||
|
# 환경: Mac / Docker Desktop / Homebrew
|
||||||
|
#====================================================================
|
||||||
|
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
MODEL="qwen3.5:35b"
|
||||||
|
WEBUI_PORT=3000
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo " Qwen3.5 + Open WebUI (Ollama) 셋업"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 1. 사전 요구사항 확인
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[1/5] 사전 요구사항 확인..."
|
||||||
|
|
||||||
|
if ! command -v brew &>/dev/null; then
|
||||||
|
echo "❌ Homebrew가 설치되어 있지 않습니다."
|
||||||
|
echo " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✓ Homebrew"
|
||||||
|
|
||||||
|
if ! command -v docker &>/dev/null; then
|
||||||
|
echo "❌ Docker가 설치되어 있지 않습니다."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! docker info &>/dev/null; then
|
||||||
|
echo "❌ Docker Desktop이 실행 중이 아닙니다."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✓ Docker"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 2. Ollama 설치 및 시작
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[2/5] Ollama 설치..."
|
||||||
|
|
||||||
|
if ! command -v ollama &>/dev/null; then
|
||||||
|
brew install ollama
|
||||||
|
echo " ✓ Ollama 설치 완료"
|
||||||
|
else
|
||||||
|
echo " ✓ Ollama 이미 설치됨"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! brew services list | grep ollama | grep -q started; then
|
||||||
|
brew services start ollama
|
||||||
|
sleep 3
|
||||||
|
echo " ✓ Ollama 서비스 시작"
|
||||||
|
else
|
||||||
|
echo " ✓ Ollama 서비스 실행 중"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 3. 모델 다운로드
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[3/5] 모델 다운로드 ($MODEL)..."
|
||||||
|
echo " (네트워크 속도에 따라 시간이 걸릴 수 있습니다)"
|
||||||
|
|
||||||
|
ollama pull "$MODEL"
|
||||||
|
echo " ✓ 모델 다운로드 완료"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 4. Docker Compose 설정 및 실행
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[4/5] Open WebUI 실행..."
|
||||||
|
|
||||||
|
if [ ! -f "$PROJECT_DIR/docker-compose.yml" ]; then
|
||||||
|
cat > "$PROJECT_DIR/docker-compose.yml" << EOF
|
||||||
|
services:
|
||||||
|
open-webui:
|
||||||
|
image: ghcr.io/open-webui/open-webui:main
|
||||||
|
container_name: open-webui
|
||||||
|
ports:
|
||||||
|
- "${WEBUI_PORT}:8080"
|
||||||
|
environment:
|
||||||
|
- OLLAMA_BASE_URL=http://host.docker.internal:11434
|
||||||
|
volumes:
|
||||||
|
- open-webui-data:/app/backend/data
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
open-webui-data:
|
||||||
|
EOF
|
||||||
|
echo " ✓ docker-compose.yml 생성"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
docker compose up -d 2>&1 | grep -v "^$"
|
||||||
|
echo " ✓ Open WebUI 실행 중"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# 5. 완료
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
echo "[5/5] 셋업 완료!"
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " 브라우저에서 http://localhost:${WEBUI_PORT} 접속"
|
||||||
|
echo " (첫 접속 시 회원가입 → 첫 계정이 admin)"
|
||||||
|
echo " 모델 선택: $MODEL"
|
||||||
|
echo "============================================"
|
||||||
37
stop-mlx.sh
Executable file
37
stop-mlx.sh
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
# vllm-mlx + Open WebUI 종료
|
||||||
|
#====================================================================
|
||||||
|
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
PORT=8090
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo " vllm-mlx + Open WebUI 종료"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Open WebUI 컨테이너 종료
|
||||||
|
if docker ps -q --filter name=open-webui-mlx | grep -q .; then
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
docker compose -f docker-compose.mlx.yml down 2>&1 | grep -v "^$"
|
||||||
|
echo " ✓ Open WebUI 종료"
|
||||||
|
else
|
||||||
|
echo " - Open WebUI 컨테이너 없음 (이미 종료됨)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# vllm-mlx 서버 종료
|
||||||
|
PIDS=$(lsof -ti :$PORT 2>/dev/null || true)
|
||||||
|
if [ -n "$PIDS" ]; then
|
||||||
|
echo "$PIDS" | xargs kill -9 2>/dev/null || true
|
||||||
|
echo " ✓ vllm-mlx 서버 종료 (포트 $PORT)"
|
||||||
|
else
|
||||||
|
echo " - vllm-mlx 서버 없음 (이미 종료됨)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo " 완료. 재시작하려면:"
|
||||||
|
echo " 1. ./setup-mlx.sh"
|
||||||
|
echo " 2. 서버 시작 (setup 완료 시 안내되는 명령어 참고)"
|
||||||
|
echo ""
|
||||||
33
stop-ollama.sh
Executable file
33
stop-ollama.sh
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
# Ollama + Open WebUI 종료
|
||||||
|
#====================================================================
|
||||||
|
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo " Ollama + Open WebUI 종료"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Open WebUI 컨테이너 종료
|
||||||
|
if docker ps -q --filter name=open-webui | grep -q .; then
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
docker compose down 2>&1 | grep -v "^$"
|
||||||
|
echo " ✓ Open WebUI 종료"
|
||||||
|
else
|
||||||
|
echo " - Open WebUI 컨테이너 없음 (이미 종료됨)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ollama 서비스 종료
|
||||||
|
if brew services list 2>/dev/null | grep ollama | grep -q started; then
|
||||||
|
brew services stop ollama
|
||||||
|
echo " ✓ Ollama 서비스 종료"
|
||||||
|
else
|
||||||
|
echo " - Ollama 이미 종료됨"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo " 완료. 재시작하려면: ./setup-ollama.sh"
|
||||||
|
echo ""
|
||||||
Loading…
Reference in New Issue
Block a user