diff --git a/.gitignore b/.gitignore index 7b390b3..96eb48e 100644 --- a/.gitignore +++ b/.gitignore @@ -52,10 +52,28 @@ .smallclaw/workspace_state.json .smallclaw/.migrated-from-localclaw -# --- WORKSPACE RUNTIME FILES --- +# --- PER-USER RUNTIME DATA --- +# Sessions, workspace runtime files, uploads are all user-specific runtime data. +# Keep: prompts/AGENTS.md, prompts/TOOLS.md, workspace/SOUL.md etc. (static templates) +.smallclaw/users/*/sessions/ +.smallclaw/users/*/workspace/memory/ +.smallclaw/users/*/workspace/uploads/ +.smallclaw/users/*/workspace/attachments/ +.smallclaw/users/*/workspace/pubmed/ +.smallclaw/users/*/workspace/tool_audit.log +.smallclaw/users/*/workspace/HEARTBEAT.md +.smallclaw/users/*/workspace/note.txt +.smallclaw/users/*/workspace/*.html +.smallclaw/users/*/workspace/*.js +.smallclaw/users/*/workspace/*.css +.smallclaw/users/*/workspace/*.py +.smallclaw/users/*/workspace/*.sh +.smallclaw/users/*/workspace/*.bat +.smallclaw/users/*/workspace/*.txt + +# --- ROOT WORKSPACE RUNTIME FILES --- # Keep: SOUL.md, SELF.md, IDENTITY.md, USER.md, MEMORY.md, AGENTS.md, TOOLS.md, BOOT.md, README.md # These are default templates that ship with SmallClaw — new users need them. -# Ignore: daily memory logs, tool audit log, heartbeat, and any AI-generated scratch files. workspace/memory/ workspace/tool_audit.log workspace/HEARTBEAT.md diff --git a/scripts/install-image-deps.sh b/scripts/install-image-deps.sh new file mode 100755 index 0000000..22f6e43 --- /dev/null +++ b/scripts/install-image-deps.sh @@ -0,0 +1,207 @@ +#!/bin/bash +# SmallClaw Image Editing Dependencies Installer +# Run: bash scripts/install-image-deps.sh [--with-rembg] +# +# --with-rembg AI 배경 제거(rembg) 설치 (~500MB onnxruntime 포함) + +set -e + +WITH_REMBG=false +for arg in "$@"; do + [ "$arg" = "--with-rembg" ] && WITH_REMBG=true +done + +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' +ok() { echo -e " ${GREEN}✓${NC} $1"; } +skip() { echo -e " ${YELLOW}↷${NC} $1 (already installed)"; } + +echo "=== SmallClaw Image Dependencies Installer ===" +echo "" + +# ────────────────────────────────────────────────── +# 1. 시스템 패키지 +# ────────────────────────────────────────────────── +echo "[1/4] 시스템 패키지 (apt)" + +APT_OK=true +if [ "$(id -u)" -eq 0 ]; then + SUDO="" +elif sudo -n true 2>/dev/null; then + SUDO="sudo" +else + echo " 경고: sudo 권한 없음 — apt 설치를 건너뜁니다." + echo " 수동 설치: sudo apt install libimage-exiftool-perl pngquant optipng jpegoptim webp potrace librsvg2-bin tesseract-ocr tesseract-ocr-kor tesseract-ocr-eng fonts-dejavu-core libheif-dev libavif-dev" + APT_OK=false +fi + +if [ "$APT_OK" = true ]; then + $SUDO apt-get update -qq + +APT_PKGS=( + libimage-exiftool-perl # exiftool CLI — EXIF 읽기/쓰기 + pngquant # PNG 손실 압축 + optipng # PNG 무손실 최적화 + jpegoptim # JPEG 최적화 + webp # cwebp / dwebp — WebP 변환 + potrace # 비트맵 → SVG 벡터화 + librsvg2-bin # rsvg-convert — SVG → 래스터 + tesseract-ocr # OCR 엔진 + tesseract-ocr-kor # 한국어 언어 데이터 + tesseract-ocr-eng # 영어 언어 데이터 + fonts-dejavu-core # 워터마크용 DejaVu 폰트 + libheif-dev # HEIC/HEIF 디코더 헤더 + libavif-dev # AVIF 디코더 헤더 +) + +for pkg in "${APT_PKGS[@]% *}"; do + pkg_name=$(echo "$pkg" | awk '{print $1}') + if dpkg -l "$pkg_name" 2>/dev/null | grep -q "^ii"; then + skip "$pkg_name" + else + echo " installing $pkg_name..." + $SUDO apt-get install -y "$pkg_name" -qq + ok "$pkg_name" + fi +done +fi # APT_OK + +# ────────────────────────────────────────────────── +# 2. Python 패키지 (필수) +# ────────────────────────────────────────────────── +echo "" +echo "[2/4] Python 패키지 (pip, 필수)" + +PIP_PKGS=( + "pillow>=10.0" # 이미지 처리 기본 + "pillow-avif-plugin" # AVIF 포맷 지원 + "pi-heif" # HEIC/HEIF 포맷 지원 + "opencv-python-headless" # 고급 이미지 처리 + "numpy" # 픽셀 배열 연산 + "scikit-image" # 노이즈 제거·모폴로지 등 고급 처리 + "Wand" # ImageMagick Python 바인딩 + "pdfplumber" # PDF 테이블 추출 (선·공백 혼합 방식) +) + +for spec in "${PIP_PKGS[@]% *}"; do + pkg_name=$(echo "$spec" | sed 's/[>=<].*//' | awk '{print $1}') + import_name=$(echo "$pkg_name" | tr '[:upper:]' '[:lower:]' | sed 's/-/_/g; s/pillow_avif_plugin/pillow_avif/; s/pillow/PIL/; s/scikit_image/skimage/; s/wand/wand.image/; s/pi_heif/pi_heif/; s/opencv_python_headless/cv2/') + if python3 -c "import $import_name" 2>/dev/null; then + skip "$pkg_name" + else + echo " installing $pkg_name..." + pip install -q --break-system-packages "$spec" + ok "$pkg_name" + fi +done + +# ────────────────────────────────────────────────── +# 3. Python 패키지 (선택: AI 배경 제거) +# ────────────────────────────────────────────────── +echo "" +echo "[3/4] Python 패키지 (선택: AI 배경 제거)" + +if [ "$WITH_REMBG" = true ]; then + if python3 -c "import rembg" 2>/dev/null; then + skip "rembg" + else + echo " installing rembg[cpu] (~500MB, onnxruntime 포함)..." + # --ignore-installed: apt 관리 패키지(jsonschema 등) uninstall 시도를 건너뛰고 + # ~/.local 사용자 공간에 설치 (시스템 패키지보다 우선 적용됨) + pip install -q --break-system-packages --ignore-installed "rembg[cpu]" 2>/dev/null + # rembg가 idna를 3.15로 올리면 roboflow(idna==3.7 필요)가 깨지므로 즉시 복구 + pip install -q --break-system-packages --ignore-installed "idna==3.7" 2>/dev/null + ok "rembg" + fi +else + echo " rembg: 건너뜀 (활성화: --with-rembg 플래그 추가)" +fi + +# ────────────────────────────────────────────────── +# 4. Tesseract 언어 데이터 확인 +# ────────────────────────────────────────────────── +echo "" +echo "[4/4] Tesseract 언어 데이터 확인" + +TESSDATA_DIR=$(find /usr/share/tesseract-ocr -name tessdata -type d 2>/dev/null | head -1) +if [ -z "$TESSDATA_DIR" ]; then + echo " 경고: tessdata 디렉토리를 찾을 수 없음" +else + for lang in eng kor; do + if [ -f "$TESSDATA_DIR/${lang}.traineddata" ]; then + ok "tesseract-$lang ($TESSDATA_DIR/${lang}.traineddata)" + else + echo " 경고: ${lang}.traineddata 없음 — sudo apt install tesseract-ocr-${lang}" + fi + done +fi + +# homeclaw 프로젝트 루트의 eng.traineddata (tesseract.js용) +PROJ_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +if [ -f "$PROJ_ROOT/eng.traineddata" ]; then + ok "tesseract.js eng.traineddata ($PROJ_ROOT/eng.traineddata)" +else + echo " tesseract.js eng.traineddata 없음 — 필요 시 다운로드:" + echo " curl -L https://github.com/naptha/tessdata/blob/gh-pages/4.0.0/eng.traineddata.gz?raw=true | gunzip > $PROJ_ROOT/eng.traineddata" +fi + +# ────────────────────────────────────────────────── +# 최종 확인 +# ────────────────────────────────────────────────── +echo "" +echo "=== 설치 확인 ===" +echo "" + +check() { + local label="$1"; shift + if "$@" &>/dev/null; then + echo -e " ${GREEN}✓${NC} $label" + else + echo -e " ${YELLOW}✗${NC} $label" + fi +} + +check "exiftool" exiftool --version +check "pngquant" pngquant --version +check "optipng" optipng --version +check "jpegoptim" jpegoptim --version +check "cwebp (WebP)" cwebp -version +check "potrace" potrace --version +check "rsvg-convert" rsvg-convert --version +check "tesseract" tesseract --version +check "imagemagick" convert --version +check "ffmpeg" ffmpeg -version + +echo "" +python3 - <<'PYCHECK' +import sys +GREEN = "\033[0;32m"; YELLOW = "\033[1;33m"; NC = "\033[0m" +def chk(label, mod): + try: + m = __import__(mod) + ver = getattr(m, "__version__", getattr(m, "VERSION", "ok")) + print(f" {GREEN}✓{NC} {label}: {ver}") + except ImportError: + print(f" {YELLOW}✗{NC} {label}: not found") + +chk("Pillow", "PIL") +chk("pillow-avif-plugin", "pillow_avif") +chk("pi-heif", "pi_heif") +chk("opencv", "cv2") +chk("numpy", "numpy") +chk("scikit-image", "skimage") +chk("Wand", "wand") +chk("pdfplumber", "pdfplumber") +chk("rembg (optional)", "rembg") +PYCHECK + +echo "" +echo "=== 완료 ===" +echo "" +echo "image_info / image_edit 툴 사용 가능:" +echo " - crop, resize, rotate, flip, grayscale" +echo " - brightness, contrast, sharpen, thumbnail, watermark, convert" +echo " - HEIC, AVIF, WebP, PNG, JPEG, TIFF, BMP, GIF 포맷 지원" +[ "$WITH_REMBG" = true ] && echo " - rembg 배경 제거 (AI)" +echo "" diff --git a/src/gateway/server-v2.ts b/src/gateway/server-v2.ts index 7438bc7..49a90f4 100644 --- a/src/gateway/server-v2.ts +++ b/src/gateway/server-v2.ts @@ -1565,7 +1565,7 @@ function buildTools() { type: 'function', function: { name: 'ollama_web_search', - description: 'Ollama 모델이 DuckDuckGo 웹 검색을 수행하고 결과를 자연어로 종합해서 반환합니다. 검색 결과 해석·요약이 필요할 때 web_search 대신 사용.', + description: 'Ollama 로컬 모델이 DuckDuckGo로 검색 후 결과를 자연어로 종합합니다. 느리고 로컬 모델 품질에 의존. 뉴스·정보 검색은 반드시 web_search를 사용. 이 툴은 web_search가 없는 환경에서만 fallback으로 사용.', parameters: { type: 'object', required: ['query'], properties: {