feat(backend): 사내 LLM(FabriX) 게이트웨이 /api/ito + OpenCode FabriX 설정 렌더

- apps/gateway: OpenCode→FabriX 통과 중계. 헤더 3종 인증, x-llm-model-id 로 모델 선택, 401 시 Bearer/날것×클라이언트 헤더 재시도, 모델 허용 목록. ABAP_OPENCODE apps_ito 통째 복사 대신 200줄로
- opencode/opencode.fabrix.json.tmpl + render_opencode.py — .env AAF_* 로 렌더
- .env.example AAF 블록(기본 605 Gemma4, TOKEN_PREFIX=bearer). 파서가 줄 끝 # 주석을 값으로 읽던 것 수정
- tests/test_gateway.py 9개(MockTransport)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
lee-hyeon-cheol
2026-09-17 16:34:53 +09:00
co-authored by Claude Fable 5.1
parent 47d31dbe06
commit 435952f25c
14 changed files with 620 additions and 1 deletions
+9
View File
@@ -25,6 +25,8 @@ def _load_dotenv(path: Path) -> None:
if not line or line.startswith("#") or "=" not in line:
continue
k, v = line.split("=", 1)
# 줄 끝 주석(공백 뒤 #)은 값이 아님 — 안 떼면 HTTP 헤더에 한글이 실려 httpx 가 죽음(고객사 실측)
v = v.split(" #", 1)[0].split(" #", 1)[0]
os.environ.setdefault(k.strip(), v.strip().strip('"\x27'))
@@ -73,6 +75,12 @@ OPENCODE_BASE_URL = _env("OPENCODE_BASE_URL", "http://localhost:4096").rstrip("/
OPENCODE_DIRECTORY = _env("OPENCODE_DIRECTORY") or str((BASE_DIR / "opencode").resolve())
# OpenCode 에 넘길 에이전트 이름 (opencode/.opencode/agent/<이름>.md)
OPENCODE_AGENT = _env("OPENCODE_AGENT", "codeassist")
# 사내 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
FABRIX_ENV = {k: _env(k) for k in _FABRIX_KEYS}
# 세션 컨텍스트 하드 한도 — usage.limit 로 프론트 게이지에 감
CONTEXT_LIMIT_TOKENS = int(_env("CONTEXT_LIMIT_TOKENS", "128000") or "128000")
@@ -87,6 +95,7 @@ INSTALLED_APPS = [
"corsheaders",
"apps.accounts",
"apps.chat",
"apps.gateway",
]
MIDDLEWARE = [