From 7397061665d25135341d7fa06967201c2d7b1936 Mon Sep 17 00:00:00 2001 From: lee-hyeon-cheol Date: Mon, 21 Sep 2026 18:33:40 +0900 Subject: [PATCH] =?UTF-8?q?fix(opencode):=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=EA=B0=80=20=EA=B2=8C=EC=9D=B4=ED=8A=B8=EC=9B=A8=EC=9D=B4?= =?UTF-8?q?=EA=B9=8C=EC=A7=80=20=EC=95=88=20=EC=98=A4=EB=8D=98=20=EA=B2=83?= =?UTF-8?q?=20=E2=80=94=20vision=20=EB=AA=A8=EB=8D=B8=EC=97=90=20modalitie?= =?UTF-8?q?s.image=20=ED=91=9C=EC=8B=9C=20+=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=ED=84=B4=EC=9D=80=20OpenCode=20=EC=97=90=20gateway/605=20?= =?UTF-8?q?=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 고객사 실측: 게이트웨이 로그 parts=['text']. OpenCode 는 모델 설정에 이미지 입력 표시가 없으면 파트를 떨구고 텍스트만 보냄. render_opencode 가 AAF_FABRIX_VISION_MODEL_ID 모델에 attachment/modalities 를 붙이고, deploy.sh 가 OPENCODE_VISION_MODEL=gateway/605 를 채워 백엔드가 이미지 턴에 그 모델을 지정. Co-Authored-By: Claude Fable 5.1 --- 5_django_backend/.env.example | 2 +- 5_django_backend/deploy.sh | 4 +++- 5_django_backend/opencode/render_opencode.py | 12 ++++++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/5_django_backend/.env.example b/5_django_backend/.env.example index 6808026..3ec5972 100644 --- a/5_django_backend/.env.example +++ b/5_django_backend/.env.example @@ -2,7 +2,7 @@ OPENCODE_BASE_URL=http://localhost:4096 OPENCODE_AGENT=codeassist # 이미지 요청만 이 모델로 (로컬 OpenRouter). 고객사는 비우고 AAF_FABRIX_VISION_MODEL_ID 사용 -OPENCODE_VISION_MODEL=openrouter/google/gemma-4-31b-it +OPENCODE_VISION_MODEL=openrouter/google/gemma-4-31b-it # 고객사는 gateway/605 (deploy.sh 가 채움) # 토큰 서명 키 (비우면 data/.auth_secret 자동 생성) AUTH_SECRET= diff --git a/5_django_backend/deploy.sh b/5_django_backend/deploy.sh index d402923..1c77d91 100644 --- a/5_django_backend/deploy.sh +++ b/5_django_backend/deploy.sh @@ -14,7 +14,9 @@ grep -q '^DB_SSLMODE=' .env || echo 'DB_SSLMODE=disable' >> .env grep -q '^STREAM_FIRST_EVENT_TIMEOUT_S=' .env || echo 'STREAM_FIRST_EVENT_TIMEOUT_S=300' >> .env grep -q '^AAF_FABRIX_READ_TIMEOUT_S=' .env || echo 'AAF_FABRIX_READ_TIMEOUT_S=300' >> .env grep -q '^ABAP_INDEX_URL=' .env || echo 'ABAP_INDEX_URL=http://127.0.0.1:8100' >> .env -grep -q '^BACKEND_PORT=' .env || echo 'BACKEND_PORT=8080' >> .env # OpenCode 가 붙을 Django 포트. 없으면 8001 로 렌더돼 'Unable to connect' +grep -q '^BACKEND_PORT=' .env || echo 'BACKEND_PORT=8080' >> .env +# 이미지 붙은 턴은 백엔드가 OpenCode 에 이 모델을 지정 — 기본(339)으로 가면 OpenCode 가 이미지를 떨굼 +grep -q '^OPENCODE_VISION_MODEL=' .env || echo 'OPENCODE_VISION_MODEL=gateway/605' >> .env # OpenCode 가 붙을 Django 포트. 없으면 8001 로 렌더돼 'Unable to connect' sed -i 's/^AAF_FABRIX_MODEL_ID=.*/AAF_FABRIX_MODEL_ID=339/; s/^AAF_FABRIX_MODELS=.*/AAF_FABRIX_MODELS=339:GaussO Flash,581:GaussO Think,605:Gemma4/' .env .venv/bin/python manage.py migrate --noinput [ -f seed/snippets.db ] && .venv/bin/python manage.py import_snippets seed/snippets.db diff --git a/5_django_backend/opencode/render_opencode.py b/5_django_backend/opencode/render_opencode.py index 17a446b..a0b3c83 100644 --- a/5_django_backend/opencode/render_opencode.py +++ b/5_django_backend/opencode/render_opencode.py @@ -34,7 +34,7 @@ def load_env(path: Path) -> dict[str, str]: k, v = line.split("=", 1) v = v.split(" #", 1)[0].strip().strip("\"'") # 줄 끝 주석은 두 칸 띄고 # out[k.strip()] = v - out.update({k: v for k, v in os.environ.items() if k.startswith(("AAF_", "BACKEND_PORT", "MCP_"))}) + out.update({k: v for k, v in os.environ.items() if k.startswith(("AAF_", "BACKEND_PORT", "MCP_", "OPENCODE_"))}) return out @@ -43,7 +43,15 @@ def render(env: dict[str, str]) -> str: (env.get("AAF_FABRIX_MODEL_ID") or "default"): "FabriX" } values = dict(env) - values["AAF_FABRIX_MODELS_JSON"] = json.dumps({mid: {"name": name} for mid, name in models.items()}, ensure_ascii=False) + # vision 모델엔 이미지 입력 가능 표시 — 없으면 OpenCode 가 이미지 파트를 떨구고 텍스트만 보냄(고객사 실측: parts=['text']) + vision = (env.get("AAF_FABRIX_VISION_MODEL_ID") or "").strip() + def _model(mid: str, name: str) -> dict: + m: dict = {"name": name} + if mid == vision: + m["attachment"] = True + m["modalities"] = {"input": ["text", "image"], "output": ["text"]} + return m + values["AAF_FABRIX_MODELS_JSON"] = json.dumps({mid: _model(mid, name) for mid, name in models.items()}, ensure_ascii=False) values["AAF_FABRIX_DEFAULT_MODEL"] = next(iter(models)) values.setdefault("BACKEND_PORT", "8001") values.setdefault("MCP_PORT", "8888")