mas-lab-component-core 0.2.0__py3-none-any.whl

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.
Files changed (139) hide show
  1. mas/lab/artifacts.py +282 -0
  2. mas/lab/cli/__init__.py +115 -0
  3. mas/lab/cli/commands/__init__.py +3 -0
  4. mas/lab/cli/commands/benchmark/__init__.py +73 -0
  5. mas/lab/cli/commands/benchmark/analyze.py +25 -0
  6. mas/lab/cli/commands/benchmark/artifacts.py +57 -0
  7. mas/lab/cli/commands/benchmark/clean.py +56 -0
  8. mas/lab/cli/commands/benchmark/daemon.py +137 -0
  9. mas/lab/cli/commands/benchmark/export_cmd.py +49 -0
  10. mas/lab/cli/commands/benchmark/follow.py +121 -0
  11. mas/lab/cli/commands/benchmark/import_cmd.py +52 -0
  12. mas/lab/cli/commands/benchmark/import_hf.py +96 -0
  13. mas/lab/cli/commands/benchmark/list_cmd.py +29 -0
  14. mas/lab/cli/commands/benchmark/migrate.py +65 -0
  15. mas/lab/cli/commands/benchmark/pipeline.py +133 -0
  16. mas/lab/cli/commands/benchmark/publish_hf.py +88 -0
  17. mas/lab/cli/commands/benchmark/rename.py +44 -0
  18. mas/lab/cli/commands/benchmark/run.py +205 -0
  19. mas/lab/cli/commands/benchmark/show.py +101 -0
  20. mas/lab/cli/commands/benchmark/step.py +51 -0
  21. mas/lab/cli/commands/benchmark/step_info.py +116 -0
  22. mas/lab/cli/commands/benchmark/update.py +28 -0
  23. mas/lab/cli/commands/check.py +107 -0
  24. mas/lab/cli/commands/config.py +187 -0
  25. mas/lab/cli/commands/dataset.py +99 -0
  26. mas/lab/cli/commands/init.py +347 -0
  27. mas/lab/cli/commands/pipe.py +410 -0
  28. mas/lab/cli/commands/plot/__init__.py +51 -0
  29. mas/lab/cli/commands/plot/_common.py +38 -0
  30. mas/lab/cli/commands/plot/communication_flow.py +77 -0
  31. mas/lab/cli/commands/plot/context_provenance.py +136 -0
  32. mas/lab/cli/commands/plot/kg.py +97 -0
  33. mas/lab/cli/commands/plot/list_cmd.py +81 -0
  34. mas/lab/cli/commands/plot/message_graph.py +179 -0
  35. mas/lab/cli/commands/plot/multilevel_trajectory.py +90 -0
  36. mas/lab/cli/commands/plot/topology.py +58 -0
  37. mas/lab/cli/commands/plot/trajectory.py +93 -0
  38. mas/lab/cli/commands/run.py +473 -0
  39. mas/lab/cli/commands/serve.py +89 -0
  40. mas/lab/cli/commands/telemetry/__init__.py +30 -0
  41. mas/lab/cli/commands/telemetry/_clickhouse.py +123 -0
  42. mas/lab/cli/commands/telemetry/apps.py +132 -0
  43. mas/lab/cli/commands/telemetry/dump.py +232 -0
  44. mas/lab/cli/commands/telemetry/push.py +101 -0
  45. mas/lab/cli/commands/telemetry/sessions.py +92 -0
  46. mas/lab/cli/commands/telemetry/show.py +125 -0
  47. mas/lab/cli/commands/telemetry/traces.py +80 -0
  48. mas/lab/cli/commands/telemetry/verify.py +146 -0
  49. mas/lab/cli/commands/validate.py +138 -0
  50. mas/lab/cli/extensions.py +77 -0
  51. mas/lab/cli/manpage.py +729 -0
  52. mas/lab/cli/pipeline_shortcuts.py +205 -0
  53. mas/lab/cli.py +11 -0
  54. mas/lab/components/__init__.py +3 -0
  55. mas/lab/components/common/__init__.py +3 -0
  56. mas/lab/components/common/hooks.py +171 -0
  57. mas/lab/components/common/telemetry_feed.py +12 -0
  58. mas/lab/components/common/telemetry_schema.json +12 -0
  59. mas/lab/components/controller/backends.py +216 -0
  60. mas/lab/components/evaluation/__init__.py +28 -0
  61. mas/lab/components/evaluation/basic.py +35 -0
  62. mas/lab/components/evaluation/deepeval_wrapper.py +163 -0
  63. mas/lab/components/evaluation/interface.py +24 -0
  64. mas/lab/components/infrastructure/README.md +94 -0
  65. mas/lab/components/infrastructure/__init__.py +53 -0
  66. mas/lab/components/infrastructure/emulator.py +115 -0
  67. mas/lab/components/metrics/__init__.py +3 -0
  68. mas/lab/components/metrics/server.py +92 -0
  69. mas/lab/components/observability/otlp_http_local.py +56 -0
  70. mas/lab/components/package/__init__.py +116 -0
  71. mas/lab/components/ui/app.js +5210 -0
  72. mas/lab/components/ui/favicon.ico +1 -0
  73. mas/lab/components/ui/index.html +532 -0
  74. mas/lab/components/ui/js/plugin-system.js +179 -0
  75. mas/lab/components/ui/js/plugins/events.js +337 -0
  76. mas/lab/components/ui/js/plugins/hitl.js +123 -0
  77. mas/lab/components/ui/js/plugins/metrics.js +153 -0
  78. mas/lab/components/ui/js/plugins/observability.js +93 -0
  79. mas/lab/components/ui/js/plugins/plugins.css +133 -0
  80. mas/lab/components/ui/styles.css +2480 -0
  81. mas/lab/config_hygiene.py +197 -0
  82. mas/lab/connections.py +362 -0
  83. mas/lab/contracts/__init__.py +52 -0
  84. mas/lab/contracts/controller.py +115 -0
  85. mas/lab/contracts/executor.py +423 -0
  86. mas/lab/contracts/generator.py +216 -0
  87. mas/lab/contracts/loader.py +133 -0
  88. mas/lab/contracts/reporter.py +202 -0
  89. mas/lab/controllers/__init__.py +3 -0
  90. mas/lab/controllers/local_inproc.py +68 -0
  91. mas/lab/dataset/extractor.py +304 -0
  92. mas/lab/emulation_resolver.py +109 -0
  93. mas/lab/evaluation/__init__.py +20 -0
  94. mas/lab/evaluation/eval_contract.py +194 -0
  95. mas/lab/evaluation/events.py +70 -0
  96. mas/lab/evaluation/obs_event.py +4 -0
  97. mas/lab/graph/__init__.py +100 -0
  98. mas/lab/paths.py +395 -0
  99. mas/lab/pipe.py +462 -0
  100. mas/lab/processor.py +311 -0
  101. mas/lab/runners/__init__.py +35 -0
  102. mas/lab/runners/artifacts.py +84 -0
  103. mas/lab/runners/constants.py +12 -0
  104. mas/lab/runners/context.py +64 -0
  105. mas/lab/runners/factory.py +41 -0
  106. mas/lab/runners/infer.py +106 -0
  107. mas/lab/runners/invoke.py +16 -0
  108. mas/lab/runners/protocol.py +78 -0
  109. mas/lab/runners/registry.py +70 -0
  110. mas/lab/schemas/__init__.py +21 -0
  111. mas/lab/schemas/_schemas/artefacts/metrics.schema.json +114 -0
  112. mas/lab/schemas/_schemas/artefacts/run_info.schema.json +67 -0
  113. mas/lab/schemas/_schemas/dataset.schema.yaml +222 -0
  114. mas/lab/schemas/_schemas/experiment.schema.yaml +988 -0
  115. mas/lab/schemas/_schemas/fragments/memory-seed-entry.schema.yaml +26 -0
  116. mas/lab/schemas/_schemas/fragments/message.schema.yaml +16 -0
  117. mas/lab/schemas/_schemas/fragments/ref.schema.yaml +25 -0
  118. mas/lab/schemas/_schemas/fragments/tool-fixture-mapping.schema.yaml +46 -0
  119. mas/lab/schemas/_schemas/lab-config.schema.yaml +282 -0
  120. mas/lab/schemas/_schemas/lab.schema.yaml +65 -0
  121. mas/lab/schemas/_schemas/pipeline-manifest.schema.json +116 -0
  122. mas/lab/schemas/_schemas/pipeline.schema.yaml +230 -0
  123. mas/lab/schemas/_schemas/run-input.schema.yaml +155 -0
  124. mas/lab/schemas/paths.py +50 -0
  125. mas/lab/schemas/ui/agent-manifest.schema.json +286 -0
  126. mas/lab/schemas/ui/mas-manifest.schema.json +340 -0
  127. mas/lab/schemas/validate.py +89 -0
  128. mas/lab/schemas/workflow.py +63 -0
  129. mas/lab/telemetry/__init__.py +24 -0
  130. mas/lab/telemetry/otlp_push.py +10 -0
  131. mas/lab/templates/infra/llmprovider.yaml +19 -0
  132. mas/lab/tools/__init__.py +3 -0
  133. mas/lab/tools/experiment_designer_tools.py +301 -0
  134. mas/lab/utils/__init__.py +7 -0
  135. mas/lab/utils/output_formatter.py +191 -0
  136. mas_lab_component_core-0.2.0.dist-info/METADATA +12 -0
  137. mas_lab_component_core-0.2.0.dist-info/RECORD +139 -0
  138. mas_lab_component_core-0.2.0.dist-info/WHEEL +4 -0
  139. mas_lab_component_core-0.2.0.dist-info/entry_points.txt +5 -0
mas/lab/artifacts.py ADDED
@@ -0,0 +1,282 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Typed artifact value objects for the mas-lab processor/pipeline system.
4
+
5
+ Artifacts are the strongly-typed values that flow between :class:`Processor`
6
+ instances and pipeline steps. Every artifact has:
7
+
8
+ * a ``kind`` class-variable — the canonical type name used in YAML / CLI
9
+ * an optional ``path`` — when the artifact is disk-backed (file)
10
+ * an optional ``data`` — when the artifact is in-memory only
11
+ * a ``meta`` dict — provenance, run_id, timestamps, …
12
+
13
+ Hierarchy::
14
+
15
+ Artifact
16
+ ├── Trajectory ← raw JSONL event trace
17
+ │ └── AnnotatedTrajectory ← + highlights + analyst notes
18
+ ├── PlotFile ← rendered HTML / SVG / PNG
19
+ ├── NormalizedTrace ← normalized manifold trace (JSONL)
20
+ └── KnowledgeGraph ← JSON-LD graph file
21
+ """
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ from dataclasses import dataclass, field
26
+ from pathlib import Path
27
+ from typing import Any, ClassVar, Dict, List, Optional, Type
28
+
29
+
30
+ # ---------------------------------------------------------------------------
31
+ # Base
32
+ # ---------------------------------------------------------------------------
33
+
34
+ @dataclass
35
+ class Artifact:
36
+ """Base value object for all typed artifacts.
37
+
38
+ Parameters
39
+ ----------
40
+ path:
41
+ File path when the artifact lives on disk.
42
+ data:
43
+ In-memory payload (list, dict, str, …) when the artifact is not
44
+ backed by a file, or has already been loaded from one.
45
+ meta:
46
+ Arbitrary provenance metadata (run_id, timestamps, labels …).
47
+ """
48
+
49
+ kind: ClassVar[str] = "artifact"
50
+
51
+ path: Optional[Path] = None
52
+ data: Any = None
53
+ meta: Dict[str, Any] = field(default_factory=dict)
54
+
55
+ def is_file(self) -> bool:
56
+ """True iff ``path`` points to an existing file."""
57
+ return self.path is not None and Path(self.path).exists()
58
+
59
+ def as_dict(self) -> Dict[str, Any]:
60
+ return {
61
+ "kind": self.kind,
62
+ "path": str(self.path) if self.path else None,
63
+ "meta": self.meta,
64
+ }
65
+
66
+ def __repr__(self) -> str:
67
+ loc = f"path={self.path}" if self.path else "in-memory"
68
+ return f"<{self.__class__.__name__} {loc}>"
69
+
70
+
71
+ # ---------------------------------------------------------------------------
72
+ # Trajectory family
73
+ # ---------------------------------------------------------------------------
74
+
75
+ @dataclass
76
+ class Trajectory(Artifact):
77
+ """Raw MAS execution trace — a list of OTEL-style event dicts.
78
+
79
+ The ``events`` list is populated lazily: calling :meth:`load` reads the
80
+ JSONL file at ``path`` if ``events`` is empty.
81
+ """
82
+
83
+ kind: ClassVar[str] = "trajectory"
84
+
85
+ events: List[Dict[str, Any]] = field(default_factory=list)
86
+ run_id: str = ""
87
+
88
+ def load(self) -> "Trajectory":
89
+ """Load events from ``path`` (or ``run_id``) into ``events`` (idempotent)."""
90
+ if not self.events:
91
+ source = self.path or (self.run_id or None)
92
+ if source:
93
+ from mas.lab.plots.trajectory import load_trace # lazy import
94
+ self.events = load_trace(source)
95
+ return self
96
+
97
+ def as_dict(self) -> Dict[str, Any]:
98
+ d = super().as_dict()
99
+ d["run_id"] = self.run_id
100
+ return d
101
+
102
+
103
+ @dataclass
104
+ class AnnotatedTrajectory(Trajectory):
105
+ """Trajectory enriched with analyst highlights and free-form notes.
106
+
107
+ Parameters
108
+ ----------
109
+ highlights:
110
+ Each entry is a correlation-id prefix (e.g. ``"f19445b6"``) **or** a
111
+ 1-based delegation index (e.g. ``"3"``).
112
+ notes:
113
+ List of ``{"index": int, "note": str}`` dicts — one per highlighted
114
+ delegation. Rendered as tooltips or annotations in plots.
115
+ """
116
+
117
+ kind: ClassVar[str] = "annotated_trajectory"
118
+
119
+ highlights: List[str] = field(default_factory=list)
120
+ notes: List[Dict[str, Any]] = field(default_factory=list)
121
+
122
+ def as_dict(self) -> Dict[str, Any]:
123
+ d = super().as_dict()
124
+ d["highlights"] = self.highlights
125
+ d["notes"] = self.notes
126
+ return d
127
+
128
+
129
+ # ---------------------------------------------------------------------------
130
+ # Plot artifacts
131
+ # ---------------------------------------------------------------------------
132
+
133
+ @dataclass
134
+ class PlotFile(Artifact):
135
+ """A rendered visualisation written to disk (HTML, SVG, PNG …)."""
136
+
137
+ kind: ClassVar[str] = "plot_file"
138
+
139
+ format: str = "html"
140
+ """One of: html, svg, mermaid, png."""
141
+
142
+ def as_dict(self) -> Dict[str, Any]:
143
+ d = super().as_dict()
144
+ d["format"] = self.format
145
+ return d
146
+
147
+
148
+ # ---------------------------------------------------------------------------
149
+ # Knowledge-graph family
150
+ # ---------------------------------------------------------------------------
151
+
152
+ @dataclass
153
+ class NormalizedTrace(Artifact):
154
+ """Normalized manifold trace (JSONL) ready for KG construction."""
155
+
156
+ kind: ClassVar[str] = "normalized_trace"
157
+
158
+ run_id: str = ""
159
+
160
+ def as_dict(self) -> Dict[str, Any]:
161
+ d = super().as_dict()
162
+ d["run_id"] = self.run_id
163
+ return d
164
+
165
+
166
+ @dataclass
167
+ class KnowledgeGraph(Artifact):
168
+ """JSON-LD knowledge graph produced by the manifold normalizer."""
169
+
170
+ kind: ClassVar[str] = "knowledge_graph"
171
+
172
+ run_id: str = ""
173
+
174
+ def load_json(self) -> Any:
175
+ """Read and return the JSON-LD dict from ``path``."""
176
+ if self.path:
177
+ return json.loads(Path(self.path).read_text(encoding="utf-8"))
178
+ return self.data
179
+
180
+ def as_dict(self) -> Dict[str, Any]:
181
+ d = super().as_dict()
182
+ d["run_id"] = self.run_id
183
+ return d
184
+
185
+
186
+ # ---------------------------------------------------------------------------
187
+ # MCE session-metrics artefacts
188
+ # ---------------------------------------------------------------------------
189
+
190
+ @dataclass
191
+ class SessionMetrics(Artifact):
192
+ """MCE metric scores for one MAS session.
193
+
194
+ Written as ``metrics.json`` next to ``run_info.json`` in each run folder
195
+ (``item{N}/r{K}/metrics.json``) by :class:`EvalMceBatchStep`.
196
+
197
+ Parameters
198
+ ----------
199
+ item_id:
200
+ Dataset item identifier (e.g. ``"1"``).
201
+ scenario:
202
+ Benchmark scenario name (e.g. ``"baseline"``).
203
+ session:
204
+ Session-level scores keyed by metric_id.
205
+ Each value is ``{"value": float|None, "reasoning": str, "error": str|None}``.
206
+ agents:
207
+ Per-agent scores (empty in schema_version="1").
208
+ Reserved for future extension.
209
+ schema_version:
210
+ Artefact format version. Increment on breaking changes.
211
+ """
212
+
213
+ kind: ClassVar[str] = "session_metrics"
214
+
215
+ item_id: str = ""
216
+ scenario: str = ""
217
+ session: Dict[str, Any] = field(default_factory=dict)
218
+ agents: Dict[str, Any] = field(default_factory=dict)
219
+ schema_version: str = "1"
220
+
221
+ def as_dict(self) -> Dict[str, Any]:
222
+ d = super().as_dict()
223
+ d.update({
224
+ "schema_version": self.schema_version,
225
+ "item_id": self.item_id,
226
+ "scenario": self.scenario,
227
+ "session": self.session,
228
+ "agents": self.agents,
229
+ })
230
+ return d
231
+
232
+ @classmethod
233
+ def from_json(cls, path: Path) -> "SessionMetrics":
234
+ """Load a ``metrics.json`` file from *path*."""
235
+ doc = json.loads(Path(path).read_text(encoding="utf-8"))
236
+ return cls(
237
+ path=path,
238
+ item_id=doc.get("item_id", ""),
239
+ scenario=doc.get("scenario", ""),
240
+ session=doc.get("session", {}),
241
+ agents=doc.get("agents", {}),
242
+ schema_version=doc.get("schema_version", "1"),
243
+ )
244
+
245
+
246
+ # ---------------------------------------------------------------------------
247
+ # Registry
248
+ # ---------------------------------------------------------------------------
249
+
250
+ _ARTIFACT_REGISTRY: Dict[str, Type[Artifact]] = {
251
+ "artifact": Artifact,
252
+ "trajectory": Trajectory,
253
+ "annotated_trajectory": AnnotatedTrajectory,
254
+ "plot_file": PlotFile,
255
+ "normalized_trace": NormalizedTrace,
256
+ "knowledge_graph": KnowledgeGraph,
257
+ "session_metrics": SessionMetrics,
258
+ }
259
+
260
+
261
+ def register_artifact(cls: Type[Artifact]) -> Type[Artifact]:
262
+ """Class decorator — register a custom Artifact subclass by its ``kind``."""
263
+ _ARTIFACT_REGISTRY[cls.kind] = cls
264
+ return cls
265
+
266
+
267
+ def artifact_from_dict(d: Dict[str, Any]) -> Artifact:
268
+ """Reconstruct an Artifact from a dict produced by :meth:`Artifact.as_dict`."""
269
+ cls = _ARTIFACT_REGISTRY.get(d.get("kind", "artifact"), Artifact)
270
+ path = Path(d["path"]) if d.get("path") else None
271
+ inst = cls.__new__(cls)
272
+ # call dataclass __init__ with just the base fields; subclass extras default
273
+ Artifact.__init__(inst, path=path, meta=d.get("meta", {}))
274
+ if hasattr(inst, "run_id") and "run_id" in d:
275
+ inst.run_id = d["run_id"]
276
+ if hasattr(inst, "highlights") and "highlights" in d:
277
+ inst.highlights = d["highlights"]
278
+ if hasattr(inst, "notes") and "notes" in d:
279
+ inst.notes = d["notes"]
280
+ if hasattr(inst, "format") and "format" in d:
281
+ inst.format = d["format"]
282
+ return inst
@@ -0,0 +1,115 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """mas-lab click CLI — ``app`` is the single entry point.
4
+
5
+ Import and use programmatically::
6
+
7
+ from mas.lab.cli import app
8
+ from click.testing import CliRunner
9
+
10
+ result = CliRunner().invoke(app, ["benchmark", "list"])
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import importlib
15
+ import logging
16
+
17
+ import click
18
+ import mas.third_party_otel # noqa: F401
19
+ from dotenv import find_dotenv, load_dotenv
20
+ from mas.lab.cli.commands import check
21
+ from mas.lab.cli.commands import config as _config_mod
22
+ from mas.lab.cli.commands import init as _init_mod
23
+ from mas.lab.cli.commands import validate as _validate_mod
24
+ from mas.lab.cli.extensions import register_extension_components
25
+
26
+
27
+ @click.group()
28
+ @click.option("-v", "--verbose", count=True, default=0,
29
+ help="Verbosity: -v exchange log, -vv debug, -vvv full")
30
+ @click.pass_context
31
+ def app(ctx: click.Context, verbose: int) -> None:
32
+ """MAS Lab — evaluation, experimentation, and benchmarking tools.
33
+
34
+ \b
35
+ Command groups:
36
+ benchmark Run experiments, list results, manage steps
37
+ control Start/stop the controller daemon (UI + workers)
38
+ worker List and manage background workers
39
+ plot Generate trajectory and communication diagrams
40
+ run Execute processors and pipelines
41
+ pipe Stream-oriented processing
42
+
43
+ \b
44
+ Utilities:
45
+ check Validate a MAS configuration
46
+ config Show resolved paths and environment configuration
47
+ serve Start the benchmark UI server
48
+ telemetry Convert, verify, push, and dump traces (library-telemetry)
49
+ kg Normalize, validate, push, and dump graphs (library-kg)
50
+ eval Score traces with MCE metrics (library-eval)
51
+ validate Validate YAML manifests
52
+
53
+ \b
54
+ Optional extensions are discovered via the ``mas.lab.cli.components``
55
+ entry-point group and loaded automatically when their packages are installed.
56
+
57
+ \b
58
+ Global options:
59
+ -v Show exchange log
60
+ -vv Debug output
61
+ -vvv Full trace output
62
+ """
63
+ ctx.ensure_object(dict)
64
+ ctx.obj["verbose"] = verbose
65
+
66
+ try:
67
+ from mas.ctl.logging_setup import setup_logging
68
+ setup_logging(verbosity=verbose)
69
+ except ImportError:
70
+ level = (logging.DEBUG if verbose >= 2
71
+ else logging.INFO if verbose >= 1
72
+ else logging.WARNING)
73
+ logging.basicConfig(level=level, format="%(message)s")
74
+
75
+ # Workspace config — loaded once, shared via ctx.obj
76
+ try:
77
+ from mas.lab.workspace import WorkspaceConfig
78
+ ctx.obj["workspace"] = WorkspaceConfig.load()
79
+ except ImportError:
80
+ ctx.obj["workspace"] = None
81
+
82
+ load_dotenv(find_dotenv(usecwd=True))
83
+
84
+
85
+ def _add_if_importable(name: str, module: str, attr: str) -> None:
86
+ """Attach a command that lives in an optional lab component."""
87
+ try:
88
+ mod = importlib.import_module(module)
89
+ except ImportError:
90
+ return
91
+ app.add_command(getattr(mod, attr), name=name)
92
+
93
+
94
+ app.add_command(check.check_cmd, name="check")
95
+ app.add_command(check.check_config_cmd, name="check-config")
96
+ app.add_command(_config_mod.config_cmd, name="config")
97
+ app.add_command(_init_mod.init_cmd, name="init")
98
+ app.add_command(_validate_mod.validate_cmd, name="validate")
99
+ _add_if_importable("serve", "mas.lab.cli.commands.serve", "serve_cmd")
100
+ _add_if_importable("benchmark", "mas.lab.cli.commands.benchmark", "group")
101
+ _add_if_importable("plot", "mas.lab.cli.commands.plot", "plot_group")
102
+ _add_if_importable("telemetry", "mas.lab.cli.commands.telemetry", "telemetry_group")
103
+ _add_if_importable("run", "mas.lab.cli.commands.run", "run_group")
104
+ _add_if_importable("pipe", "mas.lab.cli.commands.pipe", "pipe_group")
105
+
106
+ # Register optional command groups from installed extension packages.
107
+ register_extension_components(app)
108
+
109
+
110
+ def main() -> None:
111
+ app()
112
+
113
+
114
+ if __name__ == "__main__":
115
+ app()
@@ -0,0 +1,3 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Click command modules for mas-lab."""
@@ -0,0 +1,73 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Click wrappers for ``mas-lab benchmark`` subcommands."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import click
8
+
9
+ from .analyze import analyze_cmd
10
+ from .artifacts import artifact_group
11
+ from .clean import clean_cmd
12
+ from .daemon import daemon_group
13
+ from .export_cmd import export_cmd
14
+ from .follow import follow_cmd
15
+ from .import_cmd import import_cmd
16
+ from .import_hf import import_hf_cmd
17
+ from .list_cmd import list_cmd
18
+ from .migrate import migrate_cmd
19
+ from .pipeline import pipeline_group
20
+ from .publish_hf import publish_hf_cmd
21
+ from .rename import rename_cmd
22
+ from .run import run_cmd
23
+ from .show import show_cmd
24
+ from .step import step_group
25
+ from .step_info import step_info_cmd
26
+ from .update import update_cmd
27
+
28
+
29
+ @click.group("benchmark")
30
+ def group() -> None:
31
+ """Benchmark management — run, list, show, update, analyze, step."""
32
+
33
+
34
+ group.add_command(run_cmd)
35
+ group.add_command(list_cmd)
36
+ group.add_command(show_cmd)
37
+ group.add_command(artifact_group)
38
+ group.add_command(update_cmd)
39
+ group.add_command(rename_cmd)
40
+ group.add_command(migrate_cmd)
41
+ group.add_command(analyze_cmd)
42
+ group.add_command(export_cmd)
43
+ group.add_command(import_cmd)
44
+ group.add_command(publish_hf_cmd)
45
+ group.add_command(import_hf_cmd)
46
+ group.add_command(follow_cmd)
47
+ group.add_command(clean_cmd)
48
+ group.add_command(step_info_cmd)
49
+ group.add_command(step_group)
50
+ group.add_command(pipeline_group)
51
+ group.add_command(daemon_group)
52
+
53
+ __all__ = [
54
+ "group",
55
+ "run_cmd",
56
+ "list_cmd",
57
+ "show_cmd",
58
+ "artifact_group",
59
+ "update_cmd",
60
+ "rename_cmd",
61
+ "migrate_cmd",
62
+ "analyze_cmd",
63
+ "export_cmd",
64
+ "import_cmd",
65
+ "publish_hf_cmd",
66
+ "import_hf_cmd",
67
+ "follow_cmd",
68
+ "clean_cmd",
69
+ "step_info_cmd",
70
+ "step_group",
71
+ "pipeline_group",
72
+ "daemon_group",
73
+ ]
@@ -0,0 +1,25 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """``mas-lab benchmark analyze`` command."""
4
+ from __future__ import annotations
5
+
6
+ from pathlib import Path
7
+ from types import SimpleNamespace
8
+
9
+ import click
10
+
11
+
12
+ @click.command("analyze")
13
+ @click.argument("benchmark_id")
14
+ @click.option("--experiment-yaml", type=Path, default=None,
15
+ help="Path to experiment.yaml (auto-detected if omitted).")
16
+ @click.option("-o", "--output-dir", type=Path, default=None)
17
+ def analyze_cmd(benchmark_id: str, experiment_yaml: Path | None,
18
+ output_dir: Path | None) -> None:
19
+ """Regenerate statistics and plots from existing results."""
20
+ from mas.lab.benchmark.cli import analyze_command
21
+
22
+ args = SimpleNamespace(benchmark_id=benchmark_id,
23
+ experiment_yaml=experiment_yaml,
24
+ output_dir=output_dir)
25
+ raise SystemExit(analyze_command(args))
@@ -0,0 +1,57 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """``mas-lab benchmark artifact`` commands."""
4
+ from __future__ import annotations
5
+
6
+ from types import SimpleNamespace
7
+
8
+ import click
9
+
10
+
11
+ @click.group("artifact")
12
+ def artifact_group() -> None:
13
+ """List and show artifacts for an experiment."""
14
+
15
+
16
+ _LEVEL = click.Choice(["application", "scenario", "test", "run"], case_sensitive=False)
17
+
18
+
19
+ @artifact_group.command("list")
20
+ @click.argument("target")
21
+ @click.option("-t", "--type", "artifact_type", default=None, metavar="TYPE",
22
+ help="Filter by artifact type.")
23
+ @click.option("-l", "--level", "level", type=_LEVEL, default=None,
24
+ help="Filter to one hierarchy level.")
25
+ @click.option("-v", "--verbose", is_flag=True, default=False,
26
+ help="Include the type description.")
27
+ def artifact_list_cmd(
28
+ target: str,
29
+ artifact_type: str | None,
30
+ level: str | None,
31
+ verbose: bool,
32
+ ) -> None:
33
+ """List artifacts declared in the experiment, and whether each was generated.
34
+
35
+ TARGET is ``last``, a benchmark id, or an experiment YAML. The run directory
36
+ is resolved from the last-run pointer or the experiment identity — not passed
37
+ as an argument.
38
+ """
39
+ from mas.lab.benchmark.cli import artifact_list_command
40
+
41
+ raise SystemExit(artifact_list_command(SimpleNamespace(
42
+ target=target,
43
+ artifact_type=artifact_type,
44
+ level=level,
45
+ verbose=verbose,
46
+ )))
47
+
48
+
49
+ @artifact_group.command("show")
50
+ @click.argument("artifact_id")
51
+ def artifact_show_cmd(artifact_id: str) -> None:
52
+ """Show one generated artifact by its 8-character id."""
53
+ from mas.lab.benchmark.cli import show_artifact_by_id_command
54
+
55
+ raise SystemExit(show_artifact_by_id_command(SimpleNamespace(
56
+ artifact_id=artifact_id,
57
+ )))
@@ -0,0 +1,56 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """``mas-lab benchmark clean`` command."""
4
+ from __future__ import annotations
5
+
6
+ from pathlib import Path
7
+ from types import SimpleNamespace
8
+
9
+ import click
10
+
11
+
12
+ @click.command("clean")
13
+ @click.argument("experiment_yaml", type=Path, metavar="EXPERIMENT_YAML")
14
+ @click.option("--scenario", "scenarios", multiple=True, metavar="SCENARIO_ID",
15
+ help="Scenario ID(s) to clean. Repeatable. If omitted, ALL scenarios are cleaned.")
16
+ @click.option("--dry-run", is_flag=True, default=False,
17
+ help="Print what would be deleted without deleting anything.")
18
+ @click.option("--keep-traces", is_flag=True, default=False,
19
+ help="Delete only the scenario run dirs; keep the global trace-cache entries.")
20
+ @click.option("-o", "--output-dir", type=Path, default=None,
21
+ help="Override the output dir directly (use when experiment.yaml is unavailable; pass '-' as EXPERIMENT_YAML).") # noqa: E501
22
+ def clean_cmd(
23
+ experiment_yaml: Path,
24
+ scenarios: tuple[str, ...],
25
+ dry_run: bool,
26
+ keep_traces: bool,
27
+ output_dir: Path | None,
28
+ ) -> None:
29
+ """Delete cached runs for selected scenarios so they are re-executed on the next run.
30
+
31
+ Examples:
32
+
33
+ \b
34
+ # Clean only the ToT scenario
35
+ mas-lab benchmark clean labs/design-space.lab/01-design-patterns/experiment.yaml \\
36
+ --scenario pattern-tree-of-thoughts
37
+
38
+ \b
39
+ # Dry-run: see what would be removed
40
+ mas-lab benchmark clean labs/design-space.lab/01-design-patterns/experiment.yaml \\
41
+ --scenario pattern-tree-of-thoughts --dry-run
42
+
43
+ \b
44
+ # Clean all scenarios in an experiment
45
+ mas-lab benchmark clean labs/design-space.lab/01-design-patterns/experiment.yaml
46
+ """
47
+ from mas.lab.benchmark.cli import clean_command
48
+
49
+ args = SimpleNamespace(
50
+ experiment_yaml=experiment_yaml,
51
+ scenarios=list(scenarios) if scenarios else None,
52
+ dry_run=dry_run,
53
+ keep_traces=keep_traces,
54
+ output_dir=output_dir,
55
+ )
56
+ raise SystemExit(clean_command(args))