feat(backend): 스트림 타임아웃 env 화 — 첫 이벤트 300s, FabriX 조각 간 300s 기본

Gemma4 가 큐 밀리면 '안녕'에도 200초라 첫 이벤트 60s 로는 no-events 로 잘림.
STREAM_FIRST_EVENT_TIMEOUT_S / STREAM_TURN_TIMEOUT_S / AAF_FABRIX_READ_TIMEOUT_S / AAF_FABRIX_TOTAL_TIMEOUT_S 를 .env 로.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
lee-hyeon-cheol
2026-09-21 13:31:00 +09:00
co-authored by Claude Fable 5.1
parent e9e644b15b
commit aa37cef13d
5 changed files with 20 additions and 3 deletions
+7
View File
@@ -16,6 +16,10 @@ DB_SCHEMA=codeassist
# 프론트 origin (dev 는 Vite proxy 라 안 탐. Tauri 빌드용) # 프론트 origin (dev 는 Vite proxy 라 안 탐. Tauri 빌드용)
CORS_ORIGINS=http://localhost:15173,http://tauri.localhost,tauri://localhost 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)에선 전부 비워둠 ── # ── 사내 LLM(FabriX) — 고객사 전용. 개발(OpenRouter)에선 전부 비워둠 ──
# OpenCode 는 opencode/opencode.json 을 FabriX 용으로 렌더해서(opencode/render_opencode.py) 이 서버의 /api/ito 로 붙음. # OpenCode 는 opencode/opencode.json 을 FabriX 용으로 렌더해서(opencode/render_opencode.py) 이 서버의 /api/ito 로 붙음.
AAF_FABRIX_BASE_URL= AAF_FABRIX_BASE_URL=
@@ -31,6 +35,9 @@ AAF_FABRIX_CLIENT_HEADER=
AAF_FABRIX_TOKEN_PREFIX=bearer AAF_FABRIX_TOKEN_PREFIX=bearer
AAF_FABRIX_MAX_TOKENS= AAF_FABRIX_MAX_TOKENS=
AAF_RELAY_STREAM_USAGE= AAF_RELAY_STREAM_USAGE=
# FabriX 스트림 타임아웃(초) — 조각 사이 무수신 / 전체. 기본 300 / 600
AAF_FABRIX_READ_TIMEOUT_S=300
AAF_FABRIX_TOTAL_TIMEOUT_S=600
# OpenCode → 이 서버 사이 잠금. 같은 PC 안이면 비워도 됨. 서버를 바깥에 열면 반드시 랜덤 문자열 # OpenCode → 이 서버 사이 잠금. 같은 PC 안이면 비워도 됨. 서버를 바깥에 열면 반드시 랜덤 문자열
AAF_GATEWAY_KEY= AAF_GATEWAY_KEY=
# OpenCode 가 붙을 이 서버 포트 (render_opencode.py 가 씀) # OpenCode 가 붙을 이 서버 포트 (render_opencode.py 가 씀)
+3 -2
View File
@@ -37,10 +37,11 @@ log = logging.getLogger(__name__)
ALLOWED_IMAGE_TYPES = {"image/png", "image/jpeg", "image/webp"} ALLOWED_IMAGE_TYPES = {"image/png", "image/jpeg", "image/webp"}
MAX_IMAGES = 4 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 를 받았는데 이 세션 이벤트가 하나도 안 오면 OpenCode 가 조용히 실패한 것
# (예: 다른 프로젝트의 세션 — 로그에만 "prompt_async failed" 남고 session.error 안 옴) # (예: 다른 프로젝트의 세션 — 로그에만 "prompt_async failed" 남고 session.error 안 옴)
FIRST_EVENT_TIMEOUT_S = 60 FIRST_EVENT_TIMEOUT_S = settings.STREAM_FIRST_EVENT_TIMEOUT_S # 기본 300
KEEPALIVE_S = 15 KEEPALIVE_S = 15
_END = object() # 큐 종료 표시 _END = object() # 큐 종료 표시
+5 -1
View File
@@ -31,6 +31,8 @@ ENV_KEYS = (
"AAF_RELAY_STREAM_USAGE", # "1" 이면 stream_options.include_usage — 제공자가 거부하면 끔 "AAF_RELAY_STREAM_USAGE", # "1" 이면 stream_options.include_usage — 제공자가 거부하면 끔
"AAF_GATEWAY_KEY", # OpenCode 가 Authorization: Bearer 로 보내는 키. 비우면 검사 안 함 "AAF_GATEWAY_KEY", # OpenCode 가 Authorization: Bearer 로 보내는 키. 비우면 검사 안 함
"AAF_FABRIX_VISION_MODEL_ID", # 이미지가 붙은 요청만 이 모델로(Gemma4). 비우면 분기 안 함 "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 = "" gateway_key: str = ""
vision_model_id: str = "" # 이미지 있을 때만 쓰는 모델. Gemma 가 느려서 텍스트는 기본 모델로 vision_model_id: str = "" # 이미지 있을 때만 쓰는 모델. Gemma 가 느려서 텍스트는 기본 모델로
connect_timeout_s: float = 10.0 connect_timeout_s: float = 10.0
read_timeout_s: float = 120.0 # 스트림 조각 사이 무수신 한계 read_timeout_s: float = 300.0 # 스트림 조각 사이 무수신 한계
total_timeout_s: float = 600.0 total_timeout_s: float = 600.0
@classmethod @classmethod
@@ -102,6 +104,8 @@ class FabrixConfig:
stream_usage=g("AAF_RELAY_STREAM_USAGE") == "1", stream_usage=g("AAF_RELAY_STREAM_USAGE") == "1",
gateway_key=g("AAF_GATEWAY_KEY"), gateway_key=g("AAF_GATEWAY_KEY"),
vision_model_id=g("AAF_FABRIX_VISION_MODEL_ID"), 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]: def missing(self) -> list[str]:
+4
View File
@@ -76,6 +76,10 @@ OPENCODE_DIRECTORY = _env("OPENCODE_DIRECTORY") or str((BASE_DIR / "opencode").r
# OpenCode 에 넘길 에이전트 이름 (opencode/.opencode/agent/<이름>.md) # OpenCode 에 넘길 에이전트 이름 (opencode/.opencode/agent/<이름>.md)
OPENCODE_AGENT = _env("OPENCODE_AGENT", "codeassist") 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 로 중계. # 사내 LLM(FabriX) 게이트웨이 — 고객사에선 OpenCode 가 /api/ito 로 붙고 apps/gateway 가 FabriX 로 중계.
# 키 이름은 ABAP_OPENCODE 와 같음(AAF_*). 전부 비어 있으면 /api/ito/chat/completions 만 503 이고 나머진 멀쩡. # 키 이름은 ABAP_OPENCODE 와 같음(AAF_*). 전부 비어 있으면 /api/ito/chat/completions 만 503 이고 나머진 멀쩡.
from apps.gateway.fabrix import ENV_KEYS as _FABRIX_KEYS # noqa: E402 from apps.gateway.fabrix import ENV_KEYS as _FABRIX_KEYS # noqa: E402
+1
View File
@@ -7,3 +7,4 @@
| 10:40 | PwC 로고·마크 전부 제거(LogoMark 빈 렌더, 파비콘 투명), 기본 모드 라이트로(예전 dark 저장값 한 번 초기화), 삼성 팔레트 포인트를 Samsung Blue #1428A0 로(버튼·링·링크) | | 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 번들 재활용, 컨테이너 간 통신 없이) | | 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:24 | Esc 로 답변 중단 — 생성 중이면 Esc 가 중단(클로드처럼), 아니면 목록으로. 재진입 상태에서도 백엔드 cancel. 페이지 테스트 2개 |
| 13:30 | 스트림 타임아웃 env 화 — 첫 이벤트 60→300s, FabriX 조각 간 120→300s (Gemma4 '안녕' 200초 대비). 한 턴 600 유지 |