diff --git a/backend/Dockerfile b/backend/Dockerfile index 93606db..4745cc9 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -8,6 +8,7 @@ COPY backend/requirements.txt ./requirements.txt RUN pip install --no-cache-dir -r requirements.txt COPY backend ./backend +COPY services ./services EXPOSE 8000 diff --git a/docker-compose.yml b/docker-compose.yml index 46901a0..4e93976 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,6 +44,7 @@ services: condition: service_healthy volumes: - ./backend:/app/backend + - ./services:/app/services command: uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload frontend: diff --git a/services/claude_service.py b/services/claude_service.py index 4b42ebd..299ae09 100644 --- a/services/claude_service.py +++ b/services/claude_service.py @@ -4,6 +4,7 @@ Integrates geo_service zones and scoring_service rankings. """ import os import json +import logging from typing import Dict, List, Optional from pydantic import BaseModel import httpx @@ -11,6 +12,8 @@ import httpx from .geo_service import build_search_zones from .scoring_service import WeightedScorer +logger = logging.getLogger(__name__) + class PrimaryZone(BaseModel): priority: int @@ -63,7 +66,7 @@ async def analyze_case(case_data: dict) -> AnalysisResult: return await analyze_with_claude(case_data, api_key) except Exception as e: # Логируем ошибку и переходим на fallback - print(f"Claude API unavailable: {e}. Using fallback scoring service.") + logger.warning(f"Claude API unavailable: {e}. Using fallback scoring service.") # Fallback: используем только scoring_service return await analyze_with_fallback(case_data) @@ -108,7 +111,7 @@ async def analyze_with_claude(case_data: dict, api_key: str) -> AnalysisResult: zones_data += f"лес {zone['forest_pct']}%, " zones_data += f"дороги {zone['road_density']} км/км²\n" except Exception as e: - print(f"Geo/scoring service error: {e}") + logger.warning(f"Geo/scoring service error: {e}") # Формируем промпт на русском языке prompt = f"""Ты — эксперт по поисково-спасательным операциям (ПСО) МЧС Республики Беларусь. Проанализируй следующий случай пропажи человека и дай структурированные рекомендации. @@ -154,7 +157,7 @@ async def analyze_with_claude(case_data: dict, api_key: str) -> AnalysisResult: "content-type": "application/json" }, json={ - "model": "claude-sonnet-4-20250514", + "model": "claude-sonnet-5", "max_tokens": 4096, "messages": [ { @@ -244,7 +247,7 @@ async def analyze_with_fallback(case_data: dict) -> AnalysisResult: search_radius_km = scorer.distance_multiplier * 2.0 except Exception as e: - print(f"Geo/scoring service error in fallback: {e}") + logger.warning(f"Geo/scoring service error in fallback: {e}") # Если зоны не построены, используем базовые if not primary_zones: diff --git a/services/distance_service.py b/services/distance_service.py index baa6fc5..12767bc 100644 --- a/services/distance_service.py +++ b/services/distance_service.py @@ -118,6 +118,8 @@ def get_terrain_coefficient(terrain: str) -> float: Returns: Terrain coefficient (0.0 - 1.0) """ + if not terrain: + return 0.5 terrain_lower = terrain.lower() terrain_map = { @@ -155,6 +157,8 @@ def get_time_of_day_coefficient(time_of_day: str) -> float: Returns: Time coefficient (0.0 - 1.0) """ + if not time_of_day: + return 1.0 time_lower = time_of_day.lower() if 'ночь' in time_lower: @@ -175,6 +179,8 @@ def get_weather_coefficient(weather: str) -> float: Returns: Weather coefficient (0.0 - 1.0) """ + if not weather: + return 1.0 weather_lower = weather.lower() if 'ливень' in weather_lower or 'сильный дождь' in weather_lower: