From 01bf8aa2585867e066c98fcdc5299554357cc4d1 Mon Sep 17 00:00:00 2001 From: CD Date: Sat, 7 Mar 2026 00:48:30 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20gemini-3-flash-preview=20+=20=EC=97=AD?= =?UTF-8?q?=ED=95=A0=EB=B3=84=20thinkingBudget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 모델: gemini-3-flash-preview (2.0 Flash → 3 Flash 업그레이드) - 역할별 thinkingBudget 동적 조절: unified/summarizer = 512 (가벼운 분류/요약) planner = 4096 (계획/검수) coder/reviewer = 8192 (구현/비평) - ~/.gemini/settings.json에 호출 직전 반영 - Docker에서도 settings.json 마운트로 동작 --- core/gemini_caller.py | 54 +++++++++++++++++++++++++++++++++++++------ workspaces.json | 17 ++++++++++++++ 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/core/gemini_caller.py b/core/gemini_caller.py index 8b5f695..06d05e8 100644 --- a/core/gemini_caller.py +++ b/core/gemini_caller.py @@ -1,12 +1,11 @@ """GeminiCaller — gemini CLI 호출. -두 가지 모드: -1. call() — 텍스트 입출력 (분류/리뷰/총평) -2. call_agent() — 프로젝트 디렉토리에서 에이전트 실행 (코딩) - → Gemini가 직접 파일 읽기/쓰기/명령 실행 +모든 역할이 gemini-3-flash-preview 모델을 사용하며, +역할별 thinkingBudget을 동적으로 조절합니다. """ import asyncio +import json import logging import time import sys @@ -15,6 +14,18 @@ from pathlib import Path logger = logging.getLogger("variet.gemini") ROLE_PROMPTS_DIR = Path(__file__).parent.parent / "prompts" +GEMINI_MODEL = "gemini-3-flash-preview" + +# 역할별 thinkingBudget (토큰 단위) +# 512=가벼운 분류/요약, 4096=계획/검수, 8192=구현/비평 +ROLE_THINKING: dict[str, int] = { + "unified": 512, + "summarizer": 512, + "planner": 4096, + "coder": 8192, + "reviewer": 8192, +} +DEFAULT_THINKING = 4096 # 동시 호출 제한 (Gemini AI Ultra 120RPM 고려) _semaphore = asyncio.Semaphore(4) @@ -22,6 +33,9 @@ _semaphore = asyncio.Semaphore(4) # Windows에서 PS ExecutionPolicy 우회 _IS_WIN = sys.platform == "win32" +# ~/.gemini/settings.json 경로 +_SETTINGS_PATH = Path.home() / ".gemini" / "settings.json" + class GeminiCallError(Exception): """Gemini CLI 호출 실패.""" @@ -37,10 +51,34 @@ class GeminiCaller: self.last_call_time = 0.0 def _build_cmd(self): - """OS에 맞는 gemini 커맨드 빌드.""" + """에 맞는 gemini 커맨드 빌드.""" if _IS_WIN: - return ["cmd", "/c", "gemini", "--approval-mode", "yolo"] - return ["gemini", "--approval-mode", "yolo"] + return ["cmd", "/c", "gemini", "--model", GEMINI_MODEL, + "--approval-mode", "yolo"] + return ["gemini", "--model", GEMINI_MODEL, "--approval-mode", "yolo"] + + def _set_thinking_budget(self, role: str): + """역할별 thinkingBudget을 settings.json에 반영.""" + budget = ROLE_THINKING.get(role, DEFAULT_THINKING) + try: + if _SETTINGS_PATH.exists(): + settings = json.loads(_SETTINGS_PATH.read_text(encoding="utf-8")) + else: + settings = {} + + # modelConfigs.default.thinkingConfig.thinkingBudget 설정 + configs = settings.setdefault("modelConfigs", {}) + default = configs.setdefault("default", {}) + thinking = default.setdefault("thinkingConfig", {}) + thinking["thinkingBudget"] = budget + + _SETTINGS_PATH.write_text( + json.dumps(settings, indent=2, ensure_ascii=False), + encoding="utf-8", + ) + logger.debug(f"thinkingBudget={budget} for role={role}") + except Exception as e: + logger.warning(f"thinkingBudget 설정 실패 (role={role}): {e}") def _clean_output(self, raw: str) -> str: """Gemini 출력에서 노이즈 라인 제거.""" @@ -70,6 +108,7 @@ class GeminiCaller: async def _call_text(self, role: str, context: str, timeout: int) -> str: """텍스트 전용 호출.""" + self._set_thinking_budget(role) prompt_file = ROLE_PROMPTS_DIR / f"{role}.md" if prompt_file.exists(): system_prompt = prompt_file.read_text(encoding="utf-8") @@ -143,6 +182,7 @@ class GeminiCaller: self, role: str, context: str, cwd: str, timeout: int, ) -> str: """에이전트 모드 구현.""" + self._set_thinking_budget(role) prompt_file = ROLE_PROMPTS_DIR / f"{role}.md" if prompt_file.exists(): system_prompt = prompt_file.read_text(encoding="utf-8") diff --git a/workspaces.json b/workspaces.json index bbe4f12..7db0c9a 100644 --- a/workspaces.json +++ b/workspaces.json @@ -15,5 +15,22 @@ "project_id": 0 }, "docs_path": "docs/wiki" + }, + "1479489442249969796": { + "name": "test_2", + "path": "c:\\Users\\Certes\\Desktop\\VW_Proj\\test_2", + "channel_id": 1479489442249969796, + "git": { + "url": "", + "token": "", + "repo": "", + "branch": "main" + }, + "vikunja": { + "url": "", + "token": "", + "project_id": 0 + }, + "docs_path": "docs/wiki" } } \ No newline at end of file