""" API 요청/응답 데이터 모델 """ from typing import Optional, List, Dict, Any from pydantic import BaseModel, Field from enum import Enum class CardType(str, Enum): """협상 카드 타입""" C1 = "C1" C2 = "C2" C3 = "C3" C4 = "C4" class ScenarioType(str, Enum): """시나리오 타입""" A = "A" # S_1 B = "B" # S_4 C = "C" # S_3 D = "D" # S_2 class PriceZoneType(str, Enum): """가격 구간 타입""" PZ1 = "PZ1" # P < A PZ2 = "PZ2" # A < P < T PZ3 = "PZ3" # T < P class ExperienceData(BaseModel): """경험 데이터 모델""" state: str = Field(..., description="현재 상태") action: CardType = Field(..., description="선택한 행동") reward: float = Field(..., description="받은 보상") next_state: str = Field(..., description="다음 상태") done: bool = Field(..., description="에피소드 종료 여부") timestamp: float = Field(..., description="타임스탬프") metadata: Optional[Dict[str, Any]] = Field(default=None, description="추가 메타데이터") class NegotiationState(BaseModel): """협상 상태 모델""" current_card: CardType = Field(..., description="현재 카드") scenario: ScenarioType = Field(..., description="시나리오") price_zone: PriceZoneType = Field(..., description="가격 구간") @property def state_id(self) -> str: """상태 ID 생성""" return f"{self.current_card.value}{self.scenario.value}{self.price_zone.value}" class RewardCalculationRequest(BaseModel): """보상 계산 요청 모델""" scenario: ScenarioType = Field(..., description="시나리오") price_zone: PriceZoneType = Field(..., description="가격 구간") anchor_price: float = Field(..., gt=0, description="목표가 (A)") proposed_price: float = Field(..., gt=0, description="제안가 (P)") is_end: bool = Field(..., description="협상 종료 여부") class RewardCalculationResponse(BaseModel): """보상 계산 응답 모델""" reward: float = Field(..., description="계산된 보상") weight: float = Field(..., description="가중치 W") scenario_weight: float = Field(..., description="시나리오 가중치 S_n") price_zone_weight: float = Field(..., description="가격구간 가중치 PZ_n") price_ratio: float = Field(..., description="가격 비율 A/P") formula_breakdown: str = Field(..., description="공식 분해") class QTableState(BaseModel): """Q-Table 상태 모델""" q_table: Dict[str, Dict[str, float]] = Field(..., description="Q-Table 데이터") update_count: int = Field(..., description="업데이트 횟수") learning_rate: float = Field(..., description="학습률") discount_factor: float = Field(..., description="할인율") class EpisodeGenerationRequest(BaseModel): """에피소드 생성 요청 모델""" num_episodes: int = Field(..., ge=1, le=100, description="생성할 에피소드 수") max_steps: int = Field(..., ge=1, le=20, description="에피소드당 최대 스텝") anchor_price: float = Field(..., gt=0, description="목표가") exploration_rate: float = Field(default=0.4, ge=0, le=1, description="탐험율") class LearningUpdateRequest(BaseModel): """학습 업데이트 요청 모델""" learning_rate: float = Field(..., ge=0.001, le=1.0, description="학습률") discount_factor: float = Field(..., ge=0.1, le=0.99, description="할인율") batch_size: int = Field(default=32, ge=1, le=1000, description="배치 크기") class FQICQLRequest(BaseModel): """FQI+CQL 학습 요청 모델""" alpha: float = Field(default=1.0, ge=0, description="CQL 보수성 파라미터") gamma: float = Field(default=0.95, ge=0.1, le=0.99, description="할인율") batch_size: int = Field(default=32, ge=1, le=1000, description="배치 크기") num_iterations: int = Field(default=10, ge=1, le=100, description="반복 횟수") class ActionRecommendationRequest(BaseModel): """행동 추천 요청 모델""" current_state: str = Field(..., description="현재 상태") use_epsilon_greedy: bool = Field(default=False, description="엡실론 그리디 사용") epsilon: float = Field(default=0.1, ge=0, le=1, description="엡실론 값") class ActionRecommendationResponse(BaseModel): """행동 추천 응답 모델""" recommended_action: CardType = Field(..., description="추천 행동") q_values: Dict[str, float] = Field(..., description="현재 상태의 Q값들") confidence: float = Field(..., description="추천 신뢰도") exploration: bool = Field(..., description="탐험 행동 여부") class SystemStatus(BaseModel): """시스템 상태 모델""" total_experiences: int = Field(..., description="총 경험 데이터 수") q_table_updates: int = Field(..., description="Q-Table 업데이트 횟수") unique_states: int = Field(..., description="고유 상태 수") average_reward: float = Field(..., description="평균 보상") success_rate: float = Field(..., description="성공률") last_update: Optional[float] = Field(default=None, description="마지막 업데이트 시간")