Skip to content

feat: add define_metric API for chart customization - #1733

Merged
SAKURA-CAT merged 14 commits into
mainfrom
feat/define-metric
Aug 29, 2026
Merged

feat: add define_metric API for chart customization#1733
SAKURA-CAT merged 14 commits into
mainfrom
feat/define-metric

Conversation

@Nexisato

@Nexisato Nexisato commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

新增 swanlab.define_metric() API,用于在 log 之前定义指标的图表行为:自定义 X 轴、图表分组、隐藏。配套完成 SDK 内部 define_metric 解析/对齐机制的架构重构(消费端按是否 define 分流、resolver 状态精简、builder/resolver 收归 consumer 包)。

API

swanlab.define_metric(
    key: str,                              # 精确 key 或末尾单个 '*' 的 glob(如 "train/*",单独的 "*" 匹配所有自定义 key)
    *,
    x_axis: Optional[str] = None,          # 自定义 X 轴 key;None 为系统 step;"_step"/"_relative_time" 为系统轴(由服务端解析,SDK 不做注入与去重);兼容 step_metric 别名
    section_name: Optional[str] = None,    # 图表分组名;None 使用默认分组
    hidden: Optional[bool] = None,         # 三态:None=未提供(merge 保留旧值)、True 隐藏、False 显式解除隐藏
    step_sync: Optional[bool] = None,      # X/Y 分开 log 时是否把最近 X 值同步到当前 step;设了 x_axis/step_metric 时默认 True,否则 False;显式 False 优先
    overwrite: bool = False,               # False 合并已有定义,True 重置未指定字段为默认
    **kwargs,                              # 仅用于 step_metric 兼容别名,其余未知参数静默忽略
)

语义要点

  • 图表定义 first-writer-wins:同一 (class, key) 全项目共享一张图,仅首个引入该 key 的 run 生效;key 首次 log 之后,本 run 内后续 define 不再生效(automatic 钉住,不回溯、无告警)
  • 未 define 的 key 列创建时机回归 core:SDK 只构建数据 record,列由 core 收到数据后自动创建(与引入 define_metric 前行为一致)
  • 自定义 X 假设单调递增:同一 X 值仅保留首个 Y(实现上抑制连续重复,epsilon 1e-8);非单调回退值会当作新值
  • 匹配优先级:exact > 最长前缀 glob > automatic
  • 仅支持末尾单个 *,非法 glob 在 API 层同步报错拒绝;x_axis 仅对 scalar 生效,media 一律回退 _step;scalar 允许自引用(x_axis == key)

