Spaces:
Sleeping
Sleeping
File size: 4,497 Bytes
20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 5370a84 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 20cd78e 312907b 5370a84 312907b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | """
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"
)
|