From 748bf41c53008bc790e71473efcdacb190357bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=97=E5=B9=B3?= Date: Mon, 29 Jun 2026 23:21:47 +0800 Subject: [PATCH] feat(m1): scaffold tsmm package (T1.1) - pyproject.toml (src-layout, deps per plan) - src/tsmm/{data,model,train,eval} skeletons - tests/test_smoke.py (RED->GREEN: importable + subpackages) - configs/, scripts/, data/, checkpoints/ + .gitignore - project venv (.venv) for reproducible env (M1 deps only; CUDA torch @ M2) Verifications: import tsmm OK; pytest tests/ 3 passed. --- .gitignore | 10 +++++++ README.md | 33 ++++++++++++++++++++++ checkpoints/.gitignore | 3 ++ configs/README.md | 3 ++ data/.gitignore | 3 ++ openspec/changes/ts-as-modality/tasks.md | 2 +- pyproject.toml | 35 ++++++++++++++++++++++++ scripts/README.md | 3 ++ src/tsmm/__init__.py | 7 +++++ src/tsmm/data/__init__.py | 1 + src/tsmm/eval/__init__.py | 1 + src/tsmm/model/__init__.py | 1 + src/tsmm/train/__init__.py | 1 + tests/__init__.py | 0 tests/test_smoke.py | 25 +++++++++++++++++ 15 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 README.md create mode 100644 checkpoints/.gitignore create mode 100644 configs/README.md create mode 100644 data/.gitignore create mode 100644 pyproject.toml create mode 100644 scripts/README.md create mode 100644 src/tsmm/__init__.py create mode 100644 src/tsmm/data/__init__.py create mode 100644 src/tsmm/eval/__init__.py create mode 100644 src/tsmm/model/__init__.py create mode 100644 src/tsmm/train/__init__.py create mode 100644 tests/__init__.py create mode 100644 tests/test_smoke.py diff --git a/.gitignore b/.gitignore index 63268e9..edd2d9b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,16 @@ node_modules/ jspm_packages/ +# Python +.venv/ +venv/ +env/ +__pycache__/ +*.py[cod] +*.egg-info/ +.pytest_cache/ +.ipynb_checkpoints/ + # ───────────────────────────────────────────────────────────── # Build output & caches # ───────────────────────────────────────────────────────────── diff --git a/README.md b/README.md new file mode 100644 index 0000000..c1e80a8 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# tsmm + +**Time-series as a Modality** — an experimental, single-GPU multimodal model that +treats multivariate time series as an independent modality, jointly injected into a +small LLM (Qwen2.5-0.5B) for time-series question answering and reasoning +(ChatTS route). + +## Status + +M1 (data pipeline) under construction. See +`docs/superpowers/specs/2026-06-29-ts-as-modality-design.md` for the design and +`docs/superpowers/plans/2026-06-29-ts-as-modality-plan.md` for the task plan. + +## Layout + +``` +src/tsmm/ package (data, model, train, eval) +configs/ training / data configs +scripts/ offline generation & training entrypoints +tests/ pytest suite +data/ generated datasets (gitignored) +checkpoints/ model checkpoints (gitignored) +``` + +## Install (dev) + +```bash +python3 -m venv .venv +. .venv/bin/activate +pip install -e . # full deps (incl. torch/transformers/peft/...) +# M1-only lightweight install: +pip install numpy tqdm pyyaml pytest && pip install -e . --no-deps +``` diff --git a/checkpoints/.gitignore b/checkpoints/.gitignore new file mode 100644 index 0000000..debd748 --- /dev/null +++ b/checkpoints/.gitignore @@ -0,0 +1,3 @@ +# Model checkpoints — never commit. +* +!.gitignore diff --git a/configs/README.md b/configs/README.md new file mode 100644 index 0000000..3205d99 --- /dev/null +++ b/configs/README.md @@ -0,0 +1,3 @@ +# Configs directory + +YAML / Python configs for data generation and training live here. diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..bb83077 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,3 @@ +# Generated datasets — never commit. +* +!.gitignore diff --git a/openspec/changes/ts-as-modality/tasks.md b/openspec/changes/ts-as-modality/tasks.md index 975a111..17706e6 100644 --- a/openspec/changes/ts-as-modality/tasks.md +++ b/openspec/changes/ts-as-modality/tasks.md @@ -5,7 +5,7 @@ ## 1. M1 · 数据管线 -- [ ] 1.1 项目脚手架:创建 `pyproject.toml`(torch/transformers/peft/accelerate/datasets/numpy/tqdm/pyyaml/pytest)、`src/tsmm/` 模块骨架、`configs/`、`scripts/`、`tests/`,并建 `data/`、`checkpoints/` 加 `.gitignore`(验证:`python -c "import tsmm"` 不报错;`pytest tests/` 可跑) +- [x] 1.1 项目脚手架:创建 `pyproject.toml`(torch/transformers/peft/accelerate/datasets/numpy/tqdm/pyyaml/pytest)、`src/tsmm/` 模块骨架、`configs/`、`scripts/`、`tests/`,并建 `data/`、`checkpoints/` 加 `.gitignore`(验证:`python -c "import tsmm"` 不报错;`pytest tests/` 可跑) - [ ] 1.2 属性词表与采样 `data/attributes.py`:定义变量名词表/单位/采样率档位,`sample_attributes(n_channels)` 返回结构化属性(验证:单测字段齐全、可复现) - [ ] 1.3 成分模型与时序合成 `data/synthesis.py`:`generate_series`(趋势+周期+基线+噪声)、`inject_anomaly`(尖刺/水平偏移/方差膨胀/缺失段/缓慢漂移,返回逐点 labels+段元数据)、`couple_event`(t 处阶跃+事件文本)、`add_missing`(NaN 占位)(验证:给定 seed 确定;异常段与 labels 一致;事件处有阶跃) - [ ] 1.4 指令与回答生成 `data/instruct.py`:6 类指令模板(描述/异常/根因/预测/比较/事件关联)+ Evol-Instruct 演化,`build_instruction` 产 JSON 区间/数值/文本回答(验证:异常类 JSON 可 `json.loads` 且段与 labels 吻合) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..57733cd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,35 @@ +[build-system] +requires = ["setuptools>=68", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "tsmm" +version = "0.0.1" +description = "Time-series as a modality: multimodal TS-question-answering model (experimental, single-GPU)" +readme = "README.md" +requires-python = ">=3.10" +license = { text = "MIT" } +authors = [{ name = "tsmm contributors" }] +dependencies = [ + "torch", + "transformers", + "peft", + "accelerate", + "datasets", + "numpy", + "tqdm", + "pyyaml", + "pytest", +] + +[project.optional-dependencies] +# Heavy GPU deps installed on-demand at M2 (CUDA torch build, etc.) +dev = ["pytest", "pytest-cov"] + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["src"] +addopts = "-q" diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..51b5732 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,3 @@ +# Scripts directory + +Offline entrypoints: `gen_synthetic.py`, `train_stage1.sh`, `train_stage2.sh`, `run_eval.sh`. diff --git a/src/tsmm/__init__.py b/src/tsmm/__init__.py new file mode 100644 index 0000000..dc736dd --- /dev/null +++ b/src/tsmm/__init__.py @@ -0,0 +1,7 @@ +"""tsmm — Time-series as a Modality (multimodal). + +Experimental, single-GPU implementation following the ChatTS "TS as a new +modality" route. See `docs/superpowers/specs/2026-06-29-ts-as-modality-design.md`. +""" + +__version__ = "0.0.1" diff --git a/src/tsmm/data/__init__.py b/src/tsmm/data/__init__.py new file mode 100644 index 0000000..09e79d4 --- /dev/null +++ b/src/tsmm/data/__init__.py @@ -0,0 +1 @@ +"""Data pipeline: attribute vocab, synthesis, instructions, collator, real benchmarks.""" diff --git a/src/tsmm/eval/__init__.py b/src/tsmm/eval/__init__.py new file mode 100644 index 0000000..643b091 --- /dev/null +++ b/src/tsmm/eval/__init__.py @@ -0,0 +1 @@ +"""Evaluation: answer parsing, TS anomaly metrics, QA judging, baselines.""" diff --git a/src/tsmm/model/__init__.py b/src/tsmm/model/__init__.py new file mode 100644 index 0000000..e4641ae --- /dev/null +++ b/src/tsmm/model/__init__.py @@ -0,0 +1 @@ +"""Model components: TS encoder, projector, multimodal splice, training/inference wrapper.""" diff --git a/src/tsmm/train/__init__.py b/src/tsmm/train/__init__.py new file mode 100644 index 0000000..02714d0 --- /dev/null +++ b/src/tsmm/train/__init__.py @@ -0,0 +1 @@ +"""Training: stage-1 alignment, stage-2 SFT, losses.""" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_smoke.py b/tests/test_smoke.py new file mode 100644 index 0000000..394dd5c --- /dev/null +++ b/tests/test_smoke.py @@ -0,0 +1,25 @@ +"""Smoke tests for the package boundary (T1.1). + +These guard the most basic contract: the package is importable and exposes +a version. If these fail, the install / src-layout is broken. +""" + +import tsmm + + +def test_package_is_importable(): + assert tsmm is not None + + +def test_package_exposes_version(): + assert isinstance(tsmm.__version__, str) + assert tsmm.__version__ # non-empty + + +def test_subpackages_importable(): + # M1 will populate these; the skeleton must already expose the namespaces. + import importlib + + for sub in ("data", "model", "train", "eval"): + mod = importlib.import_module(f"tsmm.{sub}") + assert mod is not None