| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 21 天前 | ||
| 21 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 21 天前 |
aesthetics
Standalone UI aesthetics evaluation proxy (Rust). The proxy does not compute scores — evaluation lives in real evaluators behind one HTTP API:
- arktsx.aesthetics cascade (ArkTS, primary): L0 tree geometry, L1 pixel
heuristics, L2 local neural prior, L3 vision-LLM judge, US/EU/CN/JP
cultural profiles — reached headlessly via
demo/eval.ets(subprocess) or in-app via the graspaesthetics_assess/aesthetics_polltool - SPbU analyzer (external service): OCR contrast/readability/typography
- guideline CNN metrics the cascade does not compute; every 0..1 analyzer
score maps onto the 12-axis vector (see
analyzer_axes: contrast/typography/ consistency/hierarchy + the_JSONgeometry family → balance/density/ alignment, touch_target_size_JSON → affordance). With the parity-converted L2 ensemble vendored, the in-framework ResNet50 signal (hierarchy axis, neuralPrior) reproduces the analyzer's binarized guideline metric exactly (E2E max|Δ| = 0.0 on the tag-score; see parity/ in the aestetic workspace).
- guideline CNN metrics the cascade does not compute; every 0..1 analyzer
score maps onto the 12-axis vector (see
The proxy adds what evaluators should not care about: evidence assembly (A2UI rendering through the a2ui-server adapter), request routing with ordered fallback, the legacy dashboard projection, repair suggestions, telemetry, async jobs and HTML reports.
Layout
crates/aesthetic/ library crate — embed the proxy directly
src/proxy/ EvaluatorBackend trait + router + backends
cascade_subprocess.rs headless eval.ets runner (arkts run / bundle run.sh)
cascade_grasp.rs in-app cascade via the grasp channel
analyzer.rs SPbU analyzer client + Judgement mapping
types.rs EvalRequest, canonical Judgement (12 axes)
src/providers/ evidence: a2ui-server WS/STF render adapter,
OHOS device render adapter (grasp)
src/legacy.rs Judgement → legacy AestheticsReport (dashboards)
src/engine.rs AestheticProxy (evidence assembly + routing)
src/report.rs HTML report
crates/aesthetic-server/ axum HTTP service
Configuration (env)
| var | meaning |
|---|---|
EST_CASCADE_DIR |
arktsx/aesthetics package dir → enables the cascade subprocess backend; also the cwd for the runners (needs a compiled demo target in a checkout) |
EST_ARKTS_BIN |
arkts binary (default arkts); unused when a bundle is configured |
EST_EVAL_BUNDLE |
arkts bundle dir for demo/eval.ets → the proxy spawns its run.sh instead of arkts run (packaged deployments) |
EST_CONVERGENCE_BUNDLE |
arkts bundle dir for demo/convergence.ets |
EST_ANALYZER_URL |
SPbU analyzer base URL → enables the analyzer backend |
EST_GRASP_DEVICE + EST_GRASP_SSE |
in-app cascade via grasp (default sse http://localhost:4001/sse) |
EST_RENDER_WS_URL |
a2ui-server WS for A2UI evidence rendering |
EST_BIND |
listen address (default 0.0.0.0:8090) |
Cascade layer/judge wiring inside the subprocess follows the package's own
env keys (AESTHETICS_L2_{PARAM,BIN}, AESTHETICS_VISION_* / API keys) —
L2/L3 skip cleanly when unconfigured.
L2 guideline ensemble (SPbU vendor models, parity-converted ncnn): set
AESTHETICS_L2_DIR to a directory holding {Apple,Google,Huawei,Microsoft, Samsung}.{param,bin} (see arktsx/aesthetics/models/l2-ensemble/). This
takes precedence over the single-pair AESTHETICS_L2_{PARAM,BIN} path and
feeds neuralPrior = mean vendor probability, with per-vendor scores in the
layers.l2 detail. The optional YOLOv8 UI detector loads separately
(AESTHETICS_YOLO_{PARAM,BIN}) and is consumed by the affordance axis
tooling. Both run CPU/fp32/single-thread (setFp16(0), 1 thread) — the
parity-proven deterministic configuration.
HTTP API
| endpoint | purpose |
|---|---|
GET /healthz |
liveness + configured backends |
GET /v1/backends |
backend catalog (capabilities) |
POST /v1/score |
JSON: {screenId?, a2ui?, dump?, imageB64?, profile?, intent?, backend?, renderIfMissing?} → 202 {job_id} |
POST /v1/score/image |
multipart image field → 202 {job_id} |
POST /v1/aesthetics/report |
evaluate → legacy dashboard JSON (projection) |
GET /v1/jobs/{id} |
job status/result — canonical Judgement + legacy projection + suggestions + telemetry |
GET /v1/reports/{id} |
HTML report of a finished job |
The canonical result is the cascade's JudgementReport JSON 1:1 (camelCase:
screenId, composite, axes{12}, layers{l0..l3}, violations,
judgeFindings, regionalComposites, gates...). The legacy projection
adds stats.extendedAxes so no cascade axis is lost.
Library usage
let proxy = AestheticProxy::builder()
.with_backend(Arc::new(CascadeSubprocessBackend::new("arktsx/aesthetics")))
.with_backend(Arc::new(AnalyzerBackend { client: AnalyzerClient::new("http://192.168.100.13:8000") }))
.with_renderer(Arc::new(A2uiServerAdapter::new("ws://localhost:3099/ws/client")))
.build();
let report = proxy.score(ScoreRequest {
image: Some(png_bytes), // or a2ui + render_if_missing: true
profile: Some(Profile::Eu),
intent: "a flight card".into(),
..Default::default()
}).await?;
// report.judgement (canonical) / report.legacy (dashboards) / report.suggestions
Tests
cargo test # hermetic: judgement parsing, router selection/fallback,
# legacy translation, HTTP contract on ephemeral ports
The evaluator implementations themselves live upstream:
arktsx/aesthetics (cascade + its own test suite) and the SPbU analyzer
service. Proxy tests use mock backends; live runs are opt-in via env.
Extending
Add an evaluator by implementing EvaluatorBackend:
#[async_trait]
impl EvaluatorBackend for MyEvaluator {
fn name(&self) -> String { "mine".into() }
fn capabilities(&self) -> Capabilities { Capabilities { image: true, ..Default::default() } }
async fn evaluate(&self, req: &EvalRequest) -> Result<Judgement, EngineError> { /* ... */ }
}
Register it on the proxy/router; selection is capability-based with ordered
fallback. Output must be a Judgement — parse your evaluator's report or
map it (see proxy/analyzer.rs for a mapping example).