Phishing-env / models.py
og-arin's picture
Update models.py
ff5de8a verified
Raw
History Blame Contribute Delete
4.5 kB
"""
models.py – PhishGuard-Env Pydantic Models
==========================================
Typed request / response schemas used by env.py (FastAPI).
PhishAction : Body schema for POST /step
StepResponse : Response schema for POST /step (OpenEnv grader compliance)
ResetResponse : Response schema for POST /reset
BUG FIX (v1.0.2 β†’ v1.0.3)
────────────────────────────────────────────────────────────────────────────
StepResponse.task_id was typed as `str` but the episode-already-over guard
branch in env.py returns task_id=None. Pydantic would raise a validation
error on every post-episode /step call.
Fix: task_id is now Optional[str] with a default of None.
"""
from __future__ import annotations
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
# ─────────────────────────────────────────────────────────────────────────────
# REQUEST MODELS
# ─────────────────────────────────────────────────────────────────────────────
class PhishAction(BaseModel):
"""
Action submitted by the agent to POST /step.
Fields
------
action : One of MARK_SAFE | MOVE_TO_SPAM | QUARANTINE | BLOCK_DOMAIN
reasoning : Optional one-sentence technical justification (for logging).
"""
action: str = Field(
max_length=64,
description="Triage decision. Must be exactly one of: "
"MARK_SAFE | MOVE_TO_SPAM | QUARANTINE | BLOCK_DOMAIN"
)
reasoning: Optional[str] = Field(
default=None,
description="One-sentence technical justification for the triage decision",
)
class ResetRequest(BaseModel):
"""Body schema for POST /reset."""
difficulty: str = Field(
default="easy",
description="Difficulty level: easy | medium | hard",
)
# ─────────────────────────────────────────────────────────────────────────────
# RESPONSE MODELS (OpenEnv spec β€” all fields required by validator)
# ─────────────────────────────────────────────────────────────────────────────
class StepResponse(BaseModel):
"""
Full response for POST /step.
The OpenEnv validator inspects `task_id` and `is_correct` on every step
to count how many distinct tasks have been graded.
task_id is Optional[str] (not str) because the episode-already-over guard
branch returns None β€” a non-optional field would cause a Pydantic
ValidationError on every post-episode call.
"""
observation: Optional[Dict[str, Any]] = Field(
description="Next email dict, or null when the episode is done"
)
reward: float = Field(
description="Step reward strictly in (0.0, 1.0)"
)
done: bool = Field(
description="True when all scenarios are complete or health reaches 0"
)
task_id: Optional[str] = Field( # BUG FIX: was `str`, must be Optional
default=None,
description="Scenario ID e.g. 'lv3' β€” required by OpenEnv validator"
)
is_correct: bool = Field(
description="True when reward >= R_PERFECT (0.95)"
)
info: Dict[str, Any] = Field(
description="Full grader info payload"
)
class ResetResponse(BaseModel):
"""Response for POST /reset."""
observation: Dict[str, Any] = Field(
description="First email observation for this episode"
)
task_id: str = Field(
description="ID of the first scenario in this episode"
)
task_group: str = Field(
description="Difficulty level of the first scenario: easy | medium | hard"
)
difficulty: str = Field(
description="Active difficulty level for this episode"
)
total_tasks: int = Field(
description="Total number of scenarios in this level"
)