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
COPY backend ./backend
COPY services ./services
EXPOSE 8000
+1
View File
@@ -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:
+7 -4
View File
@@ -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:
+6
View File
@@ -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: