diff --git a/src/api/static/index.html b/src/api/static/index.html
index 143a49f..93f617c 100644
--- a/src/api/static/index.html
+++ b/src/api/static/index.html
@@ -3249,7 +3249,7 @@
데이터 라이선스
}, 60);
if(d.scores) requestAnimationFrame(function(){drawRadar(d.scores);});
var mapPoisFinal = (d.poi_info||[]).filter(function(p){return p.lat&&p.lng&&p.lat!==37.5665;});
- if (mapPoisFinal.length) requestAnimationFrame(function(){initMap(mapPoisFinal, d.optimal_route, d.congestion_warnings);});
+ if (mapPoisFinal.length) initMap(mapPoisFinal, d.optimal_route, d.congestion_warnings);
}
function animateScore(target) {
diff --git a/src/data/models.py b/src/data/models.py
index e6814fe..c272868 100644
--- a/src/data/models.py
+++ b/src/data/models.py
@@ -253,7 +253,7 @@ def hard_fail_score_cap(self) -> "ValidationResult":
class Settings(BaseSettings):
anthropic_api_key: str = ""
- claude_model: str = "claude-sonnet-4-6"
+ claude_model: str = "claude-haiku-4-5"
tour_api_key: str = ""
kakao_rest_api_key: str = ""
kakao_mobility_key: str = ""
diff --git a/src/explain/explain_engine.py b/src/explain/explain_engine.py
index 3a1439c..8fe248e 100644
--- a/src/explain/explain_engine.py
+++ b/src/explain/explain_engine.py
@@ -35,7 +35,7 @@
_ANTHROPIC_AVAILABLE = False
# ── LLM 설정 ──────────────────────────────────────────────────────────────
-DEFAULT_MODEL: str = "claude-sonnet-4-6"
+DEFAULT_MODEL: str = "claude-haiku-4-5"
DEFAULT_TIMEOUT_SEC: float = 20.0
DEFAULT_MAX_TOKENS: int = 2500
diff --git a/src/explain/plan_extractor.py b/src/explain/plan_extractor.py
index c22976c..2b67265 100644
--- a/src/explain/plan_extractor.py
+++ b/src/explain/plan_extractor.py
@@ -24,7 +24,7 @@
except ImportError:
_ANTHROPIC_AVAILABLE = False
-DEFAULT_MODEL: str = "claude-sonnet-4-6"
+DEFAULT_MODEL: str = "claude-haiku-4-5"
DEFAULT_TIMEOUT_SEC: float = 45.0
DEFAULT_MAX_TOKENS: int = 4000
@@ -148,7 +148,11 @@ def extract_from_document(self, data: bytes, media_type: str) -> ParsedPlanRespo
"type": block_type,
"source": {"type": "base64", "media_type": media_type, "data": b64},
},
- {"type": "text", "text": _USER_PREFIX_DOC},
+ {
+ "type": "text",
+ "text": _USER_PREFIX_DOC,
+ "cache_control": {"type": "ephemeral"},
+ },
]
return self._call(content)
diff --git a/src/scoring/theme_alignment.py b/src/scoring/theme_alignment.py
index 45284cb..ecb0b31 100644
--- a/src/scoring/theme_alignment.py
+++ b/src/scoring/theme_alignment.py
@@ -39,7 +39,7 @@
PENALTY_CRIT: int = 20
# ── LLM 설정 ─────────────────────────────────────────────────────────
-DEFAULT_MODEL: str = "claude-sonnet-4-6"
+DEFAULT_MODEL: str = "claude-haiku-4-5"
DEFAULT_TIMEOUT_SEC: float = 10.0
DEFAULT_MAX_TOKENS: int = 500
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..66a87a9
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,12 @@
+"""테스트 전역 fixture.
+
+src.api.main은 import 시점에 .env를 읽어 os.environ에 ANTHROPIC_API_KEY를 주입한다.
+그 모듈을 import하는 테스트가 먼저 수집되면 이후 테스트의 "키 없음" 케이스가
+실행 순서에 따라 랜덤하게 깨진다 — 매 테스트마다 강제로 지워 격리한다.
+"""
+import pytest
+
+
+@pytest.fixture(autouse=True)
+def _clean_anthropic_api_key(monkeypatch):
+ monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)