feat: implement automatic payload candidate retry mechanism in AIPlanner for improved LM Studio compatibility
This commit is contained in:
@@ -196,14 +196,31 @@ class AIPlanner:
|
||||
spinner.start()
|
||||
|
||||
try:
|
||||
payload = self._build_chat_payload(user_message)
|
||||
resp = httpx.post(
|
||||
f"{OLLAMA_HOST}/api/v1/chat",
|
||||
json=payload,
|
||||
timeout=600.0,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
data = None
|
||||
last_http_error: Exception | None = None
|
||||
candidates = self._build_payload_candidates(user_message)
|
||||
for i, payload in enumerate(candidates):
|
||||
resp = httpx.post(
|
||||
f"{OLLAMA_HOST}/api/v1/chat",
|
||||
json=payload,
|
||||
timeout=600.0,
|
||||
)
|
||||
if resp.status_code < 400:
|
||||
data = resp.json()
|
||||
break
|
||||
if resp.status_code == 400 and i < len(candidates) - 1:
|
||||
err_msg = self._extract_http_error_message(resp)
|
||||
print(f"[AI] payload 호환 재시도 {i + 1}: {err_msg}")
|
||||
continue
|
||||
try:
|
||||
resp.raise_for_status()
|
||||
except Exception as e:
|
||||
last_http_error = e
|
||||
break
|
||||
if data is None:
|
||||
if last_http_error is not None:
|
||||
raise last_http_error
|
||||
raise RuntimeError("Ollama/LM Studio 응답을 받지 못했습니다.")
|
||||
finally:
|
||||
stop_event.set()
|
||||
spinner.join()
|
||||
@@ -232,6 +249,40 @@ class AIPlanner:
|
||||
"options": {"temperature": 0.3, "num_ctx": 8192},
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _build_input_only_payload(user_message: str) -> dict:
|
||||
merged = f"{SYSTEM_PROMPT}\n\n{user_message}"
|
||||
return {
|
||||
"model": OLLAMA_MODEL,
|
||||
"input": merged,
|
||||
"stream": False,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _build_payload_candidates(user_message: str) -> list[dict]:
|
||||
# 서버 구현체마다 스키마가 달라 자동 호환을 위해 후보를 순차 시도한다.
|
||||
return [
|
||||
AIPlanner._build_chat_payload(user_message),
|
||||
AIPlanner._build_input_only_payload(user_message),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _extract_http_error_message(resp: httpx.Response) -> str:
|
||||
try:
|
||||
data = resp.json()
|
||||
if isinstance(data, dict):
|
||||
err = data.get("error")
|
||||
if isinstance(err, dict):
|
||||
msg = err.get("message")
|
||||
if isinstance(msg, str) and msg.strip():
|
||||
return msg
|
||||
except Exception:
|
||||
pass
|
||||
text = resp.text.strip()
|
||||
if text:
|
||||
return text[:200]
|
||||
return f"HTTP {resp.status_code}"
|
||||
|
||||
@staticmethod
|
||||
def _extract_response_content(data: dict) -> str:
|
||||
message = data.get("message")
|
||||
|
||||
Reference in New Issue
Block a user