evescaffold 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.4
2
+ Name: evescaffold
3
+ Version: 0.1.0
4
+ Summary: EVE ML project scaffold generator (init/addmodel/list-models)
5
+ Author: EVE Scaffold
6
+ License: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+
10
+ ## evescaffold
11
+
12
+ 一个用于 EVE 机器学习项目的脚手架工具,提供三个命令:
13
+
14
+ - `evescaffold init`: 初始化项目目录骨架与必要示例文件
15
+ - `evescaffold addmodel <name>`: 初始化一个模型目录与初版代码
16
+ - `evescaffold list-models`: 列出内置模型模板
17
+
18
+ ### 安装(开发模式)
19
+
20
+ ```bash
21
+ pip install -e .
22
+ ```
23
+
24
+ ### 用法
25
+
26
+ ```bash
27
+ evescaffold init
28
+ evescaffold list-models
29
+ evescaffold addmodel xgboost
30
+ ```
31
+
32
+ 也可指定项目根目录:
33
+
34
+ ```bash
35
+ evescaffold init --path /path/to/project
36
+ evescaffold addmodel randomForest --path /path/to/project
37
+ ```
38
+
39
+ 默认不会覆盖已存在文件,如需强制覆盖使用 `--force`。
@@ -0,0 +1,30 @@
1
+ ## evescaffold
2
+
3
+ 一个用于 EVE 机器学习项目的脚手架工具,提供三个命令:
4
+
5
+ - `evescaffold init`: 初始化项目目录骨架与必要示例文件
6
+ - `evescaffold addmodel <name>`: 初始化一个模型目录与初版代码
7
+ - `evescaffold list-models`: 列出内置模型模板
8
+
9
+ ### 安装(开发模式)
10
+
11
+ ```bash
12
+ pip install -e .
13
+ ```
14
+
15
+ ### 用法
16
+
17
+ ```bash
18
+ evescaffold init
19
+ evescaffold list-models
20
+ evescaffold addmodel xgboost
21
+ ```
22
+
23
+ 也可指定项目根目录:
24
+
25
+ ```bash
26
+ evescaffold init --path /path/to/project
27
+ evescaffold addmodel randomForest --path /path/to/project
28
+ ```
29
+
30
+ 默认不会覆盖已存在文件,如需强制覆盖使用 `--force`。
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "evescaffold"
7
+ version = "0.1.0"
8
+ description = "EVE ML project scaffold generator (init/addmodel/list-models)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = {text = "MIT"}
12
+ authors = [{name = "EVE Scaffold"}]
13
+
14
+ [project.scripts]
15
+ evescaffold = "evescaffold.cli:main"
16
+
17
+ [tool.setuptools]
18
+ package-dir = {"" = "src"}
19
+
20
+ [tool.setuptools.packages.find]
21
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """evescaffold package."""
2
+
3
+ __all__ = ["__version__"]
4
+
5
+ __version__ = "0.1.0"
@@ -0,0 +1,9 @@
1
+ """Module entrypoint to support `python -m evescaffold ...`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .cli import main
6
+
7
+
8
+ if __name__ == "__main__":
9
+ main()
@@ -0,0 +1,80 @@
1
+ """Command line interface for evescaffold.
2
+
3
+ We intentionally use only the Python standard library here (argparse) to keep
4
+ the package lightweight and easy to install.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ from .scaffold import add_model, init_project, list_builtin_models
14
+
15
+
16
+ def _build_parser() -> argparse.ArgumentParser:
17
+ parser = argparse.ArgumentParser(
18
+ prog="evescaffold",
19
+ description="EVE ML 项目脚手架:init / addmodel / list-models",
20
+ )
21
+ sub = parser.add_subparsers(dest="command", required=True)
22
+
23
+ p_init = sub.add_parser("init", help="初始化项目目录骨架")
24
+ p_init.add_argument(
25
+ "--path",
26
+ "-p",
27
+ type=Path,
28
+ default=None,
29
+ help="项目根目录(默认当前目录)",
30
+ )
31
+ p_init.add_argument(
32
+ "--force",
33
+ action="store_true",
34
+ help="覆盖已存在文件",
35
+ )
36
+
37
+ p_add = sub.add_parser("addmodel", help="初始化一个模型模板目录")
38
+ p_add.add_argument("name", help="模型模板名,如 xgboost / randomForest / embeddingMLP")
39
+ p_add.add_argument(
40
+ "--path",
41
+ "-p",
42
+ type=Path,
43
+ default=None,
44
+ help="项目根目录(默认当前目录)",
45
+ )
46
+ p_add.add_argument(
47
+ "--force",
48
+ action="store_true",
49
+ help="覆盖已存在文件",
50
+ )
51
+
52
+ sub.add_parser("list-models", help="列出内置模型模板")
53
+
54
+ return parser
55
+
56
+
57
+ def main(argv: list[str] | None = None) -> None:
58
+ parser = _build_parser()
59
+ args = parser.parse_args(argv)
60
+
61
+ base_dir = Path.cwd() if getattr(args, "path", None) is None else args.path
62
+
63
+ if args.command == "init":
64
+ init_project(base_dir=base_dir, force=bool(args.force))
65
+ print(f"Initialized scaffold at: {base_dir}")
66
+ return
67
+
68
+ if args.command == "addmodel":
69
+ model_dir = add_model(base_dir=base_dir, name=str(args.name), force=bool(args.force))
70
+ print(f"Model scaffold created at: {model_dir}")
71
+ return
72
+
73
+ if args.command == "list-models":
74
+ for item in list_builtin_models():
75
+ print(f"- {item['name']}: {item['description']}")
76
+ return
77
+
78
+ # Should not happen due to argparse validation.
79
+ parser.print_help(sys.stderr)
80
+ raise SystemExit(2)
@@ -0,0 +1,595 @@
1
+ """Project scaffolding logic.
2
+
3
+ This module implements:
4
+ - `init_project`: create the baseline project structure
5
+ - `add_model`: create model template code under `models/<ModelName>/`
6
+ - `list_builtin_models`: list built-in model templates
7
+
8
+ Design goals:
9
+ - Safe by default (do not overwrite existing files unless `force=True`)
10
+ - Minimal dependencies (stdlib only)
11
+ - Predictable, cross-platform directory names (ASCII)
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from dataclasses import dataclass
17
+ from pathlib import Path
18
+ from typing import Dict, List, Optional
19
+
20
+
21
+ # -------------------------
22
+ # Small filesystem helpers
23
+ # -------------------------
24
+
25
+
26
+ def _mkdir(path: Path) -> None:
27
+ path.mkdir(parents=True, exist_ok=True)
28
+
29
+
30
+ def _write_text(path: Path, content: str, force: bool) -> bool:
31
+ """Write `content` to `path`.
32
+
33
+ Returns True if a write happened; False if skipped due to existing file.
34
+ """
35
+ if path.exists() and not force:
36
+ return False
37
+ path.parent.mkdir(parents=True, exist_ok=True)
38
+ path.write_text(content, encoding="utf-8")
39
+ return True
40
+
41
+
42
+ # -------------------------
43
+ # Model templates
44
+ # -------------------------
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class BuiltinModel:
49
+ name: str
50
+ description: str
51
+ files: Dict[str, str] # relative paths under models/<name>/
52
+
53
+
54
+ def _normalize_model_name(raw: str) -> str:
55
+ """Normalize user input into canonical built-in model directory name.
56
+
57
+ We accept a few common aliases to improve UX.
58
+ """
59
+ s = raw.strip()
60
+ low = s.lower().replace("-", "").replace("_", "")
61
+
62
+ if low in {"embeddingmlp", "mlp", "embedding"}:
63
+ return "embeddingMLP"
64
+ if low in {"xgboost", "xgb", "xgbboost", "xgbbooster", "xgbtree"}:
65
+ return "xgBoost"
66
+ if low in {"randomforest", "rf"}:
67
+ return "randomForest"
68
+
69
+ # For unknown models, we keep the input as-is (directory name).
70
+ return s
71
+
72
+
73
+ def list_builtin_models() -> List[dict]:
74
+ """Return built-in model list for CLI display."""
75
+ return [{"name": m.name, "description": m.description} for m in _BUILTIN_MODELS.values()]
76
+
77
+
78
+ def init_project(base_dir: Path, force: bool) -> None:
79
+ """Initialize the project scaffold in `base_dir`."""
80
+ _mkdir(base_dir)
81
+
82
+ # 公共工具层 (Global Utilities)
83
+ _mkdir(base_dir / "global_utils" / "featureUtils")
84
+ _mkdir(base_dir / "global_utils" / "modelUtils")
85
+
86
+ _write_text(
87
+ base_dir / "global_utils" / "featureUtils" / "featureTransform.py",
88
+ _T_FEATURE_TRANSFORM,
89
+ force,
90
+ )
91
+ _write_text(
92
+ base_dir / "global_utils" / "featureUtils" / "featureStats.py",
93
+ _T_FEATURE_STATS,
94
+ force,
95
+ )
96
+ _write_text(
97
+ base_dir / "global_utils" / "modelUtils" / "modelConverter.py",
98
+ _T_MODEL_CONVERTER,
99
+ force,
100
+ )
101
+ _write_text(
102
+ base_dir / "global_utils" / "modelUtils" / "modelTrainer.py",
103
+ _T_MODEL_TRAINER,
104
+ force,
105
+ )
106
+
107
+ # 数据层
108
+ _mkdir(base_dir / "data" / "raw")
109
+ _mkdir(base_dir / "data" / "features")
110
+ _write_text(base_dir / "data" / "raw" / "README.md", _T_DATA_RAW_README, force)
111
+ _write_text(base_dir / "data" / "features" / "README.md", _T_DATA_FEATURES_README, force)
112
+
113
+ # 特征工程层
114
+ _mkdir(base_dir / "feature_engineering" / "feature_generation")
115
+ _mkdir(base_dir / "feature_engineering" / "feature_config")
116
+ _mkdir(base_dir / "feature_engineering" / "transform_scripts")
117
+ _write_text(
118
+ base_dir / "feature_engineering" / "feature_generation" / "README.md",
119
+ _T_FE_GEN_README,
120
+ force,
121
+ )
122
+ _write_text(
123
+ base_dir / "feature_engineering" / "feature_config" / "README.md",
124
+ _T_FE_CFG_README,
125
+ force,
126
+ )
127
+ _write_text(
128
+ base_dir / "feature_engineering" / "transform_scripts" / "README.md",
129
+ _T_FE_TRANSFORM_README,
130
+ force,
131
+ )
132
+
133
+ # 模型层
134
+ _mkdir(base_dir / "models")
135
+
136
+ # Create all three built-in models by default to match the requested structure.
137
+ add_model(base_dir=base_dir, name="embeddingMLP", force=force)
138
+ add_model(base_dir=base_dir, name="xgBoost", force=force)
139
+ add_model(base_dir=base_dir, name="randomForest", force=force)
140
+
141
+ # pipeline.py
142
+ _write_text(base_dir / "pipeline.py", _T_PIPELINE, force)
143
+
144
+
145
+ def add_model(base_dir: Path, name: str, force: bool) -> Path:
146
+ """Create model scaffold under `base_dir/models/<name>/`."""
147
+ model_name = _normalize_model_name(name)
148
+ model_dir = base_dir / "models" / model_name
149
+
150
+ _mkdir(model_dir)
151
+ _mkdir(model_dir / "save")
152
+ _mkdir(model_dir / "save" / "save1")
153
+ _write_text(model_dir / "save" / "README.md", _T_SAVE_README, force)
154
+
155
+ builtin = _BUILTIN_MODELS.get(model_name)
156
+ if builtin is None:
157
+ # Unknown model: generate a generic minimal template.
158
+ _write_text(model_dir / "model.py", _T_GENERIC_MODEL_PY.format(model_name=model_name), force)
159
+ _write_text(model_dir / "config.py", _t_generic_config_py(model_name), force)
160
+ _write_text(model_dir / "sampleDataUpload.py", _T_SAMPLE_UPLOAD_PY, force)
161
+ return model_dir
162
+
163
+ for rel_path, content in builtin.files.items():
164
+ _write_text(model_dir / rel_path, content, force)
165
+
166
+ return model_dir
167
+
168
+
169
+ def _t_generic_config_py(model_name: str) -> str:
170
+ safe = "".join(ch if ch.isalnum() else "_" for ch in model_name)
171
+ class_name = f"{safe[:1].upper()}{safe[1:]}Config"
172
+ return (
173
+ "from dataclasses import dataclass\n\n\n"
174
+ f"@dataclass\nclass {class_name}:\n"
175
+ " # TODO: fill config fields\n"
176
+ " pass\n"
177
+ )
178
+
179
+
180
+ # -------------------------
181
+ # File templates (utilities / readmes / pipeline)
182
+ # -------------------------
183
+
184
+
185
+ _T_FEATURE_TRANSFORM = """\
186
+ \"\"\"特征转换工具(占位)。
187
+
188
+ 建议职责:
189
+ - 纯函数/可复用转换(不直接依赖业务 I/O)
190
+ - 输入输出协议稳定(DataFrame/ndarray/自定义样本结构)
191
+ \"\"\"
192
+
193
+ from __future__ import annotations
194
+
195
+
196
+ def example_transform(x: float) -> float:
197
+ \"\"\"示例:对数变换(请按业务替换)。\"\"\"
198
+ if x <= 0:
199
+ return 0.0
200
+ # 简化示例:避免额外依赖 numpy
201
+ import math
202
+
203
+ return math.log(x)
204
+ """
205
+
206
+
207
+ _T_FEATURE_STATS = """\
208
+ \"\"\"特征统计工具(占位)。\"\"\"
209
+
210
+ from __future__ import annotations
211
+
212
+ from dataclasses import dataclass
213
+
214
+
215
+ @dataclass
216
+ class Stats:
217
+ count: int
218
+ mean: float
219
+
220
+
221
+ def mean(values: list[float]) -> Stats:
222
+ if not values:
223
+ return Stats(count=0, mean=0.0)
224
+ return Stats(count=len(values), mean=sum(values) / len(values))
225
+ """
226
+
227
+
228
+ _T_MODEL_CONVERTER = """\
229
+ \"\"\"模型转换工具(占位)。
230
+
231
+ 建议职责:
232
+ - 统一保存/加载协议(如:pickle / joblib / ONNX)
233
+ - 版本标记、兼容性校验、元信息记录
234
+ \"\"\"
235
+
236
+ from __future__ import annotations
237
+
238
+ from pathlib import Path
239
+
240
+
241
+ def save_bytes(path: Path, payload: bytes) -> None:
242
+ path.parent.mkdir(parents=True, exist_ok=True)
243
+ path.write_bytes(payload)
244
+
245
+
246
+ def load_bytes(path: Path) -> bytes:
247
+ return path.read_bytes()
248
+ """
249
+
250
+
251
+ _T_MODEL_TRAINER = """\
252
+ \"\"\"通用训练工具(占位)。
253
+
254
+ 建议职责:
255
+ - 训练流程编排(数据读取 -> 特征 -> 训练 -> 评估 -> 保存)
256
+ - 统一日志/指标落盘协议
257
+ \"\"\"
258
+
259
+ from __future__ import annotations
260
+
261
+ from pathlib import Path
262
+ from typing import Any, Callable
263
+
264
+
265
+ def run_training(train_fn: Callable[[Path], Any], save_dir: Path) -> Any:
266
+ \"\"\"统一的训练执行入口(示例)。\"\"\"
267
+ save_dir.mkdir(parents=True, exist_ok=True)
268
+ return train_fn(save_dir)
269
+ """
270
+
271
+
272
+ _T_DATA_RAW_README = """\
273
+ 这里放原始数据(raw)。
274
+ 建议:不要把大文件提交到 git,使用 DVC/对象存储/数据平台。
275
+ """
276
+
277
+
278
+ _T_DATA_FEATURES_README = """\
279
+ 这里放特征数据(features)。
280
+ 建议:明确特征生成版本与数据 schema。
281
+ """
282
+
283
+
284
+ _T_FE_GEN_README = """\
285
+ 特征生成层:建议放生成逻辑(例如按天批处理/增量生成)。
286
+ """
287
+
288
+
289
+ _T_FE_CFG_README = """\
290
+ 特征配置层:建议放特征开关、schema、特征列表、分桶配置等。
291
+ """
292
+
293
+
294
+ _T_FE_TRANSFORM_README = """\
295
+ 转换脚本:建议放一次性迁移脚本/数据修复脚本/特征回填脚本等。
296
+ """
297
+
298
+
299
+ _T_SAVE_README = """\
300
+ 这里存放多次训练的结果与图表。
301
+ 建议结构:
302
+ - save1/ save2/ ... saveN/
303
+ 每次训练固定落盘:metrics.json、模型文件、可视化图表等。
304
+ """
305
+
306
+
307
+ _T_SAMPLE_UPLOAD_PY = """\
308
+ \"\"\"样本收集/上传占位脚本(通用)。
309
+
310
+ 按你的业务实际改造:
311
+ - 从 data/raw 或线上拉取数据
312
+ - 做脱敏/采样
313
+ - 写入 data/features 或上传到对象存储
314
+ \"\"\"
315
+
316
+ from __future__ import annotations
317
+
318
+ from pathlib import Path
319
+
320
+
321
+ def main() -> None:
322
+ project_root = Path(__file__).resolve().parents[2]
323
+ raw_dir = project_root / "data" / "raw"
324
+ print(f"[sampleDataUpload] raw data dir: {raw_dir}")
325
+ print("TODO: implement your sample upload logic")
326
+
327
+
328
+ if __name__ == "__main__":
329
+ main()
330
+ """
331
+
332
+
333
+ _T_PIPELINE = """\
334
+ \"\"\"项目 pipeline 入口(占位)。
335
+
336
+ 建议在这里做:
337
+ - 数据/特征/训练/评估的编排
338
+ - 统一参数解析与运行模式(本地/离线/线上)
339
+ \"\"\"
340
+
341
+ from __future__ import annotations
342
+
343
+ from pathlib import Path
344
+
345
+
346
+ def main() -> None:
347
+ root = Path(__file__).resolve().parent
348
+ print(f"Project root: {root}")
349
+ print("TODO: implement pipeline orchestration")
350
+
351
+
352
+ if __name__ == "__main__":
353
+ main()
354
+ """
355
+
356
+
357
+ _T_GENERIC_MODEL_PY = """\
358
+ \"\"\"{model_name} 模型占位模板(通用)。
359
+
360
+ 此文件为脚手架生成的最小可运行骨架:
361
+ - 不引入任何第三方依赖
362
+ - 通过写 metrics.json 的方式示范训练输出
363
+
364
+ 后续你可以在这里接入 sklearn / xgboost / torch 等。
365
+ \"\"\"
366
+
367
+ from __future__ import annotations
368
+
369
+ from dataclasses import dataclass
370
+ import json
371
+ from pathlib import Path
372
+
373
+
374
+ @dataclass
375
+ class Config:
376
+ # TODO: fill model hyperparameters
377
+ pass
378
+
379
+
380
+ def train(cfg: Config, save_dir: Path) -> dict:
381
+ save_dir.mkdir(parents=True, exist_ok=True)
382
+ metrics = {{"status": "not_implemented", "model": "{model_name}"}}
383
+ (save_dir / "metrics.json").write_text(json.dumps(metrics, indent=2), encoding="utf-8")
384
+ return metrics
385
+
386
+
387
+ def main() -> None:
388
+ here = Path(__file__).resolve().parent
389
+ out = here / "save" / "save1"
390
+ metrics = train(Config(), out)
391
+ print(f"[{model_name}] placeholder done, metrics saved to: {{out}}")
392
+ print(metrics)
393
+
394
+
395
+ if __name__ == "__main__":
396
+ main()
397
+ """
398
+
399
+
400
+ # -------------------------
401
+ # Built-in model templates
402
+ # -------------------------
403
+
404
+
405
+ _BUILTIN_MODELS: Dict[str, BuiltinModel] = {
406
+ "embeddingMLP": BuiltinModel(
407
+ name="embeddingMLP",
408
+ description="最小可运行的 MLP 示例(占位版本,方便后续替换成真实 embedding/深度模型)",
409
+ files={
410
+ "model.py": """\
411
+ \"\"\"embeddingMLP 模型训练入口(最小可运行示例)。
412
+
413
+ 目标:
414
+ - 给脚手架一个“能跑通”的示例,避免生成后用户无从下手
415
+ - 真实项目中你可以将此处替换为 PyTorch/TF/自研训练流程
416
+ \"\"\"
417
+
418
+ from __future__ import annotations
419
+
420
+ from dataclasses import dataclass
421
+ import json
422
+ from pathlib import Path
423
+ import random
424
+
425
+
426
+ @dataclass
427
+ class Config:
428
+ # 这里是占位配置;你可以扩展为从 yaml/cli 读取
429
+ seed: int = 42
430
+ epochs: int = 3
431
+ lr: float = 1e-3
432
+
433
+
434
+ def train(cfg: Config, save_dir: Path) -> dict:
435
+ \"\"\"占位训练函数:模拟 loss 下降并写出 metrics.json。\"\"\"
436
+ random.seed(cfg.seed)
437
+ save_dir.mkdir(parents=True, exist_ok=True)
438
+
439
+ loss = 1.0
440
+ history = []
441
+ for _ in range(cfg.epochs):
442
+ loss *= (0.7 + random.random() * 0.1)
443
+ history.append(loss)
444
+
445
+ metrics = {"final_loss": loss, "history": history, "epochs": cfg.epochs, "lr": cfg.lr}
446
+ (save_dir / "metrics.json").write_text(json.dumps(metrics, indent=2), encoding="utf-8")
447
+ return metrics
448
+
449
+
450
+ def main() -> None:
451
+ cfg = Config()
452
+ here = Path(__file__).resolve().parent
453
+ out = here / "save" / "save1"
454
+ metrics = train(cfg, out)
455
+ print(f"[embeddingMLP] done, metrics saved to: {out}")
456
+ print(metrics)
457
+
458
+
459
+ if __name__ == "__main__":
460
+ main()
461
+ """,
462
+ "config.py": """\
463
+ \"\"\"embeddingMLP 配置(可按需替换为 pydantic/yaml/hydra 等)\"\"\"
464
+
465
+ from dataclasses import dataclass
466
+
467
+
468
+ @dataclass
469
+ class EmbeddingMLPConfig:
470
+ seed: int = 42
471
+ epochs: int = 3
472
+ lr: float = 1e-3
473
+ """,
474
+ "sampleDataUpload.py": _T_SAMPLE_UPLOAD_PY,
475
+ },
476
+ ),
477
+ "xgBoost": BuiltinModel(
478
+ name="xgBoost",
479
+ description="XGBoost 占位模板(只提供接口骨架,不强依赖 xgboost 包)",
480
+ files={
481
+ "model.py": """\
482
+ \"\"\"xgBoost 模型占位模板。
483
+
484
+ 说明:
485
+ - 为了让脚手架保持轻量,这里不强制依赖 xgboost
486
+ - 你可以在此处接入 xgboost.XGBClassifier / XGBRegressor
487
+ \"\"\"
488
+
489
+ from __future__ import annotations
490
+
491
+ from dataclasses import dataclass
492
+ import json
493
+ from pathlib import Path
494
+
495
+
496
+ @dataclass
497
+ class Config:
498
+ # 占位参数:按需扩展
499
+ n_estimators: int = 200
500
+ max_depth: int = 6
501
+ learning_rate: float = 0.1
502
+
503
+
504
+ def train(cfg: Config, save_dir: Path) -> dict:
505
+ save_dir.mkdir(parents=True, exist_ok=True)
506
+ metrics = {"status": "not_implemented", "cfg": cfg.__dict__}
507
+ (save_dir / "metrics.json").write_text(json.dumps(metrics, indent=2), encoding="utf-8")
508
+ return metrics
509
+
510
+
511
+ def main() -> None:
512
+ here = Path(__file__).resolve().parent
513
+ out = here / "save" / "save1"
514
+ metrics = train(Config(), out)
515
+ print(f"[xgBoost] placeholder done, metrics saved to: {out}")
516
+ print(metrics)
517
+
518
+
519
+ if __name__ == "__main__":
520
+ main()
521
+ """,
522
+ "config.py": """\
523
+ from dataclasses import dataclass
524
+
525
+
526
+ @dataclass
527
+ class XGBoostConfig:
528
+ n_estimators: int = 200
529
+ max_depth: int = 6
530
+ learning_rate: float = 0.1
531
+ """,
532
+ "sampleDataUpload.py": _T_SAMPLE_UPLOAD_PY,
533
+ },
534
+ ),
535
+ "randomForest": BuiltinModel(
536
+ name="randomForest",
537
+ description="RandomForest 占位模板(可对接 sklearn)",
538
+ files={
539
+ "model.py": """\
540
+ \"\"\"randomForest 模型占位模板。
541
+
542
+ 说明:
543
+ - 你可以在此处接入 sklearn.ensemble.RandomForestClassifier/Regressor
544
+ - 脚手架默认不引入 sklearn 依赖,避免安装过重;按需在项目侧添加
545
+ \"\"\"
546
+
547
+ from __future__ import annotations
548
+
549
+ from dataclasses import dataclass
550
+ import json
551
+ from pathlib import Path
552
+ from typing import Optional
553
+
554
+
555
+ @dataclass
556
+ class Config:
557
+ n_estimators: int = 300
558
+ max_depth: Optional[int] = None
559
+ random_state: int = 42
560
+
561
+
562
+ def train(cfg: Config, save_dir: Path) -> dict:
563
+ save_dir.mkdir(parents=True, exist_ok=True)
564
+ metrics = {"status": "not_implemented", "cfg": cfg.__dict__}
565
+ (save_dir / "metrics.json").write_text(json.dumps(metrics, indent=2), encoding="utf-8")
566
+ return metrics
567
+
568
+
569
+ def main() -> None:
570
+ here = Path(__file__).resolve().parent
571
+ out = here / "save" / "save1"
572
+ metrics = train(Config(), out)
573
+ print(f"[randomForest] placeholder done, metrics saved to: {out}")
574
+ print(metrics)
575
+
576
+
577
+ if __name__ == "__main__":
578
+ main()
579
+ """,
580
+ "config.py": """\
581
+ from dataclasses import dataclass
582
+ from typing import Optional
583
+
584
+
585
+ @dataclass
586
+ class RandomForestConfig:
587
+ n_estimators: int = 300
588
+ max_depth: Optional[int] = None
589
+ random_state: int = 42
590
+ """,
591
+ "sampleDataUpload.py": _T_SAMPLE_UPLOAD_PY,
592
+ },
593
+ ),
594
+ }
595
+
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.4
2
+ Name: evescaffold
3
+ Version: 0.1.0
4
+ Summary: EVE ML project scaffold generator (init/addmodel/list-models)
5
+ Author: EVE Scaffold
6
+ License: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+
10
+ ## evescaffold
11
+
12
+ 一个用于 EVE 机器学习项目的脚手架工具,提供三个命令:
13
+
14
+ - `evescaffold init`: 初始化项目目录骨架与必要示例文件
15
+ - `evescaffold addmodel <name>`: 初始化一个模型目录与初版代码
16
+ - `evescaffold list-models`: 列出内置模型模板
17
+
18
+ ### 安装(开发模式)
19
+
20
+ ```bash
21
+ pip install -e .
22
+ ```
23
+
24
+ ### 用法
25
+
26
+ ```bash
27
+ evescaffold init
28
+ evescaffold list-models
29
+ evescaffold addmodel xgboost
30
+ ```
31
+
32
+ 也可指定项目根目录:
33
+
34
+ ```bash
35
+ evescaffold init --path /path/to/project
36
+ evescaffold addmodel randomForest --path /path/to/project
37
+ ```
38
+
39
+ 默认不会覆盖已存在文件,如需强制覆盖使用 `--force`。
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/evescaffold/__init__.py
4
+ src/evescaffold/__main__.py
5
+ src/evescaffold/cli.py
6
+ src/evescaffold/scaffold.py
7
+ src/evescaffold.egg-info/PKG-INFO
8
+ src/evescaffold.egg-info/SOURCES.txt
9
+ src/evescaffold.egg-info/dependency_links.txt
10
+ src/evescaffold.egg-info/entry_points.txt
11
+ src/evescaffold.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ evescaffold = evescaffold.cli:main
@@ -0,0 +1 @@
1
+ evescaffold