fix: apply A1/A2/A3 to the live services/ copy and ship it in the backend image

backend/routers/analyze.py imports services.* (root package), not
backend.services.* - so the earlier A1/A2/A3 edits landed in an unused
duplicate. Also COPY/mount services/ so the container can start at all
(ModuleNotFoundError: No module named services).
This commit is contained in:
root
2026-07-25 12:31:07 +00:00
parent 5a631f6553
commit 76278f5fe1
4 changed files with 15 additions and 4 deletions
+1
View File
@@ -8,6 +8,7 @@ COPY backend/requirements.txt ./requirements.txt
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt
COPY backend ./backend COPY backend ./backend
COPY services ./services
EXPOSE 8000 EXPOSE 8000
+1
View File
@@ -44,6 +44,7 @@ services:
condition: service_healthy condition: service_healthy
volumes: volumes:
- ./backend:/app/backend - ./backend:/app/backend
- ./services:/app/services
command: uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload command: uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload
frontend: frontend:
+7 -4
View File
@@ -4,6 +4,7 @@ Integrates geo_service zones and scoring_service rankings.
""" """
import os import os
import json import json
import logging
from typing import Dict, List, Optional from typing import Dict, List, Optional
from pydantic import BaseModel from pydantic import BaseModel
import httpx import httpx
@@ -11,6 +12,8 @@ import httpx
from .geo_service import build_search_zones from .geo_service import build_search_zones
from .scoring_service import WeightedScorer from .scoring_service import WeightedScorer
logger = logging.getLogger(__name__)
class PrimaryZone(BaseModel): class PrimaryZone(BaseModel):
priority: int priority: int
@@ -63,7 +66,7 @@ async def analyze_case(case_data: dict) -> AnalysisResult:
return await analyze_with_claude(case_data, api_key) return await analyze_with_claude(case_data, api_key)
except Exception as e: except Exception as e:
# Логируем ошибку и переходим на fallback # Логируем ошибку и переходим на 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 # Fallback: используем только scoring_service
return await analyze_with_fallback(case_data) 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['forest_pct']}%, "
zones_data += f"дороги {zone['road_density']} км/км²\n" zones_data += f"дороги {zone['road_density']} км/км²\n"
except Exception as e: except Exception as e:
print(f"Geo/scoring service error: {e}") logger.warning(f"Geo/scoring service error: {e}")
# Формируем промпт на русском языке # Формируем промпт на русском языке
prompt = f"""Ты — эксперт по поисково-спасательным операциям (ПСО) МЧС Республики Беларусь. Проанализируй следующий случай пропажи человека и дай структурированные рекомендации. prompt = f"""Ты — эксперт по поисково-спасательным операциям (ПСО) МЧС Республики Беларусь. Проанализируй следующий случай пропажи человека и дай структурированные рекомендации.
@@ -154,7 +157,7 @@ async def analyze_with_claude(case_data: dict, api_key: str) -> AnalysisResult:
"content-type": "application/json" "content-type": "application/json"
}, },
json={ json={
"model": "claude-sonnet-4-20250514", "model": "claude-sonnet-5",
"max_tokens": 4096, "max_tokens": 4096,
"messages": [ "messages": [
{ {
@@ -244,7 +247,7 @@ async def analyze_with_fallback(case_data: dict) -> AnalysisResult:
search_radius_km = scorer.distance_multiplier * 2.0 search_radius_km = scorer.distance_multiplier * 2.0
except Exception as e: 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: if not primary_zones:
+6
View File
@@ -118,6 +118,8 @@ def get_terrain_coefficient(terrain: str) -> float:
Returns: Returns:
Terrain coefficient (0.0 - 1.0) Terrain coefficient (0.0 - 1.0)
""" """
if not terrain:
return 0.5
terrain_lower = terrain.lower() terrain_lower = terrain.lower()
terrain_map = { terrain_map = {
@@ -155,6 +157,8 @@ def get_time_of_day_coefficient(time_of_day: str) -> float:
Returns: Returns:
Time coefficient (0.0 - 1.0) Time coefficient (0.0 - 1.0)
""" """
if not time_of_day:
return 1.0
time_lower = time_of_day.lower() time_lower = time_of_day.lower()
if 'ночь' in time_lower: if 'ночь' in time_lower:
@@ -175,6 +179,8 @@ def get_weather_coefficient(weather: str) -> float:
Returns: Returns:
Weather coefficient (0.0 - 1.0) Weather coefficient (0.0 - 1.0)
""" """
if not weather:
return 1.0
weather_lower = weather.lower() weather_lower = weather.lower()
if 'ливень' in weather_lower or 'сильный дождь' in weather_lower: if 'ливень' in weather_lower or 'сильный дождь' in weather_lower: