rpage 1.0.3

Rust browser automation (DrissionPage-inspired) with built-in AI agent diagnostics — page_snapshot/smart_click/eval_agent/page_diagnostics
//! Agent — high-level data structures for AI-agent–driven browser automation.
//!
//! These types describe the information an LLM / AI agent needs to understand a
//! page (summary, snapshot) and the result of an action it requested. They are
//! pure data carriers with no behaviour of their own; methods on
//! [`ChromiumPage`](crate::ChromiumPage) fill them in.

use std::collections::HashMap;

use serde::{Deserialize, Serialize};

// ---------------------------------------------------------------------------
// Interactive element
// ---------------------------------------------------------------------------

/// A single interactive (or informative) element on the page.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InteractiveElement {
    /// HTML tag name (lower-case), e.g. `"a"`, `"button"`, `"input"`.
    pub tag: String,
    /// Visible text content of the element (trimmed, max 256 chars).
    pub text: String,
    /// Input type attribute (empty string for non-input elements).
    #[serde(rename = "type")]
    pub input_type: String,
    /// The `name` attribute.
    pub name: String,
    /// Current value of the element.
    pub value: String,
    /// The `placeholder` attribute.
    pub placeholder: String,
    /// The `href` attribute (for links).
    pub href: String,
    /// Whether the element is currently visible.
    pub is_visible: bool,
    /// Bounding box `{x, y, w, h}` in CSS pixels.
    pub rect: Rect,
    /// All HTML attributes of the element.
    pub attributes: HashMap<String, String>,
}

/// Bounding box of an element.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Rect {
    pub x: f64,
    pub y: f64,
    pub w: f64,
    pub h: f64,
}

// ---------------------------------------------------------------------------
// Form structures
// ---------------------------------------------------------------------------

/// A single field inside a [`FormInfo`].
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FormField {
    /// The `name` attribute of the field.
    pub name: String,
    /// Input type (`"text"`, `"email"`, `"password"`, `"checkbox"`, …).
    #[serde(rename = "type")]
    pub field_type: String,
    /// Human-readable label associated with the field (via `<label>`, `aria-label`, …).
    #[serde(default)]
    pub label: String,
    /// Current value of the field.
    #[serde(default)]
    pub value: String,
    /// Whether the field has the `required` attribute.
    #[serde(default)]
    pub required: bool,
    /// For `<select>` elements: the list of option values / texts.
    #[serde(default)]
    pub options: Vec<String>,
}

/// A `<form>` element on the page.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FormInfo {
    /// The form's `action` URL (resolved to absolute).
    pub action: String,
    /// HTTP method (`"GET"` / `"POST"`).
    pub method: String,
    /// Fields contained in the form.
    pub fields: Vec<FormField>,
}

// ---------------------------------------------------------------------------
// Page-level summaries
// ---------------------------------------------------------------------------

/// Link pair: (visible_text, href).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LinkInfo {
    pub text: String,
    pub href: String,
}

/// A comprehensive summary of the page — everything an AI agent needs to decide
/// what to do next.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageSummary {
    /// The current URL.
    pub url: String,
    /// Document title.
    pub title: String,
    /// Content of the `<meta name="description">` tag, if present.
    #[serde(default)]
    pub description: Option<String>,
    /// All links on the page as (visible_text, href) pairs.
    pub links: Vec<LinkInfo>,
    /// Forms present on the page.
    pub forms: Vec<FormInfo>,
}

/// A lightweight snapshot of the visible page state, optimised for quick
/// consumption by an LLM context window.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageSnapshot {
    /// The current URL.
    pub url: String,
    /// Document title.
    pub title: String,
    /// Viewport size string, e.g. `"1920x1080"`.
    pub viewport_size: String,
    /// Scroll position string, e.g. `"x=0 y=300/5000"`.
    pub scroll_position: String,
    /// Interactive elements visible in the current viewport.
    pub interactive_elements: Vec<InteractiveElement>,
    /// Up to the first 2000 characters of visible body text.
    pub visible_text: String,
}

// ---------------------------------------------------------------------------
// Action result
// ---------------------------------------------------------------------------

/// Outcome of an action attempted by the agent.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActionAttempt {
    /// Whether the action completed without errors.
    pub success: bool,
    /// Error message if `success` is `false`.
    pub error: Option<String>,
    /// URL before the action was executed.
    pub before_url: String,
    /// URL after the action was executed.
    pub after_url: String,
}

// ---------------------------------------------------------------------------
// Diagnostics — "what went wrong on this page"
// ---------------------------------------------------------------------------

/// 页面诊断快照 —— 一次性聚合"页面出了什么问题",给 LLM 做调试决策。
///
/// agent 调用 [`ChromiumPage::page_diagnostics`](crate::ChromiumPage::page_diagnostics)
/// 获取。与 [`PageSnapshot`](struct.PageSnapshot.html)(描述"页面长什么样")互补:
/// snapshot 回答"页面上有什么",diagnostics 回答"页面有没有报错 / 接口有没有失败"。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageDiagnostics {
    /// 当前 URL。
    pub url: String,
    /// 页面标题。
    pub title: String,
    /// 是否存在 JS 异常或失败请求(快速判断"页面有没有问题")。
    pub has_errors: bool,
    /// 捕获到的 JS 异常(含源文件 URL / 行号 / 列号 / 调用栈)。
    pub exceptions: Vec<crate::console::JsException>,
    /// 错误(Error)与警告(Warn)级别的 console 日志。
    /// 过滤掉普通 log/info/debug,减少喂给 LLM 的无关 token。
    pub error_logs: Vec<crate::console::ConsoleEntry>,
    /// 失败的网络请求(4xx / 5xx / 超时 / CORS / DNS 失败等)。
    pub failed_requests: Vec<crate::network::FailedRequest>,
    /// 页面加载时序(Navigation Timing 各阶段毫秒)。
    /// `None` 表示 Performance API 不可用(如页面已卸载)。
    #[serde(default)]
    pub timing: Option<HashMap<String, f64>>,
}

/// JS 执行结果 —— agent 调用
/// [`ChromiumPage::eval_agent`](crate::ChromiumPage::eval_agent) 获取。
///
/// 把脚本的**返回值**与执行期间**新产生的控制台异常**打包返回,
/// 让 LLM 不光看到脚本算出了什么,还知道脚本有没有引发报错。
/// 这是"注入 → 观察"调试闭环的关键一环。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvalResult {
    /// 脚本是否成功执行(没有抛 JS 异常)。
    pub success: bool,
    /// 脚本表达式的返回值。
    /// 复杂对象会被 `JSON.stringify` 序列化为字符串或保留为 JSON Value。
    #[serde(default)]
    pub value: serde_json::Value,
    /// 执行期间新捕获到的 JS 异常(若有)。
    #[serde(default)]
    pub new_exceptions: Vec<crate::console::JsException>,
    /// 执行失败时的错误信息(`success = false` 时填充)。
    #[serde(default)]
    pub error: Option<String>,
}