Spaces:
Sleeping
Sleeping
Download backend/app/models.py from ColdAshSage/machineread: direct link, hf CLI and curl.
- Browser
- Download file 35.5 kB
-
https://huggingface.co/spaces/ColdAshSage/machineread/resolve/main/backend/app/models.py
- Command line
-
hf download hf://spaces/ColdAshSage/machineread/backend/app/models.py
-
curl -L -o models.py https://huggingface.co/spaces/ColdAshSage/machineread/resolve/main/backend/app/models.py
35.5 kB
| from typing import Literal | |
| from pydantic import BaseModel, ConfigDict, Field, field_validator | |
| from app.presets import VALID_PRESETS, PresetName, _VALID_OVERRIDE_KEYS | |
| PillarName = Literal["off_site", "scrapability", "seo"] | |
| EffortLevel = Literal["low", "medium", "high"] | |
| # A curated OpenAPI example for the audit request body. The example is the | |
| # smallest legal payload (URL only) so agents can copy/paste it as a smoke | |
| # test without needing to understand presets, overrides, or scope toggles | |
| # on first contact. | |
| _AUDIT_REQUEST_EXAMPLE: dict = { | |
| "url": "https://example.com/", | |
| } | |
| # A second example showing the preset path so agents discover the preset | |
| # model from the schema without reading docs first. | |
| _AUDIT_REQUEST_PRESET_EXAMPLE: dict = { | |
| "url": "https://example.com/", | |
| "preset": "blog", | |
| } | |
| class AuditRequest(BaseModel): | |
| # OpenAPI examples + field-level descriptions so the public schema is | |
| # self-explanatory for agents. The model_config block provides the JSON | |
| # Schema ``examples`` array surfaced under AuditRequest in OpenAPI. | |
| model_config = ConfigDict( | |
| json_schema_extra={ | |
| "examples": [ | |
| _AUDIT_REQUEST_EXAMPLE, | |
| _AUDIT_REQUEST_PRESET_EXAMPLE, | |
| ], | |
| }, | |
| ) | |
| url: str = Field( | |
| description=( | |
| "Public HTTP(S) URL to audit. The scheme is added automatically if " | |
| "missing (defaults to https). Private, loopback, link-local, and " | |
| "reserved address ranges are rejected before the audit starts." | |
| ), | |
| examples=["https://example.com/"], | |
| ) | |
| # The three legacy booleans remain part of the public contract (existing | |
| # integrations depend on them) but the preset path is the recommended one. | |
| # Marking the fields deprecated surfaces this in generated client SDKs and | |
| # OpenAPI tooling without breaking callers. | |
| include_protocols: bool = Field( | |
| default=False, | |
| deprecated=True, | |
| description=( | |
| "Legacy scope toggle. When ``preset`` is set this field is " | |
| "ignored; when ``preset`` is None, this controls whether " | |
| "protocol/API surfaces (MCP, A2A, Agent Skills, WebMCP, API " | |
| "catalog) are scored. Prefer the preset-based path for new " | |
| "integrations; this field is preserved for backward compatibility " | |
| "and remains part of the public contract." | |
| ), | |
| ) | |
| include_account_auth: bool = Field( | |
| default=False, | |
| deprecated=True, | |
| description=( | |
| "Legacy scope toggle. When ``preset`` is set this field is " | |
| "ignored; when ``preset`` is None, this controls whether " | |
| "account/auth surfaces (OAuth/OIDC discovery, auth.md) are " | |
| "scored. Prefer the preset-based path for new integrations; this " | |
| "field is preserved for backward compatibility and remains part " | |
| "of the public contract." | |
| ), | |
| ) | |
| include_ecommerce: bool = Field( | |
| default=False, | |
| deprecated=True, | |
| description=( | |
| "Legacy scope toggle. When ``preset`` is set this field is " | |
| "ignored; when ``preset`` is None, this controls whether " | |
| "commerce surfaces (Product/Offer JSON-LD, price/availability, " | |
| "checkout-protocol metadata) are scored. Prefer the preset-based " | |
| "path for new integrations; this field is preserved for backward " | |
| "compatibility and remains part of the public contract." | |
| ), | |
| ) | |
| preset: PresetName | None = Field( | |
| default=None, | |
| description=( | |
| "Preset identifier. When set, the preset wins over the " | |
| "three legacy include_* booleans. When None, the booleans are " | |
| "honored verbatim and custom_overrides must be empty. Supported " | |
| "values: 'blog', 'corporate', 'services', 'ecommerce', 'news', " | |
| "'saas', 'custom'." | |
| ), | |
| examples=["blog", "saas", None], | |
| ) | |
| custom_overrides: dict[str, bool] | None = Field( | |
| default=None, | |
| description=( | |
| "Custom/Power User overrides applied on top of the " | |
| "selected preset. Requires preset='custom' or another preset to " | |
| "be set; rejected when preset is None. Supported keys: " | |
| "'protocols', 'account_auth', 'ecommerce', 'feed_discovery', " | |
| "'article_schema', 'localbusiness_schema', 'news_article_schema', " | |
| "'claimreview_schema', 'product_offer_schema', 'commerce_fields', " | |
| "'api_catalog', 'mcp', 'a2a', 'agent_skills', 'webmcp', " | |
| "'oauth_oidc', 'ard_catalog', 'auth_md'." | |
| ), | |
| examples=[None, {"feed_discovery": True, "api_catalog": True}], | |
| ) | |
| def _validate_preset(_cls, value: str | None) -> str | None: | |
| if value is None: | |
| return value | |
| if value not in VALID_PRESETS: | |
| raise ValueError( | |
| f"Unknown preset {value!r}; valid presets: {sorted(VALID_PRESETS)}" | |
| ) | |
| return value | |
| def _validate_override_keys( | |
| _cls, value: dict[str, bool] | None | |
| ) -> dict[str, bool] | None: | |
| if value is None: | |
| return value | |
| unknown = sorted(set(value) - _VALID_OVERRIDE_KEYS) | |
| if unknown: | |
| raise ValueError( | |
| f"Unknown custom_overrides keys: {unknown}; " | |
| f"valid keys: {sorted(_VALID_OVERRIDE_KEYS)}" | |
| ) | |
| return value | |
| class CheckResult(BaseModel): | |
| """One row of the public audit report. | |
| A row corresponds to a check group (for example ``robots_txt`` or | |
| ``schema_ld``) that may bundle several underlying sub-signals. Rows that | |
| are not yet scored are returned with ``state='warn'`` and | |
| ``evidence_level='unknown'`` rather than being omitted from the response. | |
| """ | |
| pillar: PillarName = Field( | |
| description=( | |
| "Pillar this row contributes to. ``off_site`` covers off-site " | |
| "presence and entity metadata; ``scrapability`` covers AI " | |
| "crawler access, structured data, and machine-readable surfaces; " | |
| "``seo`` covers traditional search discovery signals." | |
| ), | |
| examples=["scrapability"], | |
| ) | |
| check_name: str = Field( | |
| description=( | |
| "Stable machine identifier for the check group. Use this key " | |
| "rather than ``label`` for downstream filtering; ``label`` is " | |
| "human-readable and may change without a contract bump." | |
| ), | |
| examples=["robots_txt", "schema_ld", "llms_txt"], | |
| ) | |
| label: str = Field( | |
| description=( | |
| "Human-readable title for the check group, suitable for " | |
| "displaying in dashboards." | |
| ), | |
| examples=["AI Bot Policy Signals"], | |
| ) | |
| state: Literal["pass", "partial", "fail", "warn", "locked"] = Field( | |
| description=( | |
| "Outcome state. ``pass`` and ``partial`` mean the row scored " | |
| "evidence on the live site; ``fail`` means the row ran and the " | |
| "site did not satisfy it; ``warn`` means the row is inconclusive " | |
| "(check failed to complete or returned no signal); ``locked`` " | |
| "means the row is reserved for a paid tier and is not scored in " | |
| "Essentials." | |
| ), | |
| examples=["partial"], | |
| ) | |
| evidence_level: Literal[ | |
| "verified", "inferred", "unknown", "not_applicable" | |
| ] = Field( | |
| default="inferred", | |
| description=( | |
| "How strong the evidence behind this row is. ``verified`` means " | |
| "the live public response was inspected; ``inferred`` means the " | |
| "row used deterministic heuristics or metadata; ``unknown`` " | |
| "means the row could not be evaluated; ``not_applicable`` means " | |
| "the row was excluded by the active scope/preset. Default is " | |
| "``inferred``: most check results combine deterministic " | |
| "heuristics with snapshot evidence rather than a full verified " | |
| "live-response crawl, so the safer default avoids overclaiming " | |
| "verification when callers omit the field." | |
| ), | |
| examples=["inferred"], | |
| ) | |
| available_in: str = Field( | |
| default="Essentials", | |
| description=( | |
| "Tier or surface where this row is fully evaluated. ``Essentials`` " | |
| "means the row is scored in the free audit; ``Starter`` and " | |
| "``Pro`` mean the row is locked advanced coverage and not scored " | |
| "in Essentials." | |
| ), | |
| examples=["Essentials", "Starter", "Pro"], | |
| ) | |
| score: int = Field( | |
| description=( | |
| "Points earned on this row. Sum of all included row scores is " | |
| "the per-pillar score; overall score is the sum across the three " | |
| "pillars, capped at the rubric (100 points total)." | |
| ), | |
| examples=[3], | |
| ) | |
| max_score: int = Field( | |
| description=( | |
| "Maximum points this row can earn when the active scope includes " | |
| "every surface the row checks. The checked denominator across all " | |
| "Essentials rows is 56 by default and grows when scope toggles " | |
| "add protocol, account/auth, or commerce surfaces." | |
| ), | |
| examples=[6], | |
| ) | |
| finding: str = Field( | |
| description=( | |
| "Public-facing explanation of what was observed. Safe to surface " | |
| "directly in dashboards; never contains internal infrastructure " | |
| "detail or fetched response bodies." | |
| ), | |
| examples=["The robots.txt file does not mention the GPTBot user-agent."], | |
| ) | |
| fix: str = Field( | |
| description=( | |
| "Public-facing remediation hint tied to this finding. The hint is " | |
| "deterministic and never presumes access to internal tooling." | |
| ), | |
| examples=[ | |
| "Add an explicit GPTBot directive (Allow or Disallow) to robots.txt." | |
| ], | |
| ) | |
| effort: EffortLevel = Field( | |
| description=( | |
| "Estimated effort to apply the suggested fix. ``low`` is a " | |
| "single-file config edit; ``medium`` is a small code or template " | |
| "change; ``high`` is a structural site change." | |
| ), | |
| examples=["low"], | |
| ) | |
| class AuditScope(BaseModel): | |
| include_protocols: bool = Field( | |
| description=( | |
| "Resolved protocol/API scope for this audit. ``True`` when " | |
| "protocol families (API catalog, MCP, A2A, Agent Skills, WebMCP) " | |
| "are scored; derived from the preset when one is applied." | |
| ), | |
| ) | |
| include_account_auth: bool = Field( | |
| description=( | |
| "Resolved account/auth scope. ``True`` when OAuth/OIDC or " | |
| "auth.md surfaces are scored." | |
| ), | |
| ) | |
| include_ecommerce: bool = Field( | |
| description=( | |
| "Resolved commerce scope. ``True`` when Product/Offer JSON-LD, " | |
| "price/availability, and commerce-protocol metadata are scored." | |
| ), | |
| ) | |
| label: str = Field( | |
| description=( | |
| "Human-readable label for the resolved scope, suitable for " | |
| "displaying next to the score. For preset audits this is the " | |
| "preset label (for example ``'Blog/Content audit'``); for legacy " | |
| "boolean audits this is the legacy ``General website + extras`` " | |
| "string." | |
| ), | |
| examples=["Blog/Content audit"], | |
| ) | |
| included_optional_surfaces: list[str] = Field( | |
| description=( | |
| "Optional surfaces (protocol, account/auth, commerce) that were " | |
| "enabled for this audit. Empty when only the universal core is " | |
| "scored." | |
| ), | |
| ) | |
| excluded_optional_surfaces: list[str] = Field( | |
| description=( | |
| "Optional surfaces that were deliberately excluded by the " | |
| "preset/legacy booleans. The audit never penalises a site for " | |
| "excluded surfaces." | |
| ), | |
| ) | |
| # Preset resolution output (added fields). | |
| preset_applied: str | None = Field( | |
| default=None, | |
| description=( | |
| "Preset identifier that won scope resolution for this " | |
| "audit, or ``null`` when the legacy boolean path was used." | |
| ), | |
| examples=[None, "blog", "saas"], | |
| ) | |
| overrides_applied: dict[str, bool] = Field( | |
| default_factory=dict, | |
| description=( | |
| "Custom/Power User override keys that the audit actually applied " | |
| "on top of the preset defaults. Empty for legacy audits." | |
| ), | |
| ) | |
| included_families: list[str] = Field( | |
| default_factory=list, | |
| description=( | |
| "Sub-signal families that were enabled for this audit after " | |
| "preset + override resolution. Universal-core families are not " | |
| "listed here." | |
| ), | |
| ) | |
| excluded_families: list[str] = Field( | |
| default_factory=list, | |
| description=( | |
| "Sub-signal families that were explicitly disabled for this " | |
| "audit (by preset, override, or universal exclusion)." | |
| ), | |
| ) | |
| machine_surfaces_scope: Literal["common-contextual", "full"] = Field( | |
| default="common-contextual", | |
| description=( | |
| "Agent Protocol Discovery depth. ``common-contextual`` is the " | |
| "default bounded probe; ``full`` is selected by presets that " | |
| "explicitly opt into the complete protocol surface set." | |
| ), | |
| examples=["common-contextual", "full"], | |
| ) | |
| class PillarScores(BaseModel): | |
| off_site: int = Field( | |
| description="Points earned on the off-site pillar (0-30).", | |
| ) | |
| scrapability: int = Field( | |
| description="Points earned on the AI access / scrapability pillar (0-40).", | |
| ) | |
| seo: int = Field( | |
| description="Points earned on the SEO pillar (0-30).", | |
| ) | |
| class PillarMax(BaseModel): | |
| off_site: int = Field( | |
| description="Maximum off-site pillar score possible under the resolved scope.", | |
| ) | |
| scrapability: int = Field( | |
| description="Maximum AI access pillar score possible under the resolved scope.", | |
| ) | |
| seo: int = Field( | |
| description="Maximum SEO pillar score possible under the resolved scope.", | |
| ) | |
| class BenchmarkEntry(BaseModel): | |
| name: str = Field(description="Display name for the benchmark peer.") | |
| category: str = Field( | |
| description=( | |
| "Peer category bucket used by the benchmark profile generator " | |
| "(for example, ``'blog'``, ``'saas'``, ``'ecommerce'``)." | |
| ), | |
| ) | |
| group: str = Field( | |
| description=( | |
| "Peer size/cohort group (``'small'``, ``'medium'``, ``'large'``) " | |
| "used for percentile grouping." | |
| ), | |
| ) | |
| size: str = Field( | |
| description="Human-readable size label for the peer.", | |
| ) | |
| url: str = Field( | |
| description=( | |
| "Public benchmark peer URL. Safe to surface in dashboards; no " | |
| "private benchmarks are exposed through this field." | |
| ), | |
| ) | |
| overall_score: int = Field(description="Peer overall score at snapshot time.") | |
| free_evidence_score: int = Field( | |
| description="Peer free-evidence score at snapshot time." | |
| ) | |
| checked_score: int = Field(description="Peer checked-score at snapshot time.") | |
| checked_max: int = Field(description="Peer checked denominator at snapshot time.") | |
| agent_readiness_score: int = Field( | |
| description="Peer strict agent-readiness score at snapshot time." | |
| ) | |
| agent_readiness_earned: int = Field( | |
| description="Peer strict agent-readiness earned points." | |
| ) | |
| agent_readiness_max: int = Field( | |
| description="Peer strict agent-readiness denominator." | |
| ) | |
| pillar_scores: PillarScores = Field( | |
| description="Peer pillar scores at snapshot time." | |
| ) | |
| class BenchmarkComparison(BaseModel): | |
| score: int = Field( | |
| description=( | |
| "The site's free-evidence score (Essentials evidence only, no " | |
| "locked rows). This is the score used for peer comparison." | |
| ), | |
| ) | |
| checked_score: int = Field( | |
| description="Checked score earned by the site on this audit." | |
| ) | |
| checked_max: int = Field( | |
| description="Checked denominator for the resolved scope of this audit." | |
| ) | |
| benchmark_count: int = Field( | |
| description="Number of public benchmark peers considered." | |
| ) | |
| median_score: int = Field( | |
| description="Median free-evidence score across public benchmark peers." | |
| ) | |
| percentile: int = Field( | |
| description=( | |
| "Site's percentile rank against public peers (0-100). 100 means " | |
| "the site scored at or above every peer; 0 means it scored at or " | |
| "below every peer." | |
| ), | |
| ) | |
| position_label: str = Field( | |
| description=( | |
| "Human-readable position label (for example, ``'Above median'`` " | |
| "or ``'Below median'``). Always paired with the numeric " | |
| "percentile; never the only signal." | |
| ), | |
| examples=["Above median", "At median", "Below median"], | |
| ) | |
| nearest: list[BenchmarkEntry] = Field( | |
| description=( | |
| "Closest peers by free-evidence score, used for explainable " | |
| "comparison. Empty when no peers are available." | |
| ), | |
| ) | |
| entries: list[BenchmarkEntry] = Field( | |
| description=( | |
| "All public peers considered for this comparison. Empty when no " | |
| "public profiles are loaded; private production profiles are " | |
| "never exposed through this field." | |
| ), | |
| ) | |
| basis: str = Field( | |
| description=( | |
| "Plain-text description of how the benchmark was computed " | |
| "(profile source, denominator, scope filter). Always includes " | |
| "the public/private source distinction." | |
| ), | |
| ) | |
| snapshot_date: str = Field( | |
| description=( | |
| "ISO date the benchmark snapshot was generated. Benchmark " | |
| "profiles are refreshed when scoring, options, or applicability " | |
| "rules change." | |
| ), | |
| examples=["2026-07-17"], | |
| ) | |
| caveat: str = Field( | |
| description=( | |
| "Mandatory caveat explaining what the benchmark does and does " | |
| "not prove. Agents should surface this text alongside any " | |
| "percentile or comparison claim." | |
| ), | |
| ) | |
| class AgentBenchmarkComparison(BaseModel): | |
| score: int = Field(description="Site's strict agent-readiness score.") | |
| earned: int = Field(description="Points earned on the strict agent-readiness lens.") | |
| max: int = Field( | |
| description="Strict agent-readiness denominator for the resolved scope." | |
| ) | |
| benchmark_count: int = Field( | |
| description="Number of public peers considered for the agent benchmark." | |
| ) | |
| median_score: int = Field( | |
| description="Median strict agent-readiness score across public peers." | |
| ) | |
| percentile: int = Field( | |
| description="Site's percentile rank on the strict agent-readiness lens." | |
| ) | |
| position_label: str = Field( | |
| description="Human-readable agent-benchmark position label." | |
| ) | |
| nearest: list[BenchmarkEntry] = Field( | |
| description="Closest peers by strict agent-readiness score." | |
| ) | |
| entries: list[BenchmarkEntry] = Field( | |
| description="All public peers considered for the agent benchmark." | |
| ) | |
| basis: str = Field( | |
| description="Plain-text basis string for the agent benchmark." | |
| ) | |
| snapshot_date: str = Field( | |
| description="ISO date the agent-benchmark snapshot was generated." | |
| ) | |
| caveat: str = Field( | |
| description="Mandatory caveat explaining what the agent benchmark does and does not prove." | |
| ) | |
| class AgentReadinessCategory(BaseModel): | |
| name: str = Field(description="Category name (for example, ``'Discovery'``).") | |
| earned: int = Field(description="Points earned in this category.") | |
| max: int = Field(description="Maximum points for this category.") | |
| score: int = Field(description="Category percentage score (0-100).") | |
| passed: list[str] = Field( | |
| description="Public signals that this category verified on the site." | |
| ) | |
| missing: list[str] = Field( | |
| description="Public signals this category looked for and did not find." | |
| ) | |
| excluded: list[str] = Field( | |
| description=( | |
| "Surfaces excluded by the active scope/preset. Listed for " | |
| "transparency; they do not affect the category score." | |
| ), | |
| ) | |
| class AgentReadinessSummary(BaseModel): | |
| score: int = Field( | |
| description=( | |
| "Strict agent-readiness percentage score (0-100). Measures " | |
| "explicit agent-native discovery and protocol signals; stricter " | |
| "than general crawlability or SEO." | |
| ), | |
| ) | |
| earned: int = Field( | |
| description="Points earned on the strict agent-readiness lens." | |
| ) | |
| max: int = Field( | |
| description="Strict agent-readiness denominator for the resolved scope." | |
| ) | |
| label: str = Field( | |
| description="Human-readable label for the strict agent-readiness score.", | |
| examples=[ | |
| "Strong agent-native readiness", | |
| "Developing agent-native readiness", | |
| "Limited agent-native readiness", | |
| "Agent-native unavailable", | |
| ], | |
| ) | |
| categories: list[AgentReadinessCategory] = Field( | |
| description="Per-category breakdown of the strict agent-readiness score." | |
| ) | |
| passed: list[str] = Field( | |
| description="All agent-native signals verified on the site." | |
| ) | |
| missing: list[str] = Field( | |
| description="All agent-native signals the audit looked for and did not find." | |
| ) | |
| not_checked: list[str] = Field( | |
| description=( | |
| "Agent-native surfaces the audit deliberately did not check, " | |
| "for example when an individual probe failed and produced an " | |
| "inconclusive warning instead of verified evidence." | |
| ), | |
| ) | |
| benchmark: AgentBenchmarkComparison = Field( | |
| description="Peer comparison on the strict agent-readiness lens." | |
| ) | |
| caveat: str = Field( | |
| description=( | |
| "Mandatory caveat explaining what the strict agent-readiness " | |
| "score does and does not prove." | |
| ), | |
| ) | |
| class AuditResult(BaseModel): | |
| # API versioning. Additive only — the field carries the contract | |
| # version that produced this response. Clients may rely on it to detect | |
| # future additions without breaking on them. | |
| api_version: str = Field( | |
| default="1.0", | |
| description=( | |
| "MachineRead API contract version that produced this response. " | |
| "Add-only: the field value is bumped when the contract changes " | |
| "in a breaking way. Agents should treat unknown api_version " | |
| "values as forward-compatible and ignore unknown fields rather " | |
| "than reject the response." | |
| ), | |
| examples=["1.0"], | |
| ) | |
| url: str = Field( | |
| description=( | |
| "Final audited URL after normalization. Always starts with " | |
| "``https://`` (or ``http://`` when the user explicitly opted " | |
| "into plain HTTP) and ends with a trailing slash." | |
| ), | |
| ) | |
| scope: AuditScope = Field( | |
| description="Resolved audit scope (see AuditScope).", | |
| ) | |
| overall_score: int = Field( | |
| description=( | |
| "Full-rubric score on a 100-point scale. Includes locked " | |
| "advanced rows (score 0 until verified) and capped at the " | |
| "active pillar maxima." | |
| ), | |
| ) | |
| pillar_scores: PillarScores = Field( | |
| description="Per-pillar earned points." | |
| ) | |
| pillar_max: PillarMax = Field( | |
| description="Per-pillar maximum under the resolved scope." | |
| ) | |
| agent_readiness: AgentReadinessSummary = Field( | |
| description=( | |
| "Strict agent-native readiness summary. Stricter than the " | |
| "general scrapability pillar; measures explicit agent-protocol " | |
| "discovery and signal verification." | |
| ), | |
| ) | |
| benchmark: BenchmarkComparison = Field( | |
| description=( | |
| "Public peer comparison on the Essentials evidence score. " | |
| "Private production benchmark profiles are never exposed; only " | |
| "public-safe snapshot entries are returned." | |
| ), | |
| ) | |
| checks: list[CheckResult] = Field( | |
| description=( | |
| "Per-check group results. Length is 13 for a default Essentials " | |
| "audit; locked advanced rows are appended at the bottom with " | |
| "``state='locked'``." | |
| ), | |
| ) | |
| # Explicit public error models. These shapes are referenced in the | |
| # `responses={...}` mapping on `/v1/audit` so the autogenerated OpenAPI schema | |
| # documents the exact contract for agents. | |
| class ErrorMessage(BaseModel): | |
| detail: str = Field( | |
| description="Human-readable explanation of why the request was rejected.", | |
| examples=[ | |
| "URL resolves to a private address. Retry with a public HTTP(S) endpoint." | |
| ], | |
| ) | |
| class ValidationErrorMessage(BaseModel): | |
| detail: str = Field( | |
| description="Human-readable explanation of the validation failure.", | |
| examples=[ | |
| "Request body failed validation: url is required.", | |
| ], | |
| ) | |
| errors: list[dict] = Field( | |
| default_factory=list, | |
| description=( | |
| "Structured per-field or per-rule error descriptors. FastAPI's " | |
| "default 422 response uses a list of `{loc, msg, type}` items; " | |
| "preset override failures expose the offending keys and reasons." | |
| ), | |
| examples=[[]], | |
| ) | |
| class RateLimitErrorMessage(BaseModel): | |
| detail: str = Field( | |
| description="Human-readable explanation that the request was rate-limited.", | |
| examples=[ | |
| "Rate limit exceeded. Retry after the time indicated by Retry-After.", | |
| ], | |
| ) | |
| retry_after: int = Field( | |
| description=( | |
| "Whole seconds the client should wait before retrying. Mirrors " | |
| "the `Retry-After` response header and the `X-RateLimit-Reset` " | |
| "value." | |
| ), | |
| examples=[42], | |
| ) | |
| # Agent-oriented compact report summary. ``AuditSummary`` is the | |
| # strict-mode projection of ``AuditResult`` returned by | |
| # ``POST /v1/audit/summary``. It excludes LLM prose, finding/fix strings, | |
| # benchmark peer entries, agent-readiness passed/missing/not_checked lists, | |
| # rate-limit body metadata, and raw context. | |
| _SUMMARY_CHECK_NAME = Literal[ | |
| "robots_txt", | |
| "bot_access", | |
| "html_structure", | |
| "schema_ld", | |
| "llms_txt", | |
| "ssr", | |
| "machine_surfaces", | |
| "pagespeed", | |
| "canonical", | |
| "indexing", | |
| "search_discovery", | |
| "social", | |
| "wikipedia", | |
| ] | |
| _SUMMARY_PILLAR = Literal["off_site", "scrapability", "seo"] | |
| _SUMMARY_STATE = Literal["fail", "partial", "warn"] | |
| _SUMMARY_EVIDENCE = Literal["verified", "inferred", "sampled", "unavailable"] | |
| _SUMMARY_EFFORT = Literal["low", "medium", "high"] | |
| class SummaryScope(BaseModel): | |
| model_config = ConfigDict(extra="forbid", strict=True) | |
| preset: Literal["blog", "corporate", "services", "ecommerce", "news", "saas", "custom"] | None = Field( | |
| description=( | |
| "Preset identifier that won scope resolution for this audit, or " | |
| "``null`` when the legacy boolean path was used." | |
| ), | |
| examples=[None, "blog", "saas"], | |
| ) | |
| protocols: bool = Field( | |
| description=( | |
| "Resolved protocol/API scope for the audit. ``True`` when the " | |
| "active scope scores protocol-family surfaces." | |
| ), | |
| ) | |
| account_auth: bool = Field( | |
| description=( | |
| "Resolved account/auth scope for the audit. ``True`` when the " | |
| "active scope scores account/auth surfaces." | |
| ), | |
| ) | |
| ecommerce: bool = Field( | |
| description=( | |
| "Resolved commerce scope for the audit. ``True`` when the active " | |
| "scope scores commerce surfaces." | |
| ), | |
| ) | |
| overrides: dict[str, bool] = Field( | |
| default_factory=dict, | |
| description=( | |
| "Custom/Power User override keys the audit applied on top of the " | |
| "preset defaults. Empty when the legacy boolean path was used." | |
| ), | |
| ) | |
| class SummaryScorePair(BaseModel): | |
| model_config = ConfigDict(extra="forbid", strict=True) | |
| earned: int = Field(description="Points earned on the associated scope.") | |
| max: int = Field(description="Maximum points possible on the associated scope.") | |
| class SummaryPercent(BaseModel): | |
| model_config = ConfigDict(extra="forbid", strict=True) | |
| percent: int = Field(description="Percentage score for the associated scope (0-100).") | |
| earned: int = Field(description="Points earned on the associated scope.") | |
| max: int = Field(description="Maximum points possible on the associated scope.") | |
| class SummaryScores(BaseModel): | |
| model_config = ConfigDict(extra="forbid", strict=True) | |
| overall: SummaryScorePair = Field( | |
| description=( | |
| "Full-rubric overall score. ``max`` is the sum of pillar caps " | |
| "(100) regardless of the active scope." | |
| ), | |
| ) | |
| pillars: dict[str, SummaryScorePair] = Field( | |
| description=( | |
| "Per-pillar earned and max points. Keys are always " | |
| "``off_site``, ``scrapability``, and ``seo``." | |
| ), | |
| ) | |
| essentials: SummaryPercent = Field( | |
| description=( | |
| "Essentials evidence score (peer comparison source). ``percent`` " | |
| "mirrors ``AuditResult.benchmark.score``; ``earned`` and ``max`` " | |
| "are the source ``checked_score`` and ``checked_max``." | |
| ), | |
| ) | |
| agent_readiness: SummaryPercent = Field( | |
| description=( | |
| "Strict agent-readiness score. ``percent`` mirrors " | |
| "``AuditResult.agent_readiness.score``; ``earned`` and ``max`` " | |
| "are the source ``earned`` and ``max``." | |
| ), | |
| ) | |
| class SummaryBenchmark(BaseModel): | |
| model_config = ConfigDict(extra="forbid", strict=True) | |
| percentile: int = Field(description="Site percentile rank against public peers (0-100).") | |
| median_percent: int = Field( | |
| description=( | |
| "Median peer score (percent) the benchmark comparison used. The " | |
| "source field ``BenchmarkComparison.median_score`` is renamed to " | |
| "``median_percent`` to make the unit explicit in the agent surface." | |
| ), | |
| ) | |
| peer_count: int = Field(description="Number of public benchmark peers considered.") | |
| snapshot: str = Field( | |
| description=( | |
| "ISO date the benchmark snapshot was generated. Benchmark profiles " | |
| "are refreshed when scoring, options, or applicability rules change." | |
| ), | |
| ) | |
| class SummaryCheckCounts(BaseModel): | |
| model_config = ConfigDict(extra="forbid", populate_by_name=True) | |
| included: int = Field(description="Count of non-locked Essentials check rows (13).") | |
| locked: int = Field(description="Count of locked advanced rows (9).") | |
| # ``pass`` is a reserved keyword in Python. Pydantic v2 allows aliases | |
| # but we want the public JSON key to remain ``pass`` (matches the | |
| # runtime terminology). ``populate_by_name`` lets the constructor use | |
| # ``pass_count`` while the serialized JSON key stays ``pass``. | |
| pass_count: int = Field( | |
| alias="pass", | |
| description="Number of included rows with state ``pass``.", | |
| ) | |
| partial: int = Field(description="Number of included rows with state ``partial``.") | |
| fail: int = Field(description="Number of included rows with state ``fail``.") | |
| warn: int = Field(description="Number of included rows with state ``warn``.") | |
| attention_total: int = Field( | |
| description=( | |
| "Count of items in the ``attention`` array after the 5-row cap. " | |
| "This matches the actual returned count rather than the pre-cap " | |
| "candidate count so the field never disagrees with the array length." | |
| ), | |
| ) | |
| class SummaryAttentionItem(BaseModel): | |
| model_config = ConfigDict(extra="forbid", strict=True) | |
| check_name: _SUMMARY_CHECK_NAME = Field( | |
| description="Stable machine identifier for the check group.", | |
| ) | |
| pillar: _SUMMARY_PILLAR = Field(description="Pillar the row contributes to.") | |
| state: _SUMMARY_STATE = Field(description="Attention state of the row.") | |
| evidence_level: _SUMMARY_EVIDENCE = Field( | |
| description="Evidence maturity for the row.", | |
| ) | |
| earned: int = Field(description="Points earned on the row.") | |
| max: int = Field(description="Maximum points possible on the row.") | |
| effort: _SUMMARY_EFFORT = Field(description="Estimated remediation effort.") | |
| # Fixed ordered tuple of limitation codes that always appear in the | |
| # 200 payload in the same order. Exposed as a module-level constant so | |
| # tests, projection code, and the OpenAPI examples all reference the same | |
| # source of truth. | |
| SUMMARY_LIMITATION_CODES: tuple[ | |
| Literal["relative_scores"], | |
| Literal["no_live_ranking"], | |
| Literal["no_provider_ip_auth"], | |
| Literal["no_paid_crawlers"], | |
| ] = ( | |
| "relative_scores", | |
| "no_live_ranking", | |
| "no_provider_ip_auth", | |
| "no_paid_crawlers", | |
| ) | |
| class AuditSummary(BaseModel): | |
| # The agent-oriented compact report summary. The shape is | |
| # strict, additive-only, and intentionally smaller than | |
| # ``AuditResult`` so agents can reason about Essentials without | |
| # pulling the full per-check evidence into context. | |
| model_config = ConfigDict(extra="forbid", strict=True) | |
| api_version: Literal["1.0"] = Field( | |
| description=( | |
| "MachineRead API contract version that produced this response. " | |
| "Same semantics as ``AuditResult.api_version``." | |
| ), | |
| ) | |
| summary_version: Literal["1.0"] = Field( | |
| description=( | |
| "Summary contract version. Bumped when ``AuditSummary`` adds or " | |
| "renames a field in a breaking way." | |
| ), | |
| ) | |
| url: str = Field(description="Final audited URL after normalization.") | |
| scope: SummaryScope = Field(description="Resolved audit scope (compact form).") | |
| scores: SummaryScores = Field(description="Compact scoring surface.") | |
| benchmarks: dict[str, SummaryBenchmark] = Field( | |
| description=( | |
| "Compact benchmark comparison. Always carries the keys " | |
| "``essentials`` and ``agent_readiness``." | |
| ), | |
| ) | |
| checks: SummaryCheckCounts = Field(description="Compact check row counts.") | |
| attention: list[SummaryAttentionItem] = Field( | |
| description=( | |
| "Up to 5 most-urgent attention rows, sorted by severity then " | |
| "ratio then check name. Empty when every included row passes." | |
| ), | |
| ) | |
| limitations: tuple[str, str, str, str] = Field( | |
| description=( | |
| "Fixed ordered tuple of limitation codes that always appear in " | |
| "the 200 payload: ``relative_scores``, ``no_live_ranking``, " | |
| "``no_provider_ip_auth``, ``no_paid_crawlers``." | |
| ), | |
| ) |