"""Seed 관리자 - 작업별 Seed 고정 및 추적.""" from __future__ import annotations import hashlib import random from typing import Optional from sqlalchemy.orm import Session from harness.logger import JobRecord, get_db_session class SeedManager: """DXF 파일 해시 기반 결정론적 seed를 생성하고 이력을 관리한다.""" MAX_SEED = 2**32 - 1 def get_seed( self, file_hash: str, fixed_seed: Optional[int] = None, deterministic: bool = True, ) -> int: """ Args: file_hash: DXF 파일의 SHA256 해시 앞 16자 fixed_seed: 사용자가 직접 지정한 seed (None이면 자동) deterministic: True면 파일 해시 기반, False면 랜덤 """ if fixed_seed is not None: return int(fixed_seed) % (self.MAX_SEED + 1) if deterministic: return self._hash_to_seed(file_hash) return random.randint(0, self.MAX_SEED) def get_or_create_seed( self, db: Session, job_id: int, file_hash: str, fixed_seed: Optional[int] = None, deterministic: bool = True, ) -> int: """DB에서 기존 seed를 조회하거나 새로 생성한다.""" existing = db.query(JobRecord).filter_by(id=job_id).first() if existing and existing.seed is not None: return existing.seed seed = self.get_seed(file_hash, fixed_seed, deterministic) if existing: existing.seed = seed db.commit() return seed @staticmethod def _hash_to_seed(file_hash: str) -> int: """파일 해시를 정수 seed로 변환한다.""" digest = hashlib.sha256(file_hash.encode()).digest() return int.from_bytes(digest[:4], "big") @staticmethod def describe(seed: int) -> str: return f"seed={seed} (0x{seed:08X})"