from typing import Dict, List, Literal, Optional from pydantic import BaseModel, Field AgentRole = Literal[ "renewable_prosumer", "ev_aggregator", "peaker_plant", "industrial_load", ] BidType = Literal["supply", "demand"] class AgentBid(BaseModel): agent_id: str = Field(..., description="Unique agent id") role: AgentRole = Field(..., description="Agent role") bid_type: BidType = Field(..., description="Supply or demand bid") quantity_mwh: float = Field(..., ge=0.0, description="Bid quantity in MWh") price_usd_per_mwh: float = Field(..., ge=0.0, description="Bid price") class JointAction(BaseModel): bids: List[AgentBid] = Field(default_factory=list, description="Bids from all agents") ev_charge_mwh: float = Field(0.0, ge=0.0, description="EV fleet charge command") ev_discharge_mwh: float = Field(0.0, ge=0.0, description="EV fleet discharge command") class DispatchAction(BaseModel): reserve_activation_mwh: float = Field(0.0, ge=0.0, description="Reserve activation target") peaker_adjustment_mwh: float = Field(0.0, description="Peaker redispatch adjustment") storage_dispatch_mwh: float = Field(0.0, description="Signed storage dispatch adjustment") corrective_redispatch_mwh: float = Field(0.0, description="Signed corrective redispatch adjustment") class MarketObservation(BaseModel): step: int steps_taken: int max_steps: int demand_mwh: float renewable_availability_mwh: float peaker_capacity_mwh: float ev_storage_mwh: float ev_storage_capacity_mwh: float last_clearing_price: float leader_price_signal: float scarcity_index: float shock_active: bool forecast_demand_mwh: float = 0.0 forecast_renewable_mwh: float = 0.0 load_forecast_error_mwh: float = 0.0 renewable_forecast_error_mwh: float = 0.0 contingency_active: bool = False contingency_type: str = "none" operator_override_enabled: bool = False public_signal: str schema_info: str hint: Optional[str] = None error_message: Optional[str] = None class MarketReward(BaseModel): score: float = Field(..., ge=0.0, le=1.0) reason: str demand_satisfaction_score: float cost_efficiency_score: float renewable_utilization_score: float stability_score: float reserve_adequacy_score: float = 0.0 emissions_intensity_tco2_per_mwh: float = 0.0 infeasibility_penalty: float blackout_penalty: float class ResetRequest(BaseModel): task_id: str = "default" seed: Optional[int] = None class ResetResponse(BaseModel): session_id: str task_id: str task_description: str schema_info: str steps_taken: int observation: MarketObservation class StepRequest(BaseModel): action: JointAction dispatch_action: Optional[DispatchAction] = None class StepResponse(BaseModel): observation: MarketObservation reward: MarketReward done: bool truncated: bool info: Dict class StateResponse(BaseModel): current_task_id: str steps_taken: int episode_done: bool observation: Optional[MarketObservation] = None class EpisodeSummary(BaseModel): average_reward: float total_demand_met: float total_cost: float infeasible_actions: int corrections: int shock_response_score: float