File size: 3,792 Bytes
9344f01 d92e749 9344f01 617c061 9344f01 617c061 9344f01 d92e749 9344f01 d92e749 9344f01 264396f d92e749 617c061 9344f01 617c061 9344f01 3264ba1 9344f01 00d343f 9344f01 1a36a19 9344f01 1a36a19 617c061 9344f01 | 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 | """HF Spaces (Gradio SDK + ZeroGPU) 진입점.
HF Spaces 빌더가 자동으로 `python app.py` 를 실행한다. 로컬에서도 같은
파일로 미리보기 가능:
pip install -e ".[dev,llm,hf]"
KPAA_LLM_BACKEND=llama_cpp python app.py # 로컬 GGUF 로 UI 만 미리보기
# → http://127.0.0.1:7860
HF Spaces 환경에서는 자동으로 `SPACE_ID` 가 잡혀 ZeroGPU 백엔드가 활성화된다.
LAW_OC 는 Space Settings > Secrets 에 등록.
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
# HF Spaces 에서는 `pip install -e .` 가 동작하지 않는다 (requirements.txt 처리
# 시점에 app 파일이 아직 mount 되지 않음). 대신 src/ 를 sys.path 에 prepend.
# 로컬 editable install 환경에서도 무해.
sys.path.insert(0, str(Path(__file__).resolve().parent / "src"))
# ─── monkey-patch: Gradio /api_info schema bug ────────────────────────────
# Gradio 5.x 의 gradio_client.utils 가 JSON Schema 의 `additionalProperties: True`
# (bool, 합법적 형식) 를 dict 로만 가정해서 `if "const" in schema:` 에서 TypeError.
# get_type 와 _json_schema_to_python_type 모두 bool 입력을 안전하게 처리하도록 wrap.
import gradio_client.utils as _gc_utils # noqa: E402
_orig_get_type = _gc_utils.get_type
_orig_jstpt = _gc_utils._json_schema_to_python_type
def _safe_get_type(schema):
if not isinstance(schema, dict):
return ""
return _orig_get_type(schema)
def _safe_jstpt(schema, defs):
if not isinstance(schema, dict):
return "Any"
return _orig_jstpt(schema, defs)
_gc_utils.get_type = _safe_get_type
_gc_utils._json_schema_to_python_type = _safe_jstpt
# ──────────────────────────────────────────────────────────────────────────
# ─── HF Spaces ZeroGPU startup canary ─────────────────────────────────────
# HF Spaces 의 ZeroGPU 는 startup 시점에 module-level `@spaces.GPU` 함수가
# 적어도 하나 검출되어야 GPU 스케줄을 잡는다. 실제 GPU 작업은
# ZeroGPUBackend.stream_chat 안의 `_run_generate` 에서 일어나지만, 그건 함수
# 호출 시점에야 데코레이트되므로 startup 스캔에서 안 보임.
# 본 카나리는 호출되지 않으며, 단지 detector 통과용.
try:
import spaces # type: ignore[import-not-found]
@spaces.GPU(duration=1)
def _zerogpu_startup_canary() -> None:
"""HF Spaces ZeroGPU detector 통과용 sentinel."""
return None
except ImportError:
pass # 로컬 dev — spaces 패키지 없음
# ──────────────────────────────────────────────────────────────────────────
from kpaa.ui.gradio import build_app # noqa: E402
def main() -> None:
app = build_app()
# HF Spaces 는 7860 노출 표준. 로컬 미리보기도 동일 포트 사용.
port = int(os.environ.get("PORT", "7860"))
# 큐 활성화 — async generator (스트리밍) 이 작동하려면 필수.
# ssr_mode=False — Node SSR 서브프로세스 없이 순수 uvicorn 으로 단일 프로세스화.
# show_api=False — /api_info 노출 스킵 (위 monkey-patch 와 함께 belt-and-suspenders).
app.queue(max_size=20).launch(
server_name="0.0.0.0",
server_port=port,
show_error=True,
ssr_mode=False,
show_api=False,
)
if __name__ == "__main__":
main()
|