Changes

  • 新增 DefinitionResolver 组件(run/components/consumer/resolver/):exact/glob rule 注册、首次 log 物化、custom X 缓存与 step_sync 注入、X 值去重
  • consumer 指标处理按「是否调用过 define_metric」分流为 plain / define 两条路径;plain 路径纯构建,零 resolver 交互
  • RecordBuilder / DefinitionResolver 收归 consumer 包;is_scalar_value 判定迁入 builder(与 build dispatch 保持一致)
  • 事件协议移除 UNSET 哨兵,字段统一 Optional 两态;hidden 升级为三态;is_glob 随事件显式下发,glob 校验上移到 API 层
  • resolver 状态精简:rule 字典直接存 EffectiveDefinition,删除 RuleState / DefinitionPatch / intern 缓存等冗余
  • helper/system.py 更名 metric.pyis_system_keyis_custom_x 同居
  • 删除 pkg/helper.calculate_section_name(core 专属逻辑内联回 build_auto_column,v0.10 分组语义不变,section_rule 读取完全收敛于 core;v0.11 移除该配置见 v0.11 弃用项移除盘点(legacy projects 支持下线等) #1753
  • proto:ColumnRecord 新增 x_axishidden 字段(含 Python/Go 重新生成);transport 按字段 presence 最小编码
  • 移除公开导出 swanlab.define_scalar(自发布起恒 raise NotImplementedError)

Testing

  • uv run pytest tests/unit:1758 passed,21 skipped
  • uv run ruff check swanlab/ tests/:通过
  • uv run basedpyright swanlab/ tests/:0 errors

Notes

  • BREAKING:移除 swanlab.define_scalar(破坏面仅为 from swanlab import define_scalar 的 ImportError)
  • 自定义 X 轴关联在复制/移动实验、import runs 时无法保留,key 会回退 step 计数
  • CI 工作流当前仅触发 swanlab/**test/** 路径,tests/** 下的测试可能不触发(仓库已知问题)

@Nexisato Nexisato self-assigned this Aug 12, 2026
@Nexisato
Nexisato force-pushed the feat/define-metric branch 2 times, most recently from 1c086b6 to 7c8e63d Compare August 19, 2026 09:53
@Nexisato
Nexisato force-pushed the feat/define-metric branch 2 times, most recently from b05f4b3 to 218fa2c Compare August 21, 2026 07:08
@SAKURA-CAT SAKURA-CAT mentioned this pull request Aug 25, 2026
@Nexisato
Nexisato force-pushed the feat/define-metric branch from 218fa2c to 90c1999 Compare August 26, 2026 10:30

@SAKURA-CAT SAKURA-CAT left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审核结论: 评论

本次 PR 解决的问题

新增 swanlab.define_metric() API(自定义 X 轴 + step_sync 注入、glob 规则、HIDDEN 分组),proto ColumnRecord 新增 x_axis/hidden 可选字段并同步 Python/Go 生成代码。

审核范围

  • 公共 API 与类型兼容: define_scalardefine_metric 重命名(降级说明见未挂行发现);签名与 .pyi 同步
  • Run 生命周期与运行时: resolver 随 Components 创建/停止,clear() 在 consumer join 后调用,顺序正确
  • Record、Core 与传输: 预扫描/候选注入两阶段设计(M1 nan 保真、S2 孤儿注入点)有注释与回归测试;flush 内 coalesce + core 存在性守卫无重复列定义;sync 回放 first-writer-wins
  • 配置、认证与 CLI: 不涉及
  • 媒体、集成与插件: _is_scalar_value 启发式与 build_scalar_or_media singledispatch 分支逐一对齐;MEDIA 回退 _step
  • Protobuf、构建与依赖: 字段 13/14 append-only 向后兼容;make proto 重新生成无 diff;Makefile 仅补 .PHONY
  • 安全、性能与跨平台兼容: 无凭据/网络变更;_prune_origins 防止 origins 无界增长;无 custom X 规则时预扫描整体跳过
  • 测试与验证: 新增 resolver/glob 校验/step_sync/X 去重/encode_column/sync parity 共 ~170 行测试,覆盖成功与失败路径

未挂行发现

  • 说明: swanlab.define_scalar 删除已复核:该名称虽随 v0.8.0+ 的 PyPI 产物发布(import/__all__/.pyi 均含),但实现自发布起即 raise NotImplementedError,不存在成功调用过它的用户代码,实际破坏面仅为 from swanlab import define_scalar 的 ImportError。降级为建议(见 inline),由维护者决策即可。
  • 说明: project-wide first-writer-wins 与 xAxis/hidden 上行依赖后端/视图支持(PR 描述已注明作用域限制)。SDK 侧 presence 编码正确,服务端行为超出本仓库范围。
  • 说明: _EFF_INTERN 为进程级 intern 缓存,不随 resolver.clear() 清理;注释已论证组合数有界,可接受。
  • 说明: X 值去重仅抑制连续重复(非单调 X 下同一 X 值可能出现多个 Y 点),docstring 与测试已如实记录该限制。

验证

  • 相关测试: test_resolver.py / test_consumer_step_sync.py / test_consumer_save.py / test_core_sync.py / test_sender.py — 99 passed
  • uv run ruff check . — 通过
  • uv run basedpyright — 0 errors
  • uv run pytest — 1727 passed, 21 skipped
  • PR Actions 已通过(三平台 × Python 3.9–3.14、Go lint、type check)
  • make proto 重新生成无 diff(本地 protoc 35.1 工具链验证)

Comment thread swanlab/__init__.py
Comment thread swanlab/__init__.pyi Outdated
Comment thread swanlab/sdk/internal/run/components/consumer/__init__.py Outdated
Comment thread swanlab/sdk/internal/run/components/resolver/state.py Outdated
Comment thread swanlab/sdk/typings/core_python/api/upload.py Outdated
@Nexisato
Nexisato force-pushed the feat/define-metric branch from d050ce4 to 7b3dd32 Compare August 28, 2026 07:35
Nexisato and others added 9 commits August 28, 2026 17:55
Validate glob patterns (only trailing single '*' allowed) in
Run.define_metric
before emitting events, rejecting invalid patterns without sending
events.
Pass the classification result via new `is_glob` field on
MetricDefineEvent,
so the resolver no longer parses glob strings. Also move `is_custom_x`
and
system X-axis constants to helper.metric module.
Refactor `_handle_metric_log` to route events based on whether
`define_metric` was called. The plain path builds data records without
emitting column definitions, while the define path materializes defined
columns and handles custom X-axis alignment. This also filters forged
system keys with a warning, and resolves concrete definitions without
emitting columns in the plain path.
… feat/define-metric

# Conflicts:
#	swanlab/__init__.pyi
#	swanlab/sdk/internal/bus/events.py
#	swanlab/sdk/internal/run/components/consumer/__init__.py
#	swanlab/sdk/internal/run/components/consumer/resolver/__init__.py
#	swanlab/sdk/internal/run/components/resolver/state.py
#	tests/unit/sdk/internal/run/components/consumer/builder/test_builder.py
#	tests/unit/sdk/internal/run/components/consumer/resolver/test_resolver.py
@SAKURA-CAT SAKURA-CAT changed the title Feat/define metric feat: add define_metric API for chart customization Aug 29, 2026
@SAKURA-CAT SAKURA-CAT self-assigned this Aug 29, 2026
SAKURA-CAT
SAKURA-CAT previously approved these changes Aug 29, 2026

@SAKURA-CAT SAKURA-CAT left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审核结论: 通过

本次 PR 解决的问题

新增 swanlab.define_metric() 公开 API(自定义 X 轴 / 图表分组 / 隐藏),配套 DefinitionResolver 注入对齐机制与 consumer plain/define 分流重构。

审核范围

  • 公共 API 与类型兼容: define_metric 新增、swanlab/__init__.pyi 签名与运行时一致(hidden 三态 / step_sync / **kwargs);define_scalar 移除已核实——main 上函数体恒 raise NotImplementedError,破坏面仅为 from swanlab import define_scalar 的 ImportError,PR Notes 描述准确
  • Run 生命周期与运行时: consumer 按 has_rules 分流、组件停止顺序保留、resolver 由消费者单线程独占、mid-run define 切换路径正确;late define 不可认领已 log key(automatic 钉住)契约有测试固化
  • Record、Core 与传输: encode_column presence 编码与 proto optional 字段匹配;旧版 Optional 返回核实为永非 None,签名收紧无行为变化;UploadColumn typings 同步新增 xAxis/hidden;auto column 分组计算去重至 helper.calculate_section_name
  • 配置、认证、API 与 CLI: 无凭据/网络变更;consumer 移除 section_rule 读取后该设置仅经 proto 传递给 core,行为一致
  • 媒体、集成与插件: is_scalar_value 迁入 builder,附带与 build_scalar_or_media dispatch registry 的一致性守卫测试
  • Protobuf、构建与依赖: ColumnRecord 新增字段 13/14(optional,未复用字段号,v1 向后兼容);Makefile .PHONY 修正
  • 安全、性能与跨平台: 无凭据接触;无 define 路径零 resolver 开销、预扫描有 has_custom_x 门槛;CI 三平台 × Python 3.9-3.14 全绿
  • 测试与验证: 分流 / 注入 / X 值去重 / merge/replace 语义均有 consumer 级与 resolver 级测试;伪造系统前缀 key 过滤等新契约已固化

未挂行发现

  • 说明: pyproject 作者邮箱更新来自分支携带的独立 chore(#1761),随本 PR 一并合入,无逻辑影响。
  • 说明: 未本地运行 make proto(依赖 protoc 工具链);已人工核对 proto 源与 Python pb2/pyi、Go pb.go 生成代码字段一致,Go lint CI 通过。

验证

  • 相关测试: uv run pytest tests/unit — 1758 passed, 21 skipped(PR head 3b6983da
  • uv run ruff check . — 通过
  • uv run basedpyright — 0 errors, 0 warnings, 0 notes
  • PR Actions 已通过: macOS / ubuntu / windows × Python 3.9-3.14 + Lint/Go 全部 pass

@SAKURA-CAT
SAKURA-CAT merged commit 6083145 into main Aug 29, 2026
26 checks passed
@SAKURA-CAT
SAKURA-CAT deleted the feat/define-metric branch August 29, 2026 12:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants