| """ํ์์ฉ ๋ก์ปฌ ๊ฐ๋ฐ ์๋ฒ โ ์ ๋ฃ API ํธ์ถ์ด '๊ตฌ์กฐ์ ์ผ๋ก' ๋ถ๊ฐ๋ฅํ ์ํ๋ก ๋์ด๋ค. |
| |
| ์ ์ด ์คํฌ๋ฆฝํธ๊ฐ ํ์ํ๊ฐ |
| ------------------------- |
| ์ด ์ฑ์ ์ ๋ฃ ๊ฒฝ๋ก(fal.ai ์ด๋ฏธ์งยท์์, Replicate, Gemini)๋ ์ ๋ถ ํ๋ก์ธ์ค |
| ํ๊ฒฝ๋ณ์๋ก๋ง ์ด๋ฆฐ๋ค. `app/services/fal_client.py` ์ |
| |
| def fal_key() -> str: |
| return (os.getenv("FAL_KEY") or "").strip() |
| |
| ๊ฐ ๋น๋ฉด ๋ชจ๋ ํธ์ถ์ด `FalUnavailable("... no network call made")` ๋ก ๋๊ธด๋ค. |
| ์ ์ฅ์ ์ด๋์๋ `load_dotenv` ๊ฐ ์์ผ๋ฏ๋ก `.env` ํ์ผ์ ํค๋ฅผ ์ ์ด๋ ์ด ๊ฒฝ๋ก์๋ |
| ๋ฟ์ง ์๋๋ค. ์ฆ **๋์ฐ๋ ํ๋ก์ธ์ค์์ ํค๋ฅผ ์ง์ฐ๋ฉด ๊ณผ๊ธ์ด ๋ฐ์ํ ์ ์๋ค.** |
| |
| ์ด ์คํฌ๋ฆฝํธ๋ ๊ทธ ์ํ๋ฅผ ๋ง๋ค๊ณ , ๋ง๋ค์๋ค๊ณ ๋ฏฟ์ง ์๊ณ **์ค์ ๋ก ํ์ธํ ๋ค** ์๋ฒ๋ฅผ |
| ๋์ด๋ค. ํ์ธ์ ์คํจํ๋ฉด ์๋ฒ๋ฅผ ๋์ฐ์ง ์๋๋ค. |
| |
| ์ฌ์ฉ๋ฒ |
| ------ |
| python scripts/dev_local.py # 8223 ๋ฒ์ผ๋ก ์๋ฒ ์คํ |
| python scripts/dev_local.py --check # ์์ ์ฅ์น๋ง ํ์ธํ๊ณ ์ข
๋ฃ (์๋ฒ ์ ๋ธ) |
| python scripts/dev_local.py --port 9000 |
| |
| ์ค์ AI ๊ฒฐ๊ณผ๊ฐ ํ์ํ๋ฉด ์ด ์คํฌ๋ฆฝํธ๋ฅผ ์ฐ์ง ๋ง๊ณ , ํค๋ฅผ ์ง์ ๋ฃ์ด ๋ณ๋๋ก ๋์ด๋ค. |
| ๊ทธ๊ฑด ๋์ด ๋๊ฐ๋ ์คํ์ด๋ผ ์ผ๋ถ๋ฌ ์ฌ๊ธฐ์ ๋ง์ ๋์๋ค. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import os |
| import sys |
| from pathlib import Path |
|
|
| BACKEND = Path(__file__).resolve().parent.parent |
|
|
| |
| PAID_KEYS = ( |
| "FAL_KEY", |
| "REPLICATE_API_TOKEN", |
| "GEMINI_API_KEY", |
| "GOOGLE_API_KEY", |
| "OPENAI_API_KEY", |
| "ANTHROPIC_API_KEY", |
| ) |
|
|
| |
| |
| SAFE_ENV = { |
| "AI_PROVIDER": "mock", |
| "GEMINI_ENABLE_REAL_CALL": "false", |
| "GEMINI_REQUIRE_API_KEY": "false", |
| "COMFYUI_REQUIRE_SERVER": "false", |
| "VIDEO_ENABLED": "false", |
| "ENV": "local", |
| "ACCESS_CODE": "", |
| "ACCESS_CODES": "", |
| "DATABASE_URL": "sqlite+pysqlite:///./dev_local.db", |
| "STORAGE_BACKEND": "local", |
| "LOCAL_STORAGE_DIR": "./dev_local_storage", |
| "CELERY_TASK_ALWAYS_EAGER": "true", |
| "CELERY_TASK_EAGER_PROPAGATES": "true", |
| } |
|
|
|
|
| def harden() -> list[str]: |
| """์ ๋ฃ ํค๋ฅผ ์ง์ฐ๊ณ ๋ฌด๋ฃ ์ค์ ์ ๊ฐ์ ํ๋ค. ์ง์์ง ํค ์ด๋ฆ์ ๋๋ ค์ค๋ค.""" |
| removed = [k for k in PAID_KEYS if (os.environ.get(k) or "").strip()] |
| for k in PAID_KEYS: |
| os.environ[k] = "" |
| for k, v in SAFE_ENV.items(): |
| os.environ[k] = v |
| return removed |
|
|
|
|
| def verify() -> list[str]: |
| """์ค์ ํ๋ค๊ณ ๋ฏฟ์ง ์๊ณ ์ฑ ์ฝ๋์ ์ง์ ๋ฌผ์ด๋ณธ๋ค. ๋ฌธ์ ๋ฅผ ๋ฌธ์์ด ๋ชฉ๋ก์ผ๋ก ๋๋ ค์ค๋ค.""" |
| problems: list[str] = [] |
|
|
| from app.services.fal_client import fal_real_enabled |
| if fal_real_enabled(): |
| problems.append("fal ์ ๋ฃ ํธ์ถ์ด ์์ง ์ด๋ ค ์๋ค (FAL_KEY ๊ฐ ์ด์ ์์)") |
|
|
| from app.core.config import get_settings |
| s = get_settings() |
| if s.ai_provider != "mock": |
| problems.append(f"AI_PROVIDER ๊ฐ mock ์ด ์๋๋ค: {s.ai_provider!r}") |
| if s.gemini_enable_real_call: |
| problems.append("GEMINI_ENABLE_REAL_CALL ์ด ์ผ์ ธ ์๋ค") |
| if s.video_enabled: |
| problems.append("VIDEO_ENABLED ๊ฐ ์ผ์ ธ ์๋ค (Kling ์์์ ์ ๋ฃ)") |
|
|
| from app.services.comfyui_client import get_ai_client, MockAIClient |
| client = get_ai_client() |
| if not isinstance(client, MockAIClient): |
| problems.append(f"AI ํด๋ผ์ด์ธํธ๊ฐ mock ์ด ์๋๋ค: {type(client).__name__}") |
|
|
| return problems |
|
|
|
|
| def main() -> int: |
| ap = argparse.ArgumentParser(description="๊ณผ๊ธ ๋ถ๊ฐ ๋ก์ปฌ ๊ฐ๋ฐ ์๋ฒ") |
| ap.add_argument("--port", type=int, default=8223) |
| ap.add_argument("--host", default="127.0.0.1") |
| ap.add_argument("--check", action="store_true", help="์์ ์ฅ์น๋ง ํ์ธํ๊ณ ์ข
๋ฃ") |
| args = ap.parse_args() |
|
|
| os.chdir(BACKEND) |
| sys.path.insert(0, str(BACKEND)) |
|
|
| removed = harden() |
| problems = verify() |
|
|
| print("=" * 62) |
| print(" SalonView AI โ ๋ก์ปฌ ๊ฐ๋ฐ ์๋ฒ (๊ณผ๊ธ ๋ถ๊ฐ ๋ชจ๋)") |
| print("=" * 62) |
| if removed: |
| print(f" ์ด ํ๋ก์ธ์ค์์ ์ ๊ฑฐํ ์ ๋ฃ ํค : {', '.join(removed)}") |
| print(" (์ ์ญ ํ๊ฒฝ์ ๊ฐ์ ๊ทธ๋๋ก๋ค. ์ด ์๋ฒ๋ง ๋ชป ๋ณผ ๋ฟ์ด๋ค.)") |
| else: |
| print(" ์ด ํ๊ฒฝ์๋ ์ ๋ฃ ํค๊ฐ ์ ์ด์ ์๋ค.") |
| print(" AI ํ๋ก๋ฐ์ด๋ : mock (์ผ๊ตด ๋ฐ์ค๋ง ๊ทธ๋ ค์ ๋ฐํ)") |
| print(" fal.ai ์ด๋ฏธ์ง/์์ : ์ฐจ๋จ โ no network call made") |
| print(" Gemini ์คํธ์ถ : off") |
| print(" DB / ์ ์ฅ์ : ./dev_local.db, ./dev_local_storage") |
| print(" Redis : ๋ถํ์ (Celery eager)") |
|
|
| if problems: |
| print("-" * 62) |
| print(" ์์ ์ฅ์น ํ์ธ ์คํจ โ ์๋ฒ๋ฅผ ๋์ฐ์ง ์๋๋ค:") |
| for p in problems: |
| print(f" - {p}") |
| print("=" * 62) |
| return 1 |
|
|
| print(" ์์ ์ฅ์น ํ์ธ : ํต๊ณผ") |
| print("=" * 62) |
|
|
| if args.check: |
| return 0 |
|
|
| print(f" http://{args.host}:{args.port}/ ๋ก ์ ์\n") |
| import uvicorn |
| uvicorn.run("app.main:app", host=args.host, port=args.port, reload=False) |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|