Spaces:
Running
Running
| """ | |
| Data models for the API Testing Environment. | |
| Defines Action, Observation, State for API integration testing training. | |
| An AI agent learns to test REST APIs intelligently β discovering endpoints, | |
| crafting requests, validating responses, finding bugs, and handling edge cases. | |
| """ | |
| from enum import Enum | |
| from typing import Any, Optional | |
| from pydantic import Field | |
| from openenv.core.env_server.types import Action, Observation, State | |
| class HTTPMethod(str, Enum): | |
| GET = "GET" | |
| POST = "POST" | |
| PUT = "PUT" | |
| DELETE = "DELETE" | |
| PATCH = "PATCH" | |
| class BugSeverity(str, Enum): | |
| EASY = "easy" | |
| MEDIUM = "medium" | |
| HARD = "hard" | |
| class APITestAction(Action): | |
| """What the agent sends each step β an HTTP request to test the API.""" | |
| method: HTTPMethod = Field(..., description="HTTP method") | |
| endpoint: str = Field(..., min_length=1, description="API endpoint path, e.g. /tasks, /users/1") | |
| headers: dict[str, str] = Field(default_factory=dict, description="Request headers") | |
| query_params: dict[str, Any] = Field(default_factory=dict, description="URL query parameters") | |
| body: Optional[dict[str, Any]] = Field(default=None, description="Request JSON body") | |
| expected_status: Optional[int] = Field( | |
| default=None, | |
| description="What the agent expects the status code to be (used for bug detection)", | |
| ) | |
| class EndpointInfo(Action): | |
| """Information about a single API endpoint from the spec.""" | |
| method: str = "" | |
| path: str = "" | |
| summary: str = "" | |
| parameters: list[dict[str, Any]] = Field(default_factory=list) | |
| request_body_schema: Optional[dict[str, Any]] = None | |
| response_schema: Optional[dict[str, Any]] = None | |
| class APITestObservation(Observation): | |
| """What the agent sees after each step.""" | |
| # API spec info (provided on reset, updated each step) | |
| available_endpoints: list[dict[str, Any]] = Field( | |
| default_factory=list, description="Available API endpoints from the spec" | |
| ) | |
| # Response from last request | |
| status_code: int = Field(default=0, description="HTTP status code of the response") | |
| response_body: Any = Field(default=None, description="Response body (JSON or text)") | |
| response_headers: dict[str, str] = Field(default_factory=dict, description="Response headers") | |
| response_time_ms: float = Field(default=0.0, description="Response time in milliseconds") | |
| # Feedback | |
| feedback: str = Field(default="", description="Human-readable feedback about the last action") | |
| bugs_found_so_far: int = Field(default=0, description="Number of bugs found so far") | |
| coverage_summary: dict[str, Any] = Field( | |
| default_factory=dict, | |
| description="Coverage stats: endpoints_tested, methods_used, status_codes_seen", | |
| ) | |
| # Context from prior steps | |
| known_resource_ids: dict[str, list[Any]] = Field( | |
| default_factory=dict, | |
| description="Resource IDs created by POST requests, keyed by resource type", | |
| ) | |
| auth_tokens: dict[str, str] = Field( | |
| default_factory=dict, | |
| description="Available auth tokens for different users/roles", | |
| ) | |
| # Task info | |
| task_id: str = Field(default="", description="Current task identifier") | |
| task_description: str = Field(default="", description="Description of the current task") | |
| steps_taken: int = Field(default=0, description="Steps taken in this episode") | |
| max_steps: int = Field(default=30, description="Maximum steps per episode") | |
| class APITestState(State): | |
| """Episode metadata β internal state exposed via state() endpoint.""" | |
| task_id: str = "" | |
| task_description: str = "" | |
| difficulty: str = "easy" | |
| steps_taken: int = 0 | |
| max_steps: int = 30 | |
| bugs_found: int = 0 | |
| total_bugs: int = 0 | |
| bugs_found_ids: list[str] = Field(default_factory=list) | |
| coverage_pct: float = 0.0 | |
| endpoints_tested: int = 0 | |
| total_endpoints: int = 0 | |
| current_score: float = 0.0 | |
| cumulative_reward: float = 0.0 | |