"""Shared Web/MCP/worker contracts for exploration.""" from typing import Literal from pydantic import BaseModel, ConfigDict, Field, field_validator from artemis.config.exploration import ExplorationConfig ExplorationStatus = Literal[ "draft", "queued", "running", "waiting_user", "demonstrating", "analyzing_demo", "finalizing", "paused", "completed", "cancelled", ] DemonstrationSource = Literal["android", "computer", "both"] RunKind = Literal["exploration", "validation"] class ExplorationRequest(BaseModel): model_config = ConfigDict(extra="forbid") request_id: str = Field(min_length=1, max_length=200) @field_validator("request_id") @classmethod def nonblank_request(cls, value: str) -> str: if not value.strip(): raise ValueError("request_id 不能为空") return value class CreateExploration(ExplorationRequest): goal: str = Field(min_length=1, max_length=10000) config: ExplorationConfig | None = None # TaskOptions is validated at the service boundary, shared with ordinary tasks. task_config: dict = Field(default_factory=dict) @field_validator("goal") @classmethod def nonblank_goal(cls, value: str) -> str: if not value.strip(): raise ValueError("请描述希望探索的测试流程") return value.strip() class ExplorationMessage(ExplorationRequest): text: str = Field(min_length=1, max_length=10000) @field_validator("text") @classmethod def nonblank_text(cls, value: str) -> str: if not value.strip(): raise ValueError("消息不能为空") return value.strip() class DemonstrationRequest(ExplorationRequest): source: DemonstrationSource | None = None class ExtendDemonstration(ExplorationRequest): seconds: int = Field(default=900, ge=60, le=86400) class CaseDraft(BaseModel): model_config = ConfigDict(extra="forbid") name: str = Field(min_length=1, max_length=200) preconditions: str = Field(default="", max_length=10000) goal: str = Field(min_length=1, max_length=10000) evidence: list[str] = Field(default_factory=list, max_length=100) @field_validator("name", "goal") @classmethod def nonblank_case(cls, value: str) -> str: if not value.strip(): raise ValueError("用例名称和提示词不能为空") return value.strip() class SaveCaseRequest(ExplorationRequest): case: CaseDraft expected_version: int = Field(ge=0) class ValidateCaseRequest(ExplorationRequest): version: int = Field(ge=1) profile: Literal["flash", "pro"] = "flash" task_config: dict = Field(default_factory=dict) class GenerateCaseRequest(ExplorationRequest): source_run_id: str = Field(min_length=1, max_length=100) expected_version: int = Field(ge=0) class CaseBatchRequest(ValidateCaseRequest): name: str = Field(default="探索用例", min_length=1, max_length=200)