leechard / scripts /dev_local.py
nenae18's picture
Deploy SalonView AI (brand single-source)
3754951 verified
Raw
History Blame Contribute Delete
5.77 kB
"""ํŒ€์›์šฉ ๋กœ์ปฌ ๊ฐœ๋ฐœ ์„œ๋ฒ„ โ€” ์œ ๋ฃŒ 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",
)
# ๋ฌด๋ฃŒ ๊ฒฝ๋กœ๋กœ ๊ณ ์ •ํ•˜๋Š” ์„ค์ •. mock ํ”„๋กœ๋ฐ”์ด๋”๋Š” ์–ผ๊ตด ๋ฐ•์Šค๋งŒ ๊ทธ๋ ค์„œ ๋Œ๋ ค์ค€๋‹ค
# (app/services/comfyui_client.py ์˜ MockAIClient).
SAFE_ENV = {
"AI_PROVIDER": "mock",
"GEMINI_ENABLE_REAL_CALL": "false",
"GEMINI_REQUIRE_API_KEY": "false",
"COMFYUI_REQUIRE_SERVER": "false",
"VIDEO_ENABLED": "false", # Kling ์˜์ƒ๋„ ์œ ๋ฃŒ๋‹ค. ๋กœ์ปฌ์—์„  ๋ฒ„ํŠผ ์ž์ฒด๋ฅผ ์ˆจ๊ธด๋‹ค.
"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", # Redis/์›Œ์ปค ์—†์ด ์š”์ฒญ ์•ˆ์—์„œ ๋ฐ”๋กœ ์ฒ˜๋ฆฌ
"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) # .env ์™€ ์ƒ๋Œ€๊ฒฝ๋กœ๊ฐ€ ์ €์žฅ์†Œ ๋ฃจํŠธ ๊ธฐ์ค€์ด ๋˜๊ฒŒ
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())