llm timeout 수정
This commit is contained in:
@@ -299,7 +299,7 @@ const StrategyEvaluationAiVerifyModal: React.FC<Props> = ({
|
||||
<div className="seval-ai-verify-loading" aria-live="polite">
|
||||
<span className="btd-run-spinner seval-ai-verify-spinner" aria-hidden />
|
||||
<p>LLM이 평가 결과와 차트 시그널을 분석 중입니다…</p>
|
||||
<p className="seval-ai-verify-loading-hint">최대 2분 정도 걸릴 수 있습니다.</p>
|
||||
<p className="seval-ai-verify-loading-hint">Ta4j 재평가와 LLM 분석에 최대 2~3분 걸릴 수 있습니다.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -118,17 +118,69 @@ async function requestOrThrow<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const text = await res.text().catch(() => '');
|
||||
if (!res.ok) {
|
||||
let msg = `요청 실패 (${res.status})`;
|
||||
try {
|
||||
const j = JSON.parse(text) as { message?: string };
|
||||
if (j.message) msg = j.message;
|
||||
} catch { /* plain text */ }
|
||||
if (!text.startsWith('{') && text) msg = text.slice(0, 200);
|
||||
if (res.status === 504) {
|
||||
msg = '게이트웨이 시간 초과(504). LLM 분석은 2~3분 걸릴 수 있습니다. 잠시 후 다시 시도해 주세요.';
|
||||
} else {
|
||||
try {
|
||||
const j = JSON.parse(text) as { message?: string };
|
||||
if (j.message) msg = j.message;
|
||||
} catch { /* plain text */ }
|
||||
if (!text.startsWith('{') && text) {
|
||||
const plain = text.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
|
||||
if (plain) msg = plain.slice(0, 200);
|
||||
}
|
||||
}
|
||||
throw new Error(msg);
|
||||
}
|
||||
if (res.status === 204 || !text) return undefined as T;
|
||||
return JSON.parse(text) as T;
|
||||
}
|
||||
|
||||
/** LLM·AI 검증 등 장시간 API */
|
||||
async function requestOrThrowWithTimeout<T>(
|
||||
path: string,
|
||||
init?: RequestInit,
|
||||
timeoutMs = 200_000,
|
||||
): Promise<T> {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}${path}`, {
|
||||
...init,
|
||||
signal: controller.signal,
|
||||
headers: { ...authHeaders(), ...(init?.headers ?? {}) },
|
||||
});
|
||||
const text = await res.text().catch(() => '');
|
||||
if (!res.ok) {
|
||||
let msg = `요청 실패 (${res.status})`;
|
||||
if (res.status === 504) {
|
||||
msg = '게이트웨이 시간 초과(504). LLM 분석은 2~3분 걸릴 수 있습니다. 잠시 후 다시 시도해 주세요.';
|
||||
} else {
|
||||
try {
|
||||
const j = JSON.parse(text) as { message?: string };
|
||||
if (j.message) msg = j.message;
|
||||
} catch { /* plain text */ }
|
||||
if (!text.startsWith('{') && text) {
|
||||
const plain = text.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
|
||||
if (plain) msg = plain.slice(0, 200);
|
||||
}
|
||||
}
|
||||
throw new Error(msg);
|
||||
}
|
||||
if (res.status === 204 || !text) return undefined as T;
|
||||
return JSON.parse(text) as T;
|
||||
} catch (e) {
|
||||
if (e instanceof DOMException && e.name === 'AbortError') {
|
||||
throw new Error(
|
||||
`요청 시간 초과(${Math.round(timeoutMs / 1000)}초). LLM이 느리면 설정 > LLM에서 타임아웃을 늘리거나 잠시 후 다시 시도하세요.`,
|
||||
);
|
||||
}
|
||||
throw e;
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
async function request<T>(path: string, init?: RequestInit): Promise<T | null> {
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}${path}`, {
|
||||
@@ -1690,11 +1742,15 @@ export interface StrategyEvaluationAiVerifyResponse {
|
||||
export async function fetchStrategyEvaluationAiVerify(
|
||||
context: Record<string, unknown>,
|
||||
): Promise<StrategyEvaluationAiVerifyResponse> {
|
||||
return requestOrThrow<StrategyEvaluationAiVerifyResponse>('/strategy/evaluation/ai-verify', {
|
||||
method: 'POST',
|
||||
cache: 'no-store',
|
||||
body: JSON.stringify({ context }),
|
||||
});
|
||||
return requestOrThrowWithTimeout<StrategyEvaluationAiVerifyResponse>(
|
||||
'/strategy/evaluation/ai-verify',
|
||||
{
|
||||
method: 'POST',
|
||||
cache: 'no-store',
|
||||
body: JSON.stringify({ context }),
|
||||
},
|
||||
200_000,
|
||||
);
|
||||
}
|
||||
|
||||
export interface LlmConnectionTestResponse {
|
||||
@@ -1706,10 +1762,14 @@ export interface LlmConnectionTestResponse {
|
||||
|
||||
/** 설정 화면 — LLM 서버 연결 테스트 (저장된 설정 기준) */
|
||||
export async function fetchLlmConnectionTest(): Promise<LlmConnectionTestResponse> {
|
||||
return requestOrThrow<LlmConnectionTestResponse>('/strategy/evaluation/llm-test', {
|
||||
method: 'POST',
|
||||
cache: 'no-store',
|
||||
});
|
||||
return requestOrThrowWithTimeout<LlmConnectionTestResponse>(
|
||||
'/strategy/evaluation/llm-test',
|
||||
{
|
||||
method: 'POST',
|
||||
cache: 'no-store',
|
||||
},
|
||||
130_000,
|
||||
);
|
||||
}
|
||||
|
||||
/** 전략 DSL에 포함된 평가 시간봉 목록 (실시간 체크·STOMP 구독용) */
|
||||
|
||||
Reference in New Issue
Block a user