feat(orchestrator): types — VisualRefProvider trait + SkippedVisualRefProvider stub

This commit is contained in:
Fini 2026-05-23 20:03:55 +08:00
parent 1a601eca70
commit 8444d4355c
3 changed files with 71 additions and 3 deletions

View file

@ -56,5 +56,8 @@ pub use intent::classify_intent;
pub use model_profile::{resolve_model_profile, ModelProfile, ModelTier};
pub use prompt::build_orchestrator_prompt;
pub use run::Orchestrator;
pub use stub_providers::{SkippedPreValidator, SkippedScreenshotProvider, SkippedVisionLlmClient};
pub use stub_providers::{
SkippedPreValidator, SkippedScreenshotProvider, SkippedVisionLlmClient,
SkippedVisualRefProvider,
};
pub use types::*;

View file

@ -1,5 +1,5 @@
//! Production-visible stub provider implementations for the vision
//! validation pipeline (S3c).
//! validation pipeline (S3c) and visual-ref pipeline (S4).
//!
//! Hosts use these when the real implementations are not yet wired —
//! `op-design-lint` integration is pending host wiring, screenshot
@ -14,6 +14,8 @@
//! loop exits before any vision round.
//! - `SkippedVisionLlmClient::validate` → `VisionResponse::Skipped`
//! (defensive in case `capture_root_frame` ever returns `Some`).
//! - `SkippedVisualRefProvider::render_html_to_screenshot` → `None`,
//! so the visual-ref pipeline skips immediately to plain orchestration.
//!
//! Net result: the Progress stream emits `ValidationStarted` →
//! `ValidationPreCheckDone { applied: 0, .. }` →
@ -23,7 +25,7 @@
use crate::types::{
DocSink, PreValidationResult, PreValidator, ScreenshotProvider, VisionCallRequest,
VisionLlmClient, VisionResponse,
VisionLlmClient, VisionResponse, VisualRefProvider,
};
/// Stub `PreValidator` — always returns zero fixes; no side effects.
@ -53,3 +55,13 @@ impl VisionLlmClient for SkippedVisionLlmClient {
VisionResponse::Skipped { reason: None }
}
}
/// Stub `VisualRefProvider` — always returns `None`. Visual-ref pipeline
/// short-circuits to plain orchestration without rendering any HTML.
pub struct SkippedVisualRefProvider;
impl VisualRefProvider for SkippedVisualRefProvider {
fn render_html_to_screenshot(&self, _html: &str, _width: f64, _height: f64) -> Option<String> {
None
}
}

View file

@ -160,6 +160,23 @@ pub struct ValidationProviders<'a> {
pub system_prompt: String,
}
// ── S4: VisualRefProvider ─────────────────────────────────────────────────────
/// Visual-reference rendering outlet.
///
/// host 实现把 HTML 字符串渲染成 base64 PNG 截图;stub 返回 `None`
/// 表示"跳过视觉参考阶段"。
///
/// Port of the `renderHtmlToScreenshot` call-site shape in
/// `visual-ref-orchestrator.ts:108-122` + spec §4.5.
pub trait VisualRefProvider: Send + Sync {
/// 将 HTML 字符串渲染为给定像素尺寸的截图,返回 base64 PNG;
/// `None` 表示不可用 / 跳过。
fn render_html_to_screenshot(&self, html: &str, width: f64, height: f64) -> Option<String>;
}
// ── S4 end ────────────────────────────────────────────────────────────────────
// ── S3c end ───────────────────────────────────────────────────────────────────
/// 廉价可克隆的中止句柄(`Arc<AtomicBool>` 语义)。
@ -564,6 +581,42 @@ mod tests {
}
}
// ── Task A2: VisualRefProvider trait + SkippedVisualRefProvider stub ─────────
/// `SkippedVisualRefProvider` returns `None` for any input.
#[test]
fn skipped_visual_ref_provider_returns_none() {
use crate::stub_providers::SkippedVisualRefProvider;
let p = SkippedVisualRefProvider;
assert!(p
.render_html_to_screenshot("<html></html>", 1280.0, 800.0)
.is_none());
assert!(p.render_html_to_screenshot("", 0.0, 0.0).is_none());
assert!(p
.render_html_to_screenshot("<html><body>Hello</body></html>", 390.0, 844.0)
.is_none());
}
/// `VisualRefProvider` trait is `Send + Sync`.
#[test]
fn visual_ref_provider_is_send_sync() {
use crate::types::VisualRefProvider;
fn assert_send_sync<T: Send + Sync + ?Sized>() {}
assert_send_sync::<dyn VisualRefProvider>();
}
/// `op_orchestrator::SkippedVisualRefProvider` resolves from a host-style import.
#[test]
fn skipped_visual_ref_provider_resolves_from_crate_root() {
use crate::{SkippedVisualRefProvider, VisualRefProvider};
let p: &dyn VisualRefProvider = &SkippedVisualRefProvider;
assert!(p
.render_html_to_screenshot("<p>test</p>", 800.0, 600.0)
.is_none());
}
// ── Task A2 end ───────────────────────────────────────────────────────────────
/// `DesignRequest.validation_enabled` defaults to `true` when omitted from JSON.
#[test]
fn design_request_validation_enabled_defaults_true() {