Codex Lab 환경(iran-airstrike-replay-codex)에서 검증 완료된 어구 모선 자동 추론 + 검토 워크플로우 전체를 이식. ## Python (prediction/) - gear_parent_inference(1,428줄): 다층 점수 모델 (correlation + name + track + prior bonus) - gear_parent_episode(631줄): Episode 연속성 (Jaccard + 공간거리) - gear_name_rules: 모선 이름 정규화 + 4자 미만 필터 - scheduler: 추론 호출 단계 추가 (4.8) - fleet_tracker/kcgdb: SQL qualified_table() 동적화 - gear_correlation: timestamp 필드 추가 ## DB (database/migration/ 012~015) - 후보 스냅샷, resolution, episode, 라벨 세션, 제외 관리 테이블 9개 + VIEW 2개 ## Backend (Java) - 12개 DTO/Controller (ParentInferenceWorkflowController 등) - GroupPolygonService: parent_resolution LEFT JOIN + 15개 API 메서드 ## Frontend - ParentReviewPanel: 모선 검토 대시보드 - vesselAnalysis: 10개 신규 API 함수 + 6개 타입 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
67 lines
1.8 KiB
Python
67 lines
1.8 KiB
Python
import re
|
|
from typing import Optional
|
|
|
|
from pydantic_settings import BaseSettings
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
# snpdb (궤적 데이터 소스)
|
|
SNPDB_HOST: str = '211.208.115.83'
|
|
SNPDB_PORT: int = 5432
|
|
SNPDB_NAME: str = 'snpdb'
|
|
SNPDB_USER: str = 'snp'
|
|
SNPDB_PASSWORD: str
|
|
|
|
# kcgdb (분석 결과 저장)
|
|
KCGDB_HOST: str = '211.208.115.83'
|
|
KCGDB_PORT: int = 5432
|
|
KCGDB_NAME: str = 'kcgdb'
|
|
KCGDB_SCHEMA: str = 'kcg'
|
|
KCGDB_USER: str = 'kcg_app'
|
|
KCGDB_PASSWORD: str
|
|
|
|
# 스케줄러
|
|
SCHEDULER_INTERVAL_MIN: int = 5
|
|
|
|
# 인메모리 캐시
|
|
CACHE_WINDOW_HOURS: int = 24
|
|
INITIAL_LOAD_HOURS: int = 24
|
|
STATIC_INFO_REFRESH_MIN: int = 60
|
|
PERMIT_REFRESH_MIN: int = 30
|
|
SNPDB_SAFE_DELAY_MIN: int = 12
|
|
SNPDB_BACKFILL_BUCKETS: int = 3
|
|
|
|
# 파이프라인
|
|
TRAJECTORY_HOURS: int = 6
|
|
MMSI_PREFIX: str = '412'
|
|
MIN_TRAJ_POINTS: int = 100
|
|
|
|
# Ollama (LLM)
|
|
OLLAMA_BASE_URL: str = 'http://localhost:11434'
|
|
OLLAMA_MODEL: str = 'qwen3:14b' # CPU-only: 14b 권장, GPU 있으면 32b
|
|
OLLAMA_TIMEOUT_SEC: int = 300
|
|
|
|
# Redis
|
|
REDIS_HOST: str = 'localhost'
|
|
REDIS_PORT: int = 6379
|
|
REDIS_PASSWORD: str = ''
|
|
|
|
# 로깅
|
|
LOG_LEVEL: str = 'INFO'
|
|
|
|
model_config = {'env_file': '.env', 'env_file_encoding': 'utf-8', 'extra': 'ignore'}
|
|
|
|
|
|
settings = Settings()
|
|
|
|
_SQL_IDENTIFIER = re.compile(r'^[A-Za-z_][A-Za-z0-9_]*$')
|
|
|
|
|
|
def qualified_table(table_name: str, schema: Optional[str] = None) -> str:
|
|
resolved_schema = schema or settings.KCGDB_SCHEMA
|
|
if not _SQL_IDENTIFIER.fullmatch(resolved_schema):
|
|
raise ValueError(f'Invalid schema name: {resolved_schema!r}')
|
|
if not _SQL_IDENTIFIER.fullmatch(table_name):
|
|
raise ValueError(f'Invalid table name: {table_name!r}')
|
|
return f'{resolved_schema}.{table_name}'
|