B2: tolerant JSON extraction from Claude responses + graceful fallback
_extract_json_payload handles a json/JSON/bare fence, raw JSON and JSON embedded in prose; any unparseable response or contract violation now degrades to the deterministic scoring_service instead of raising. Also guards the response envelope itself (content[0].text). B3: single home for recommendation scoring services/recommendation_service.py holds the rules; routers/stats.py and backend/services/stats_service.py both delegate to it. Unified rules are the union of the two old copies: same weights/threshold, substring matching (superset of the old exact match), tolerant key aliases, health_flags rule kept. Endpoint response contract unchanged. Plus: Overpass circuit breaker and concurrent zone queries in geo_service - 128 sequential calls per analysis no longer each burn a connect timeout when the host has no outbound network. Tests: 152 -> 194 passed.
This commit is contained in:
+10
-13
@@ -2,6 +2,7 @@ from fastapi import APIRouter
|
||||
|
||||
from backend.database import db
|
||||
from backend.schemas import DashboardResponse, RecommendationRequest
|
||||
from services.recommendation_service import score_recommendation
|
||||
|
||||
router = APIRouter(prefix='/api/v1/stats', tags=['stats'])
|
||||
|
||||
@@ -18,18 +19,14 @@ def stats_heatmap() -> dict:
|
||||
|
||||
@router.post('/recommendation')
|
||||
def stats_recommendation(payload: RecommendationRequest) -> dict:
|
||||
score = 0
|
||||
if payload.age is not None and payload.age < 12:
|
||||
score += 20
|
||||
if payload.elapsed_hours is not None and payload.elapsed_hours >= 12:
|
||||
score += 20
|
||||
if payload.terrain_primary in {'лес', 'болото', 'вода'}:
|
||||
score += 15
|
||||
if payload.weather in {'дождь', 'туман', 'снег', 'ночь'}:
|
||||
score += 15
|
||||
if len(payload.health_flags) >= 2:
|
||||
score += 15
|
||||
result = score_recommendation(
|
||||
age=payload.age,
|
||||
elapsed_hours=payload.elapsed_hours,
|
||||
terrain=payload.terrain_primary,
|
||||
weather=payload.weather,
|
||||
health_flags=payload.health_flags,
|
||||
)
|
||||
return {
|
||||
'recommendation': 'Высокий приоритет на прочёс и дрон' if score >= 40 else 'Стандартный приоритет поиска',
|
||||
'score': score,
|
||||
'recommendation': result['recommendation'],
|
||||
'score': result['score'],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user