feat(stock): apply webai_cache to portfolio/news/screener-preview (SP-A2)

3 endpoint cache 적용 — /api/webai/portfolio, /api/webai/news-sentiment,
/api/stock/screener/run (preview 모드만, auto는 캐시 미적용).
V1+V2 동시 호출도 NAS에서 1회 계산. web-ai 측 SP-A1 캐시와 2-layer로
작동하여 NAS 인바운드 부담 70% 감소 예상.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 21:47:23 +09:00
parent 030365bed0
commit 978aa14f8b
3 changed files with 48 additions and 5 deletions

View File

@@ -12,6 +12,7 @@ from fastapi import APIRouter, HTTPException
from . import schemas
from .registry import NODE_REGISTRY, GATE_REGISTRY
from .. import webai_cache
router = APIRouter(prefix="/api/stock/screener")
@@ -173,6 +174,12 @@ def _persist_run(conn, asof, mode, weights, node_params, gate_params, top_n,
@router.post("/run", response_model=schemas.RunResponse)
def post_run(body: schemas.RunRequest):
from .registry import NODE_REGISTRY as _NR, GATE_REGISTRY as _GR
# SP-A2 — preview 모드는 web-ai/frontend 폴링이라 캐시 적용.
# auto 모드는 실제 운영 트리거(휴장일 게이트 등)라 캐시 미적용.
if body.mode == "preview":
cached = webai_cache.cache_get_screener(body.mode, body.top_n, body.weights)
if cached is not None:
return cached
started_at = dt.datetime.utcnow().isoformat()
with _conn() as c:
asof = _resolve_asof(body.asof, c)
@@ -231,7 +238,7 @@ def post_run(body: schemas.RunRequest):
top_n=top_n, rows=result.rows, run_id=run_id,
)
return schemas.RunResponse(
response = schemas.RunResponse(
asof=asof.isoformat(), mode=body.mode, status="success",
run_id=run_id, survivors_count=result.survivors_count,
weights=weights, top_n=top_n,
@@ -239,6 +246,10 @@ def post_run(body: schemas.RunRequest):
telegram_payload=schemas.TelegramPayload(**payload),
warnings=result.warnings,
)
# SP-A2 — preview 모드 결과 캐시 저장.
if body.mode == "preview":
webai_cache.cache_set_screener(body.mode, body.top_n, body.weights, response)
return response
# ---------- /snapshot/refresh ----------