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:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user