report API
Luna-Flow/mare_mark/report 把 JSONL 事件流转换为 Plot IR 文档,并把该文档渲染为 mmks_1 JSON、独立的 SVG 或自包含的 HTML 页面。这四个函数都是纯函数:它们接收字符串和值,返回字符串。读写文件是 cli 的工作。投影规则在 report 设计 中解释。
import {
"Luna-Flow/mare_mark/ir_model",
"Luna-Flow/mare_mark/report",
}
解析事件
document_from_jsonl
document_from_jsonl 把 JSONL 事件流解析为 PlotDocument。
pub fn document_from_jsonl(String, target? : String) -> Result[@ir_model.PlotDocument, String]
target(默认为 "unknown")被复制到文档中;事件本身不携带它。空行会被跳过。其他每一行都必须是 JSON 对象,若存在 artifact_version,它必须是字符串 "mmka_1"。各行按其 "type" 解释:
type | 必需字段 | 效果 |
|---|---|---|
observation | implementation(字符串)、dataset_id、elapsed_us(数值) | 一个图表点(x = dataset_id,y = elapsed_us,series = implementation),除非 valid 为 false 或 batch_sink 不是 "kept" |
validation | 无 | 按 implementation 和 actual_kind(缺失时为 "unclassified")计入能力矩阵;状态为 invalid 或 infrastructure_failure 时会添加一行不匹配记录,并且在存在 case 和 dataset_id 时移除该实现在该用例和数据集上的点 |
validation_failure | 无 | 一行反例(最小输入、最小指纹、缩减路径、重放命令) |
summary | 无 | 设置 run_id 和语料计数;以最后一个摘要为准 |
| 其他任何类型 | 忽略,包括 calibration |
文档包含一个标题为 "Benchmark observations"、单位为 "µs/op"、区间种类为 "raw" 的 Scaling 图表;若没有任何点保留下来,则不含图表。探索性和验证性观测绘制在一起。语料总数是四个摘要计数之和。
错误是带有从 1 开始的行号的消息:无效 JSON、不是对象的行、不受支持或非字符串的产物版本、缺少 implementation、dataset_id 或 elapsed_us 的观测,以及既没有点、也没有不匹配和反例的流。
test "parse a small event stream" {
let source =
#|{"type":"observation","case":"add","implementation":"a","dataset_id":0,"elapsed_us":12.5}
#|{"type":"observation","case":"add","implementation":"b","dataset_id":0,"elapsed_us":10.0}
#|{"type":"observation","case":"add","implementation":"b","dataset_id":1,"elapsed_us":30.0,"valid":false}
#|{"type":"summary","run_id":"demo","passed_count":2}
let document = @report.document_from_jsonl(source, target="native").unwrap()
inspect(document.run_id, content="demo")
inspect(document.plots[0].points.length(), content="2")
inspect(document.differential.corpus.total, content="2")
let bad = @report.document_from_jsonl("{\"artifact_version\":\"mmka_9\"}")
inspect(bad is Err("unsupported artifact version 'mmka_9' at line 1"), content="true")
}
渲染
plot_json
plot_json 把文档序列化为 mmks_1 JSON。
pub fn plot_json(@ir_model.PlotDocument) -> String
该对象具有键 schema_version、run_id、target、plots(每项含 kind、title、unit、interval_kind,以及由 x、y、series 组成的 points)和 differential(mismatches、capabilities、counterexamples、corpus)。图表种类以蛇形命名写出(scaling、raw_distribution、block_order、interval、outlier、change_point、heatmap、pareto)。输出是紧凑的,字符串由 JSON 编码器转义。
test "plot JSON" {
let point = @ir_model.PlotPoint::new("0", 1.0, "a")
let plot = @ir_model.Plot::new(@ir_model.PlotKind::Scaling, "t", "µs/op", "raw", [point])
let document = @ir_model.PlotDocument::new("r", "native", [plot])
let json = @report.plot_json(document)
inspect(json.has_prefix("{\"schema_version\":\"mmks_1\",\"run_id\":\"r\""), content="true")
inspect(json.contains("\"points\":[{\"x\":\"0\",\"y\":1,\"series\":\"a\"}]"), content="true")
}
plot_svg
plot_svg 把一张图表渲染为独立的 SVG 元素。
pub fn plot_svg(@ir_model.Plot) -> String
该 SVG 的视框为 960 × 420,带有可访问的 <title> 和 <desc>、内联样式、五条带刻度值的水平网格线、最多约十个 x 标签以及图例。每个点绘制为一个圆,带有提示 series — x: y unit。Scaling、Interval 和 ChangePoint 图表还会按系列连接每个 x 类别上各点的均值。Heatmap 图表为每个系列和 x 类别绘制一个单元格,其不透明度随均值增大。空图表显示 “No observations”。所有文本都经过 XML 转义。
test "plot SVG" {
let points = [
@ir_model.PlotPoint::new("64", 2.0, "scalar"),
@ir_model.PlotPoint::new("128", 4.5, "scalar"),
]
let plot = @ir_model.Plot::new(@ir_model.PlotKind::Scaling, "a < b", "µs/op", "raw", points)
let svg = @report.plot_svg(plot)
inspect(svg.has_prefix("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 960 420\""), content="true")
inspect(svg.contains("<title id=\"plot-title\">a < b</title>"), content="true")
inspect(svg.contains("<polyline"), content="true")
}
html
html 把整个文档渲染为一个自包含的 HTML 页面。
pub fn html(@ir_model.PlotDocument) -> String
页面包含一个带有运行 id、目标和模式版本的页头,接着是差分报告(语料结果、不匹配、能力覆盖矩阵、最小反例),然后每张图表一个 figure。CSS 是内联的,图表是内联 SVG,没有脚本也没有外部资源,因此该文件可以原样归档或作为附件。输出只取决于文档。
test "self-contained HTML" {
let source =
#|{"type":"observation","case":"add","implementation":"a","dataset_id":0,"elapsed_us":12.5}
#|{"type":"summary","run_id":"demo"}
let page = @report.html(@report.document_from_jsonl(source).unwrap())
inspect(page.has_prefix("<!doctype html>"), content="true")
inspect(page.contains("<title>mare_mark · demo</title>"), content="true")
inspect(page.contains("<script"), content="false")
}