diff --git a/5_django_backend/.env.example b/5_django_backend/.env.example index 1aeef2a..f35bdf0 100644 --- a/5_django_backend/.env.example +++ b/5_django_backend/.env.example @@ -16,6 +16,10 @@ DB_SCHEMA=codeassist # 프론트 origin (dev 는 Vite proxy 라 안 탐. Tauri 빌드용) CORS_ORIGINS=http://localhost:15173,http://tauri.localhost,tauri://localhost +# 채팅 스트림 타임아웃(초) — Gemma4 느릴 때 올림. 첫 이벤트까지 / 한 턴 전체 +STREAM_FIRST_EVENT_TIMEOUT_S=300 +STREAM_TURN_TIMEOUT_S=600 + # ── 사내 LLM(FabriX) — 고객사 전용. 개발(OpenRouter)에선 전부 비워둠 ── # OpenCode 는 opencode/opencode.json 을 FabriX 용으로 렌더해서(opencode/render_opencode.py) 이 서버의 /api/ito 로 붙음. AAF_FABRIX_BASE_URL= @@ -31,6 +35,9 @@ AAF_FABRIX_CLIENT_HEADER= AAF_FABRIX_TOKEN_PREFIX=bearer AAF_FABRIX_MAX_TOKENS= AAF_RELAY_STREAM_USAGE= +# FabriX 스트림 타임아웃(초) — 조각 사이 무수신 / 전체. 기본 300 / 600 +AAF_FABRIX_READ_TIMEOUT_S=300 +AAF_FABRIX_TOTAL_TIMEOUT_S=600 # OpenCode → 이 서버 사이 잠금. 같은 PC 안이면 비워도 됨. 서버를 바깥에 열면 반드시 랜덤 문자열 AAF_GATEWAY_KEY= # OpenCode 가 붙을 이 서버 포트 (render_opencode.py 가 씀) diff --git a/5_django_backend/apps/chat/stream.py b/5_django_backend/apps/chat/stream.py index fbc1501..27b6202 100644 --- a/5_django_backend/apps/chat/stream.py +++ b/5_django_backend/apps/chat/stream.py @@ -37,10 +37,11 @@ log = logging.getLogger(__name__) ALLOWED_IMAGE_TYPES = {"image/png", "image/jpeg", "image/webp"} MAX_IMAGES = 4 -TURN_TIMEOUT_S = 600 # OpenCode 답변 상한 +# 타임아웃은 .env 로 조정 (settings.STREAM_*). Gemma4 가 큐 밀리면 첫 토큰도 늦어서 넉넉히. +TURN_TIMEOUT_S = settings.STREAM_TURN_TIMEOUT_S # OpenCode 답변 상한 (기본 600) # prompt 를 받았는데 이 세션 이벤트가 하나도 안 오면 OpenCode 가 조용히 실패한 것 # (예: 다른 프로젝트의 세션 — 로그에만 "prompt_async failed" 남고 session.error 안 옴) -FIRST_EVENT_TIMEOUT_S = 60 +FIRST_EVENT_TIMEOUT_S = settings.STREAM_FIRST_EVENT_TIMEOUT_S # 기본 300 KEEPALIVE_S = 15 _END = object() # 큐 종료 표시 diff --git a/5_django_backend/apps/gateway/fabrix.py b/5_django_backend/apps/gateway/fabrix.py index 3cc5cbc..bf359b8 100644 --- a/5_django_backend/apps/gateway/fabrix.py +++ b/5_django_backend/apps/gateway/fabrix.py @@ -31,6 +31,8 @@ ENV_KEYS = ( "AAF_RELAY_STREAM_USAGE", # "1" 이면 stream_options.include_usage — 제공자가 거부하면 끔 "AAF_GATEWAY_KEY", # OpenCode 가 Authorization: Bearer 로 보내는 키. 비우면 검사 안 함 "AAF_FABRIX_VISION_MODEL_ID", # 이미지가 붙은 요청만 이 모델로(Gemma4). 비우면 분기 안 함 + "AAF_FABRIX_READ_TIMEOUT_S", # 스트림 조각 사이 무수신 한계(기본 300). Gemma 가 중간에 멈추는 날 대비 + "AAF_FABRIX_TOTAL_TIMEOUT_S", # 요청 전체 상한(기본 600) ) @@ -81,7 +83,7 @@ class FabrixConfig: gateway_key: str = "" vision_model_id: str = "" # 이미지 있을 때만 쓰는 모델. Gemma 가 느려서 텍스트는 기본 모델로 connect_timeout_s: float = 10.0 - read_timeout_s: float = 120.0 # 스트림 조각 사이 무수신 한계 + read_timeout_s: float = 300.0 # 스트림 조각 사이 무수신 한계 total_timeout_s: float = 600.0 @classmethod @@ -102,6 +104,8 @@ class FabrixConfig: stream_usage=g("AAF_RELAY_STREAM_USAGE") == "1", gateway_key=g("AAF_GATEWAY_KEY"), vision_model_id=g("AAF_FABRIX_VISION_MODEL_ID"), + read_timeout_s=float(g("AAF_FABRIX_READ_TIMEOUT_S") or 300), + total_timeout_s=float(g("AAF_FABRIX_TOTAL_TIMEOUT_S") or 600), ) def missing(self) -> list[str]: diff --git a/5_django_backend/config/settings.py b/5_django_backend/config/settings.py index 048bab3..8823769 100644 --- a/5_django_backend/config/settings.py +++ b/5_django_backend/config/settings.py @@ -76,6 +76,10 @@ OPENCODE_DIRECTORY = _env("OPENCODE_DIRECTORY") or str((BASE_DIR / "opencode").r # OpenCode 에 넘길 에이전트 이름 (opencode/.opencode/agent/<이름>.md) OPENCODE_AGENT = _env("OPENCODE_AGENT", "codeassist") +# 채팅 스트림 타임아웃(초). Gemma4 가 "안녕"에도 200초 걸리는 날이 있어 첫 이벤트 300, 한 턴 600. +STREAM_TURN_TIMEOUT_S = int(_env("STREAM_TURN_TIMEOUT_S", "600")) +STREAM_FIRST_EVENT_TIMEOUT_S = int(_env("STREAM_FIRST_EVENT_TIMEOUT_S", "300")) + # 사내 LLM(FabriX) 게이트웨이 — 고객사에선 OpenCode 가 /api/ito 로 붙고 apps/gateway 가 FabriX 로 중계. # 키 이름은 ABAP_OPENCODE 와 같음(AAF_*). 전부 비어 있으면 /api/ito/chat/completions 만 503 이고 나머진 멀쩡. from apps.gateway.fabrix import ENV_KEYS as _FABRIX_KEYS # noqa: E402 diff --git a/z-my-docs/work-log/2026-09/2026-09-21.md b/z-my-docs/work-log/2026-09/2026-09-21.md index 6d12a44..18a70cc 100644 --- a/z-my-docs/work-log/2026-09/2026-09-21.md +++ b/z-my-docs/work-log/2026-09/2026-09-21.md @@ -7,3 +7,4 @@ | 10:40 | PwC 로고·마크 전부 제거(LogoMark 빈 렌더, 파비콘 투명), 기본 모드 라이트로(예전 dark 저장값 한 번 초기화), 삼성 팔레트 포인트를 Samsung Blue #1428A0 로(버튼·링·링크) | | 10:43 | SAP MCP 를 -12 에 따로 띄우는 준비 — OpenCode 템플릿 sap-icf 를 MCP_API_KEY 유무로 on/off 렌더, .env.example SAP 블록, README 절차(-13 번들 재활용, 컨테이너 간 통신 없이) | | 13:24 | Esc 로 답변 중단 — 생성 중이면 Esc 가 중단(클로드처럼), 아니면 목록으로. 재진입 상태에서도 백엔드 cancel. 페이지 테스트 2개 | +| 13:30 | 스트림 타임아웃 env 화 — 첫 이벤트 60→300s, FabriX 조각 간 120→300s (Gemma4 '안녕' 200초 대비). 한 턴 600 유지 |