rocket-engine 0.0.2b1__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.
- rocket_engine/__init__.py +8 -0
- rocket_engine/__main__.py +12 -0
- rocket_engine/adapters/README.md +54 -0
- rocket_engine/adapters/__init__.py +7 -0
- rocket_engine/adapters/api.py +39 -0
- rocket_engine/adapters/base.py +46 -0
- rocket_engine/adapters/cace/__init__.py +163 -0
- rocket_engine/adapters/circulax/README.md +10 -0
- rocket_engine/adapters/circulax/__init__.py +112 -0
- rocket_engine/adapters/circulax/_driver.py +190 -0
- rocket_engine/adapters/circulax/adapter.py +538 -0
- rocket_engine/adapters/circulax/results.py +291 -0
- rocket_engine/adapters/cli/README.md +34 -0
- rocket_engine/adapters/cli/__init__.py +305 -0
- rocket_engine/adapters/connectivity/__init__.py +196 -0
- rocket_engine/adapters/connectivity/results.py +214 -0
- rocket_engine/adapters/cosim/README.md +9 -0
- rocket_engine/adapters/cosim/__init__.py +1390 -0
- rocket_engine/adapters/cosim/results.py +214 -0
- rocket_engine/adapters/devsim/__init__.py +171 -0
- rocket_engine/adapters/digital_flow/README.md +9 -0
- rocket_engine/adapters/digital_flow/__init__.py +1121 -0
- rocket_engine/adapters/digital_flow/results.py +227 -0
- rocket_engine/adapters/digital_flow/state.py +462 -0
- rocket_engine/adapters/drc/README.klayout.md +7 -0
- rocket_engine/adapters/drc/README.md +7 -0
- rocket_engine/adapters/drc/__init__.py +1 -0
- rocket_engine/adapters/drc/klayout.py +952 -0
- rocket_engine/adapters/drc/magic.py +287 -0
- rocket_engine/adapters/drc/results.py +184 -0
- rocket_engine/adapters/drc/results_kl.py +188 -0
- rocket_engine/adapters/elmer/__init__.py +163 -0
- rocket_engine/adapters/envelope.py +120 -0
- rocket_engine/adapters/equivalence/__init__.py +156 -0
- rocket_engine/adapters/errors.py +181 -0
- rocket_engine/adapters/extract_magic/README.md +15 -0
- rocket_engine/adapters/extract_magic/__init__.py +425 -0
- rocket_engine/adapters/extract_magic/results.py +209 -0
- rocket_engine/adapters/formal_sby/README.md +7 -0
- rocket_engine/adapters/formal_sby/__init__.py +773 -0
- rocket_engine/adapters/formal_sby/formal_edge.py +195 -0
- rocket_engine/adapters/formal_sby/results.py +185 -0
- rocket_engine/adapters/gdsfactory/__init__.py +194 -0
- rocket_engine/adapters/gdsfactory/_driver.py +459 -0
- rocket_engine/adapters/gdsfactory/adapter.py +609 -0
- rocket_engine/adapters/gdsfactory/results.py +181 -0
- rocket_engine/adapters/hdl_sim/README.md +7 -0
- rocket_engine/adapters/hdl_sim/__init__.py +369 -0
- rocket_engine/adapters/hdl_sim/ghdl.py +155 -0
- rocket_engine/adapters/hdl_sim/results.py +178 -0
- rocket_engine/adapters/hdl_sim/verilator.py +167 -0
- rocket_engine/adapters/http/README.md +15 -0
- rocket_engine/adapters/http/__init__.py +458 -0
- rocket_engine/adapters/http/events.py +63 -0
- rocket_engine/adapters/ipc/README.md +9 -0
- rocket_engine/adapters/ipc/__init__.py +111 -0
- rocket_engine/adapters/lvs_netgen/README.md +20 -0
- rocket_engine/adapters/lvs_netgen/__init__.py +889 -0
- rocket_engine/adapters/lvs_netgen/results.py +168 -0
- rocket_engine/adapters/mcp/README.md +19 -0
- rocket_engine/adapters/mcp/__init__.py +85 -0
- rocket_engine/adapters/mcp/api-schema.json +6107 -0
- rocket_engine/adapters/mcp/pull.py +246 -0
- rocket_engine/adapters/mcp/server.py +794 -0
- rocket_engine/adapters/meep/__init__.py +167 -0
- rocket_engine/adapters/meow/README.md +7 -0
- rocket_engine/adapters/meow/__init__.py +190 -0
- rocket_engine/adapters/meow/_driver.py +227 -0
- rocket_engine/adapters/meow/_envelope.py +108 -0
- rocket_engine/adapters/meow/adapter.py +491 -0
- rocket_engine/adapters/meow/results.py +218 -0
- rocket_engine/adapters/mutation/__init__.py +192 -0
- rocket_engine/adapters/ngspice/README.md +28 -0
- rocket_engine/adapters/ngspice/__init__.py +627 -0
- rocket_engine/adapters/ngspice/results.py +215 -0
- rocket_engine/adapters/postlayout/README.md +28 -0
- rocket_engine/adapters/postlayout/__init__.py +17 -0
- rocket_engine/adapters/postlayout/adapter.py +1062 -0
- rocket_engine/adapters/postlayout/results.py +330 -0
- rocket_engine/adapters/postlayout/translate.py +71 -0
- rocket_engine/adapters/profile.py +220 -0
- rocket_engine/adapters/sax/README.md +23 -0
- rocket_engine/adapters/sax/__init__.py +516 -0
- rocket_engine/adapters/sax/_driver.py +148 -0
- rocket_engine/adapters/sax/results.py +291 -0
- rocket_engine/adapters/stdio_console.py +1151 -0
- rocket_engine/adapters/syntax/README.md +5 -0
- rocket_engine/adapters/syntax/__init__.py +366 -0
- rocket_engine/adapters/syntax/results.py +181 -0
- rocket_engine/adapters/synth_yosys/README.md +7 -0
- rocket_engine/adapters/synth_yosys/__init__.py +655 -0
- rocket_engine/adapters/synth_yosys/amaranth.py +120 -0
- rocket_engine/adapters/synth_yosys/ghdl.py +128 -0
- rocket_engine/adapters/synth_yosys/results.py +173 -0
- rocket_engine/adapters/timing_sta/README.md +9 -0
- rocket_engine/adapters/timing_sta/__init__.py +726 -0
- rocket_engine/adapters/view_sync/__init__.py +185 -0
- rocket_engine/adapters/view_sync/results.py +189 -0
- rocket_engine/adapters/vlsir/__init__.py +142 -0
- rocket_engine/adapters/xschem/__init__.py +440 -0
- rocket_engine/adapters/xschem/deck.py +266 -0
- rocket_engine/clients/__init__.py +5 -0
- rocket_engine/clients/runtime.py +255 -0
- rocket_engine/composition.py +2279 -0
- rocket_engine/execution/README.md +30 -0
- rocket_engine/execution/__init__.py +150 -0
- rocket_engine/execution/apig/__init__.py +1 -0
- rocket_engine/execution/apig/pool.py +463 -0
- rocket_engine/execution/apig/submit.py +124 -0
- rocket_engine/execution/block_runs_scope.py +479 -0
- rocket_engine/execution/coalesce.py +414 -0
- rocket_engine/execution/console/__init__.py +1 -0
- rocket_engine/execution/console/apply_session.py +318 -0
- rocket_engine/execution/console/lifecycle.py +265 -0
- rocket_engine/execution/dispatch.py +205 -0
- rocket_engine/execution/errors.py +31 -0
- rocket_engine/execution/ports.py +797 -0
- rocket_engine/execution/pyexec/__init__.py +1 -0
- rocket_engine/execution/pyexec/apply_context.py +547 -0
- rocket_engine/execution/pyexec/approvals.py +173 -0
- rocket_engine/execution/pyexec/runner.py +404 -0
- rocket_engine/execution/pyexec/support.py +464 -0
- rocket_engine/execution/python_exec.py +897 -0
- rocket_engine/execution/queue.py +85 -0
- rocket_engine/execution/replay_safe.py +601 -0
- rocket_engine/execution/routing/__init__.py +1 -0
- rocket_engine/execution/routing/calls.py +364 -0
- rocket_engine/execution/routing/namespace.py +785 -0
- rocket_engine/execution/routing/ports.py +102 -0
- rocket_engine/execution/routing/tools.py +346 -0
- rocket_engine/execution/runner.py +311 -0
- rocket_engine/execution/runtime.py +1149 -0
- rocket_engine/execution/service.py +279 -0
- rocket_engine/execution/worker.py +464 -0
- rocket_engine/interfaces/README.md +26 -0
- rocket_engine/interfaces/__init__.py +58 -0
- rocket_engine/interfaces/apig_assets.py +407 -0
- rocket_engine/interfaces/celllink.py +461 -0
- rocket_engine/interfaces/documents.py +92 -0
- rocket_engine/interfaces/envelopes.py +131 -0
- rocket_engine/interfaces/errors.py +16 -0
- rocket_engine/interfaces/layout.py +410 -0
- rocket_engine/interfaces/layout_schema.yaml +278 -0
- rocket_engine/interfaces/paths.py +223 -0
- rocket_engine/interfaces/pdks.py +550 -0
- rocket_engine/interfaces/ports.py +72 -0
- rocket_engine/interfaces/references.py +115 -0
- rocket_engine/interfaces/refs.py +85 -0
- rocket_engine/interfaces/runparams.py +304 -0
- rocket_engine/interfaces/setup.py +552 -0
- rocket_engine/interfaces/setup_catalogue.yaml +243 -0
- rocket_engine/interfaces/setup_probes.py +66 -0
- rocket_engine/interfaces/vocabulary.py +58 -0
- rocket_engine/interfaces/yaml_dump.py +31 -0
- rocket_engine/library/README.md +22 -0
- rocket_engine/library/__init__.py +13 -0
- rocket_engine/library/apig_catalog.py +371 -0
- rocket_engine/library/catalog.py +185 -0
- rocket_engine/library/errors.py +16 -0
- rocket_engine/mutate/__init__.py +776 -0
- rocket_engine/mutate/operators.py +700 -0
- rocket_engine/ops/__init__.py +1 -0
- rocket_engine/ops/deferred.py +279 -0
- rocket_engine/ops/editors.py +404 -0
- rocket_engine/ops/evidence.py +194 -0
- rocket_engine/ops/gates.py +262 -0
- rocket_engine/ops/ports.py +76 -0
- rocket_engine/ops/queries.py +417 -0
- rocket_engine/ops/runs.py +553 -0
- rocket_engine/ops/submit.py +614 -0
- rocket_engine/project/README.md +27 -0
- rocket_engine/project/__init__.py +6 -0
- rocket_engine/project/canvas/__init__.py +55 -0
- rocket_engine/project/canvas/compile.py +109 -0
- rocket_engine/project/canvas/integrate.py +48 -0
- rocket_engine/project/canvas/schema.py +241 -0
- rocket_engine/project/canvas/templates.py +46 -0
- rocket_engine/project/canvas/validate.py +603 -0
- rocket_engine/project/edit/__init__.py +5 -0
- rocket_engine/project/edit/cas.py +87 -0
- rocket_engine/project/edit/transactions.py +282 -0
- rocket_engine/project/errors.py +16 -0
- rocket_engine/project/frontend13.py +685 -0
- rocket_engine/project/geometry/__init__.py +23 -0
- rocket_engine/project/geometry/ops.py +451 -0
- rocket_engine/project/geometry/snap.py +103 -0
- rocket_engine/project/ip/__init__.py +1 -0
- rocket_engine/project/ip/blocks.py +195 -0
- rocket_engine/project/ip/entry.schema.json +78 -0
- rocket_engine/project/ip/lifecycle.py +402 -0
- rocket_engine/project/ip/verbs.py +123 -0
- rocket_engine/project/layers/__init__.py +6 -0
- rocket_engine/project/layers/bindings.py +115 -0
- rocket_engine/project/layers/catalog.py +228 -0
- rocket_engine/project/layers/presets.py +43 -0
- rocket_engine/project/ledger/__init__.py +1 -0
- rocket_engine/project/ledger/docs.py +167 -0
- rocket_engine/project/ledger/entities.py +176 -0
- rocket_engine/project/ledger/events.py +29 -0
- rocket_engine/project/ledger/runs.py +257 -0
- rocket_engine/project/ledger/shared.py +174 -0
- rocket_engine/project/ledger/snapshots.py +385 -0
- rocket_engine/project/ledger/wires.py +552 -0
- rocket_engine/project/process/__init__.py +14 -0
- rocket_engine/project/process/model.py +193 -0
- rocket_engine/project/process/recipes.py +121 -0
- rocket_engine/project/schematic/__init__.py +1 -0
- rocket_engine/project/schematic/hierarchy.py +312 -0
- rocket_engine/project/schematic/instances.py +393 -0
- rocket_engine/project/schematic/sheets.py +198 -0
- rocket_engine/project/service.py +2941 -0
- rocket_engine/project/symbols/__init__.py +1 -0
- rocket_engine/project/symbols/cells.py +324 -0
- rocket_engine/project/symbols/linkage.py +356 -0
- rocket_engine/project/symbols/symbols.py +352 -0
- rocket_engine/project/templates/__init__.py +303 -0
- rocket_engine/project/templates/errors.py +25 -0
- rocket_engine/project/testbench/__init__.py +49 -0
- rocket_engine/project/testbench/blocks.py +619 -0
- rocket_engine/project/testbench/templates.py +134 -0
- rocket_engine/stores/README.md +12 -0
- rocket_engine/stores/__init__.py +6 -0
- rocket_engine/stores/ecs.py +1508 -0
- rocket_engine/stores/layers.py +1023 -0
- rocket_engine/stores/settings.py +1188 -0
- rocket_engine/stores/wireplace.py +1324 -0
- rocket_engine/workflows/README.md +18 -0
- rocket_engine/workflows/__init__.py +53 -0
- rocket_engine/workflows/capability_plan.py +102 -0
- rocket_engine/workflows/completion.py +228 -0
- rocket_engine/workflows/connectgraph.py +734 -0
- rocket_engine/workflows/connectgraph_emit.py +336 -0
- rocket_engine/workflows/gates.py +144 -0
- rocket_engine/workflows/photonic_wire.py +76 -0
- rocket_engine/workflows/stages.py +324 -0
- rocket_engine/workflows/viewsync.py +399 -0
- rocket_engine-0.0.2b1.dist-info/METADATA +200 -0
- rocket_engine-0.0.2b1.dist-info/RECORD +241 -0
- rocket_engine-0.0.2b1.dist-info/WHEEL +4 -0
- rocket_engine-0.0.2b1.dist-info/entry_points.txt +2 -0
- rocket_engine-0.0.2b1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""Circulax v1 normalizer: v0 parse output to a ResultBundle.
|
|
2
|
+
|
|
3
|
+
Maps the ``run_optical`` circulax parse shape (``measurements[]``
|
|
4
|
+
with volt value, target, margin, and pass/fail status plus the
|
|
5
|
+
transient grid) onto a typed measurement table, a transient
|
|
6
|
+
series dataset with a bound trace view, measurements, and
|
|
7
|
+
findings. Pure function of the parsed output: no I/O, no tool
|
|
8
|
+
calls, deterministic ids derived from the run id. PASS
|
|
9
|
+
measurements keep value, target, margin, and status.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
TASK = "run_optical"
|
|
13
|
+
|
|
14
|
+
_DIAG_CAP = 240
|
|
15
|
+
_INLINE_CAP = 1024
|
|
16
|
+
|
|
17
|
+
_SEVERITY = {"error": "error", "warning": "warning"}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _cap(text):
|
|
21
|
+
"""Cap one diagnostic string at the bundle prose limit.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
text: Raw diagnostic text, possibly unbounded tool prose.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
The text unchanged when short, else truncated with an
|
|
28
|
+
ellipsis marker.
|
|
29
|
+
"""
|
|
30
|
+
text = text if isinstance(text, str) else str(text)
|
|
31
|
+
if len(text) <= _DIAG_CAP:
|
|
32
|
+
return text
|
|
33
|
+
return text[: _DIAG_CAP - 3] + "..."
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _quantity(value, unit):
|
|
37
|
+
"""Build one v1 Quantity, mapping unitless to dimensionless.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
value: Numeric value.
|
|
41
|
+
unit: Unit string, possibly empty for unitless measures.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Quantity mapping with an explicit unit.
|
|
45
|
+
"""
|
|
46
|
+
return {"value": float(value), "unit": unit if unit else "1"}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _limit(item):
|
|
50
|
+
"""Pick the binding target bound for one measurement.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
item: v0 measurement with a ``target`` min/max mapping.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
The bound closest to the measured value (the margin
|
|
57
|
+
binding bound), or None when the target is empty.
|
|
58
|
+
"""
|
|
59
|
+
target = item.get("target") or {}
|
|
60
|
+
scored = []
|
|
61
|
+
if target.get("min") is not None:
|
|
62
|
+
scored.append((float(item["value"]) - float(target["min"]),
|
|
63
|
+
float(target["min"])))
|
|
64
|
+
if target.get("max") is not None:
|
|
65
|
+
scored.append((float(target["max"]) - float(item["value"]),
|
|
66
|
+
float(target["max"])))
|
|
67
|
+
if not scored:
|
|
68
|
+
return None
|
|
69
|
+
return min(scored)[1]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _downsample(values):
|
|
73
|
+
"""Stride-downsample one sample list to the inline cap.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
values: Sample values in stored order.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
(samples, downsampled) pair with at most _INLINE_CAP
|
|
80
|
+
entries, stride-picked deterministically.
|
|
81
|
+
"""
|
|
82
|
+
values = list(values)
|
|
83
|
+
if len(values) <= _INLINE_CAP:
|
|
84
|
+
return values, False
|
|
85
|
+
step = -(-len(values) // _INLINE_CAP)
|
|
86
|
+
return values[::step], True
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _provenance(project_id, run_id, tools, provenance):
|
|
90
|
+
"""Build ResultProvenance, defaulting to a placeholder closure.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
project_id: Project the run belongs to.
|
|
94
|
+
run_id: Run the bundle describes.
|
|
95
|
+
tools: Tool ingredient mappings (id, version, digest?).
|
|
96
|
+
provenance: Full provenance mapping when the caller has
|
|
97
|
+
one; a deterministic placeholder otherwise.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Provenance mapping valid against the v1 schema.
|
|
101
|
+
"""
|
|
102
|
+
if provenance is not None:
|
|
103
|
+
return provenance
|
|
104
|
+
ref = {"scope": "project", "id": project_id, "revision": "r1"}
|
|
105
|
+
return {
|
|
106
|
+
"snapshot": dict(ref),
|
|
107
|
+
"input_lock": dict(ref),
|
|
108
|
+
"closure_hash": "0" * 64,
|
|
109
|
+
"tools": [
|
|
110
|
+
{
|
|
111
|
+
"id": item["id"],
|
|
112
|
+
"version": item.get("version", ""),
|
|
113
|
+
"digest": item.get("digest", "0" * 64),
|
|
114
|
+
}
|
|
115
|
+
for item in tools
|
|
116
|
+
],
|
|
117
|
+
"parents": [],
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def normalize(parsed, *, run_id, project_id="project", target=None,
|
|
122
|
+
tools=(), provenance=None):
|
|
123
|
+
"""Normalize one circulax parse output to a v1 ResultBundle.
|
|
124
|
+
|
|
125
|
+
Measurement rows become a table dataset plus a bound table
|
|
126
|
+
view; the transient becomes a time series dataset with a
|
|
127
|
+
bound trace view; violations become findings with capped
|
|
128
|
+
messages. PASS measurements keep value, target, margin, and
|
|
129
|
+
status. A malformed circuit (no transient) reports
|
|
130
|
+
not_converged with its error finding.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
parsed: v0 parse mapping with ``measurements[]``,
|
|
134
|
+
``transient``, and ``violations[]``.
|
|
135
|
+
run_id: Run the bundle describes (anchors all ids).
|
|
136
|
+
project_id: Project the run belongs to.
|
|
137
|
+
target: v1 target mapping; project kind by default.
|
|
138
|
+
tools: Tool ingredient mappings for provenance.
|
|
139
|
+
provenance: Full provenance mapping when the caller has
|
|
140
|
+
one; a deterministic placeholder otherwise.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Schema-valid ResultBundle mapping.
|
|
144
|
+
"""
|
|
145
|
+
measurements_in = parsed.get("measurements") or []
|
|
146
|
+
transient = parsed.get("transient") or {}
|
|
147
|
+
violations = parsed.get("violations") or []
|
|
148
|
+
table_id = f"{run_id}:dataset:measurements"
|
|
149
|
+
datasets = [
|
|
150
|
+
{
|
|
151
|
+
"id": table_id,
|
|
152
|
+
"kind": "table",
|
|
153
|
+
"row_count": len(measurements_in),
|
|
154
|
+
"columns": [
|
|
155
|
+
{
|
|
156
|
+
"id": f"{table_id}:name",
|
|
157
|
+
"name": "name",
|
|
158
|
+
"dtype": "utf8",
|
|
159
|
+
"unit": "1",
|
|
160
|
+
"data": [item["name"] for item in measurements_in],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"id": f"{table_id}:value",
|
|
164
|
+
"name": "value",
|
|
165
|
+
"dtype": "float64",
|
|
166
|
+
"unit": "1",
|
|
167
|
+
"data": [float(item["value"]) for item in measurements_in],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"id": f"{table_id}:margin",
|
|
171
|
+
"name": "margin",
|
|
172
|
+
"dtype": "float64",
|
|
173
|
+
"unit": "1",
|
|
174
|
+
"data": [float(item["margin"]) for item in measurements_in],
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"id": f"{table_id}:status",
|
|
178
|
+
"name": "status",
|
|
179
|
+
"dtype": "utf8",
|
|
180
|
+
"unit": "1",
|
|
181
|
+
"data": [item["status"] for item in measurements_in],
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
"conditions": [],
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
views = [
|
|
188
|
+
{
|
|
189
|
+
"id": f"{run_id}:view:measurements",
|
|
190
|
+
"kind": "table",
|
|
191
|
+
"dataset_id": table_id,
|
|
192
|
+
"name": "measurements",
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
grid, _ = _downsample(transient.get("t_s") or [])
|
|
196
|
+
curve, _ = _downsample(transient.get("v_V") or [])
|
|
197
|
+
if grid and curve:
|
|
198
|
+
ds_id = f"{run_id}:dataset:transient"
|
|
199
|
+
axis_id = f"{ds_id}:time"
|
|
200
|
+
sig_id = f"{ds_id}:voltage"
|
|
201
|
+
observe = transient.get("observe", "out")
|
|
202
|
+
datasets.append(
|
|
203
|
+
{
|
|
204
|
+
"id": ds_id,
|
|
205
|
+
"kind": "series",
|
|
206
|
+
"analysis": "tran",
|
|
207
|
+
"axes": [
|
|
208
|
+
{
|
|
209
|
+
"id": axis_id,
|
|
210
|
+
"name": "time",
|
|
211
|
+
"quantity": "time",
|
|
212
|
+
"unit": "s",
|
|
213
|
+
"kind": "continuous",
|
|
214
|
+
"scale": "linear",
|
|
215
|
+
"values": [float(v) for v in grid],
|
|
216
|
+
}
|
|
217
|
+
],
|
|
218
|
+
"signals": [
|
|
219
|
+
{
|
|
220
|
+
"id": sig_id,
|
|
221
|
+
"name": observe,
|
|
222
|
+
"quantity": "voltage",
|
|
223
|
+
"unit": "V",
|
|
224
|
+
"values": {
|
|
225
|
+
"storage": "inline",
|
|
226
|
+
"dtype": "float64",
|
|
227
|
+
"shape": [len(curve)],
|
|
228
|
+
"data": [float(v) for v in curve],
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
],
|
|
232
|
+
"conditions": [],
|
|
233
|
+
}
|
|
234
|
+
)
|
|
235
|
+
views.append(
|
|
236
|
+
{
|
|
237
|
+
"id": f"{run_id}:view:transient",
|
|
238
|
+
"kind": "traces",
|
|
239
|
+
"dataset_id": ds_id,
|
|
240
|
+
"name": observe,
|
|
241
|
+
"x_axis": axis_id,
|
|
242
|
+
"traces": [
|
|
243
|
+
{"signal": sig_id, "transform": "identity",
|
|
244
|
+
"axis": "left"}
|
|
245
|
+
],
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
measurements = []
|
|
249
|
+
for item in measurements_in:
|
|
250
|
+
entry = {
|
|
251
|
+
"name": item["name"],
|
|
252
|
+
"value": _quantity(item["value"], item.get("unit", "")),
|
|
253
|
+
"margin": _quantity(item["margin"], item.get("unit", "")),
|
|
254
|
+
"status": "PASS" if item["status"] == "pass" else "FAIL",
|
|
255
|
+
}
|
|
256
|
+
bound = _limit(item)
|
|
257
|
+
if bound is not None:
|
|
258
|
+
entry["limit"] = _quantity(bound, item.get("unit", ""))
|
|
259
|
+
measurements.append(entry)
|
|
260
|
+
findings = [
|
|
261
|
+
{
|
|
262
|
+
"id": f"{run_id}:finding:{index}",
|
|
263
|
+
"severity": _SEVERITY.get(v.get("severity"), "info"),
|
|
264
|
+
"code": v.get("rule", "circuit"),
|
|
265
|
+
"message": _cap(v.get("message", "")),
|
|
266
|
+
}
|
|
267
|
+
for index, v in enumerate(violations)
|
|
268
|
+
]
|
|
269
|
+
converged = not any(
|
|
270
|
+
v.get("rule") in ("circuit", "transient") for v in violations
|
|
271
|
+
)
|
|
272
|
+
return {
|
|
273
|
+
"schema": "rocketeda/results/v1",
|
|
274
|
+
"ref": {"scope": "project", "id": run_id, "revision": "r1"},
|
|
275
|
+
"run_id": run_id,
|
|
276
|
+
"task": TASK,
|
|
277
|
+
"target": dict(target)
|
|
278
|
+
if target is not None
|
|
279
|
+
else {"project_id": project_id, "kind": "project"},
|
|
280
|
+
"state": "complete",
|
|
281
|
+
"freshness": "current",
|
|
282
|
+
"convergence": "converged" if converged else "not_converged",
|
|
283
|
+
"provenance": _provenance(project_id, run_id, tools, provenance),
|
|
284
|
+
"datasets": datasets,
|
|
285
|
+
"views": views,
|
|
286
|
+
"measurements": measurements,
|
|
287
|
+
"findings": findings,
|
|
288
|
+
"artifacts": [],
|
|
289
|
+
"logs": [],
|
|
290
|
+
"gaps": [],
|
|
291
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Adapters/cli — headless command surface
|
|
2
|
+
|
|
3
|
+
## Responsibility (project decision)
|
|
4
|
+
|
|
5
|
+
Every engine operation invokable, every event printable: the terminal
|
|
6
|
+
client for debugging the headless engine without RocketIDE. Bare
|
|
7
|
+
args drop into the interactive REPL; `run <op> [key=value ...]`
|
|
8
|
+
runs one op; `--batch FILE`/`-` batch-executes; `--json` gives
|
|
9
|
+
stable machine envelopes. Entry: `python -m rocket_engine` (a
|
|
10
|
+
`[project.scripts]` `rocket-engine` alias can point at
|
|
11
|
+
`adapters.cli:main` once packaging wires it).
|
|
12
|
+
|
|
13
|
+
## Boundaries
|
|
14
|
+
|
|
15
|
+
Same as every transport: protocol ↔ ports only. No logic. argv
|
|
16
|
+
parsing/formatting lives here; op-call parsing and dispatch live in
|
|
17
|
+
the use-case layer (`execution.dispatch`, the inbound port shared
|
|
18
|
+
with console/MCP over the one catalog). Adapters never call each
|
|
19
|
+
other.
|
|
20
|
+
|
|
21
|
+
## Routing
|
|
22
|
+
|
|
23
|
+
- Parent rules → `../README.md`.
|
|
24
|
+
|
|
25
|
+
## Exits (honest)
|
|
26
|
+
|
|
27
|
+
- 0 execution-ok, EVEN when the verdict is FAIL (verdicts live in
|
|
28
|
+
the payload, never in exit codes — same rule as solvers).
|
|
29
|
+
- 1 engine error. 2 usage error (unknown op/args).
|
|
30
|
+
|
|
31
|
+
## Status
|
|
32
|
+
|
|
33
|
+
Live (CLI-DISPATCH). Console history replays as CLI batch scripts
|
|
34
|
+
by construction — same dispatch, same grammar.
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""CLI inbound adapter: argv parsing + result formatting over dispatch.
|
|
2
|
+
|
|
3
|
+
`rocket-engine` (this module via `python -m rocket_engine`): bare
|
|
4
|
+
args drop into the interactive REPL; `run <op> [key=value ...]`
|
|
5
|
+
runs one op; `--batch FILE`/`-` batch-executes op lines; `--json`
|
|
6
|
+
gives stable machine output. Op text parses through the one parser
|
|
7
|
+
of record (`execution.dispatch`); this adapter never parses op
|
|
8
|
+
calls itself, never calls another adapter, and holds no rules.
|
|
9
|
+
|
|
10
|
+
Exits: 0 execution-ok EVEN when the verdict is FAIL (verdicts live
|
|
11
|
+
in the payload, never in exit codes); 1 engine error; 2 usage
|
|
12
|
+
error (unknown op/args). Batch aggregates: any usage error -> 2,
|
|
13
|
+
else any engine error -> 1, else 0.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
from ...composition import Engine
|
|
20
|
+
from ...execution.dispatch import OpDispatcher, parse_op_call, parse_op_tokens
|
|
21
|
+
from ...execution.errors import UsageError
|
|
22
|
+
from ..errors import AdapterError, error_payload
|
|
23
|
+
|
|
24
|
+
__all__ = ["EXIT_ENGINE_ERROR", "EXIT_OK", "EXIT_USAGE", "USAGE", "main"]
|
|
25
|
+
|
|
26
|
+
EXIT_OK = 0
|
|
27
|
+
EXIT_ENGINE_ERROR = 1
|
|
28
|
+
EXIT_USAGE = 2
|
|
29
|
+
|
|
30
|
+
USAGE = """usage: rocket-engine [--project ID] [--actor NAME] [--request-id ID] [--json] [--batch FILE|-] [run <op> [key=value ...]]
|
|
31
|
+
|
|
32
|
+
No args drops into the interactive REPL (one op call per line,
|
|
33
|
+
blank lines and `#` comments skipped, `quit` to leave). `run`
|
|
34
|
+
executes one op call; `--batch` executes every op-call line of a
|
|
35
|
+
file (`-` reads stdin). `--json` prints stable machine envelopes.
|
|
36
|
+
`--request-id` stamps a caller id on a single `run` (batch and
|
|
37
|
+
REPL lines always mint their own, so repeats stay distinct).
|
|
38
|
+
Op calls are `<operation> [key=value ...]` with YAML-scalar values
|
|
39
|
+
(`benches=[ac,tran]`, `deck={tool: klayout}`); quote values with
|
|
40
|
+
spaces. Every call — queries and operations alike — goes through
|
|
41
|
+
submit, the one admission path.
|
|
42
|
+
|
|
43
|
+
Exits: 0 execution-ok (even when the verdict is FAIL — read the
|
|
44
|
+
payload); 1 engine error; 2 usage error (unknown op/args).
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _split_flag(token, name):
|
|
49
|
+
prefix = f"--{name}="
|
|
50
|
+
if token.startswith(prefix):
|
|
51
|
+
return token[len(prefix):]
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _parse_argv(argv):
|
|
56
|
+
options = {"json": False, "batch": None, "project": "default",
|
|
57
|
+
"actor": "cli", "request_id": None, "help": False}
|
|
58
|
+
rest = []
|
|
59
|
+
i = 0
|
|
60
|
+
while i < len(argv):
|
|
61
|
+
token = argv[i]
|
|
62
|
+
if token == "--json":
|
|
63
|
+
options["json"] = True
|
|
64
|
+
elif token in ("--help", "-h"):
|
|
65
|
+
options["help"] = True
|
|
66
|
+
elif token == "--batch":
|
|
67
|
+
i += 1
|
|
68
|
+
if i >= len(argv):
|
|
69
|
+
raise UsageError(
|
|
70
|
+
"invalid_command", "`--batch` needs a FILE (`-` for stdin).")
|
|
71
|
+
options["batch"] = argv[i]
|
|
72
|
+
elif token == "--project":
|
|
73
|
+
i += 1
|
|
74
|
+
if i >= len(argv):
|
|
75
|
+
raise UsageError("invalid_command", "`--project` needs an ID.")
|
|
76
|
+
options["project"] = argv[i]
|
|
77
|
+
elif token == "--actor":
|
|
78
|
+
i += 1
|
|
79
|
+
if i >= len(argv):
|
|
80
|
+
raise UsageError("invalid_command", "`--actor` needs a NAME.")
|
|
81
|
+
options["actor"] = argv[i]
|
|
82
|
+
elif token == "--request-id":
|
|
83
|
+
i += 1
|
|
84
|
+
if i >= len(argv):
|
|
85
|
+
raise UsageError(
|
|
86
|
+
"invalid_command", "`--request-id` needs an ID.")
|
|
87
|
+
options["request_id"] = argv[i]
|
|
88
|
+
elif _split_flag(token, "batch") is not None:
|
|
89
|
+
options["batch"] = _split_flag(token, "batch")
|
|
90
|
+
elif _split_flag(token, "project") is not None:
|
|
91
|
+
options["project"] = _split_flag(token, "project")
|
|
92
|
+
elif _split_flag(token, "actor") is not None:
|
|
93
|
+
options["actor"] = _split_flag(token, "actor")
|
|
94
|
+
elif _split_flag(token, "request-id") is not None:
|
|
95
|
+
options["request_id"] = _split_flag(token, "request-id")
|
|
96
|
+
elif token == "--":
|
|
97
|
+
rest.extend(argv[i + 1:])
|
|
98
|
+
break
|
|
99
|
+
elif token.startswith("-"):
|
|
100
|
+
raise UsageError("invalid_command", f"Unknown flag: {token}.")
|
|
101
|
+
else:
|
|
102
|
+
rest.extend(argv[i:])
|
|
103
|
+
break
|
|
104
|
+
i += 1
|
|
105
|
+
return options, rest
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _envelope(operation, ok, payload):
|
|
109
|
+
body = {"operation": operation, "ok": ok}
|
|
110
|
+
body["result" if ok else "error"] = payload
|
|
111
|
+
return json.dumps(body, sort_keys=True, default=str)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _human_summary(operation, result):
|
|
115
|
+
if isinstance(result, dict):
|
|
116
|
+
verdict = result.get("verdict")
|
|
117
|
+
if isinstance(verdict, dict) and "status" in verdict:
|
|
118
|
+
line = f"{operation}: {verdict['status']}"
|
|
119
|
+
if result.get("run_id"):
|
|
120
|
+
line += f" (run {result['run_id']})"
|
|
121
|
+
return line
|
|
122
|
+
return f"{operation}: ok"
|
|
123
|
+
return f"{operation}: {result}"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _emit_result(operation, result, as_json, stdout):
|
|
127
|
+
if as_json:
|
|
128
|
+
stdout.write(_envelope(operation, True, result) + "\n")
|
|
129
|
+
else:
|
|
130
|
+
stdout.write(_human_summary(operation, result) + "\n")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _emit_error(operation, exc, as_json, stdout, stderr):
|
|
134
|
+
payload = error_payload(exc)
|
|
135
|
+
if as_json:
|
|
136
|
+
stdout.write(_envelope(operation, False, payload) + "\n")
|
|
137
|
+
else:
|
|
138
|
+
line = f"error [{payload['code']}]: {payload['message']}"
|
|
139
|
+
if payload.get("fields"):
|
|
140
|
+
line += f" (fields: {', '.join(payload['fields'])})"
|
|
141
|
+
stderr.write(line + "\n")
|
|
142
|
+
if isinstance(exc, UsageError):
|
|
143
|
+
stderr.write(USAGE)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _classify(exc):
|
|
147
|
+
if isinstance(exc, UsageError):
|
|
148
|
+
return EXIT_USAGE
|
|
149
|
+
return EXIT_ENGINE_ERROR
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _run_single(dispatch, options, op_tokens, stdout, stderr):
|
|
153
|
+
if not op_tokens:
|
|
154
|
+
_emit_error(None, UsageError("invalid_command", "`run` needs an operation."),
|
|
155
|
+
options["json"], stdout, stderr)
|
|
156
|
+
return EXIT_USAGE
|
|
157
|
+
try:
|
|
158
|
+
operation, arguments = parse_op_tokens(op_tokens)
|
|
159
|
+
except UsageError as exc:
|
|
160
|
+
_emit_error(op_tokens[0], exc, options["json"], stdout, stderr)
|
|
161
|
+
return EXIT_USAGE
|
|
162
|
+
try:
|
|
163
|
+
result = dispatch.execute(operation, arguments,
|
|
164
|
+
request_id=options["request_id"])
|
|
165
|
+
except Exception as exc: # engine errors stay data, never tracebacks
|
|
166
|
+
_emit_error(operation, exc, options["json"], stdout, stderr)
|
|
167
|
+
return _classify(exc)
|
|
168
|
+
_emit_result(operation, result, options["json"], stdout)
|
|
169
|
+
return EXIT_OK
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _iter_batch_lines(source, stdin):
|
|
173
|
+
if source == "-":
|
|
174
|
+
return stdin.read().splitlines()
|
|
175
|
+
try:
|
|
176
|
+
with open(source, encoding="utf-8") as handle:
|
|
177
|
+
return handle.read().splitlines()
|
|
178
|
+
except OSError as exc:
|
|
179
|
+
raise AdapterError(
|
|
180
|
+
"batch_unreadable", f"Cannot read batch file {source}: {exc}."
|
|
181
|
+
) from exc
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _run_batch(dispatch, options, source, stdin, stdout, stderr):
|
|
185
|
+
try:
|
|
186
|
+
lines = _iter_batch_lines(source, stdin)
|
|
187
|
+
except Exception as exc:
|
|
188
|
+
_emit_error(None, exc, options["json"], stdout, stderr)
|
|
189
|
+
return _classify(exc)
|
|
190
|
+
worst = EXIT_OK
|
|
191
|
+
for line in lines:
|
|
192
|
+
stripped = line.strip()
|
|
193
|
+
if not stripped or stripped.startswith("#"):
|
|
194
|
+
continue
|
|
195
|
+
try:
|
|
196
|
+
operation, result = dispatch.execute_line(stripped)
|
|
197
|
+
except UsageError as exc:
|
|
198
|
+
_emit_error(stripped.split()[0] if stripped.split() else None,
|
|
199
|
+
exc, options["json"], stdout, stderr)
|
|
200
|
+
worst = max(worst, EXIT_USAGE)
|
|
201
|
+
continue
|
|
202
|
+
except Exception as exc:
|
|
203
|
+
# execute_line parsed this line already; re-parse only to
|
|
204
|
+
# label the envelope — it cannot fail here.
|
|
205
|
+
operation = parse_op_call(stripped)[0]
|
|
206
|
+
_emit_error(operation, exc, options["json"], stdout, stderr)
|
|
207
|
+
worst = max(worst, EXIT_ENGINE_ERROR)
|
|
208
|
+
continue
|
|
209
|
+
_emit_result(operation, result, options["json"], stdout)
|
|
210
|
+
return worst
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _run_repl(dispatch, options, stdin, stdout, stderr):
|
|
214
|
+
interactive = stdin.isatty() if hasattr(stdin, "isatty") else False
|
|
215
|
+
if interactive:
|
|
216
|
+
stdout.write("rocket> ")
|
|
217
|
+
stdout.flush()
|
|
218
|
+
while True:
|
|
219
|
+
line = stdin.readline()
|
|
220
|
+
if line == "":
|
|
221
|
+
break
|
|
222
|
+
stripped = line.strip()
|
|
223
|
+
if interactive:
|
|
224
|
+
stdout.write("rocket> ")
|
|
225
|
+
stdout.flush()
|
|
226
|
+
if not stripped or stripped.startswith("#"):
|
|
227
|
+
continue
|
|
228
|
+
if stripped in ("exit", "quit"):
|
|
229
|
+
break
|
|
230
|
+
if stripped == "help":
|
|
231
|
+
stdout.write(USAGE)
|
|
232
|
+
continue
|
|
233
|
+
try:
|
|
234
|
+
operation, result = dispatch.execute_line(stripped)
|
|
235
|
+
except UsageError as exc:
|
|
236
|
+
_emit_error(stripped.split()[0] if stripped.split() else None,
|
|
237
|
+
exc, options["json"], stdout, stderr)
|
|
238
|
+
continue
|
|
239
|
+
except Exception as exc:
|
|
240
|
+
operation = parse_op_call(stripped)[0]
|
|
241
|
+
_emit_error(operation, exc, options["json"], stdout, stderr)
|
|
242
|
+
continue
|
|
243
|
+
_emit_result(operation, result, options["json"], stdout)
|
|
244
|
+
return EXIT_OK
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def main(argv=None, engine=None, stdin=None, stdout=None, stderr=None):
|
|
248
|
+
"""Run the CLI; return the process exit code (never raise for use).
|
|
249
|
+
|
|
250
|
+
Bare args drop into the REPL, `run` executes one op, `--batch`
|
|
251
|
+
executes op lines; usage and engine failures become exit codes,
|
|
252
|
+
never tracebacks.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
argv: Argument vector without the program name; defaults to
|
|
256
|
+
sys.argv[1:].
|
|
257
|
+
engine: Engine to dispatch against; constructed from
|
|
258
|
+
--project/--actor when omitted.
|
|
259
|
+
stdin: Input stream for the REPL and `--batch -`.
|
|
260
|
+
stdout: Stream for results and machine envelopes.
|
|
261
|
+
stderr: Stream for human-readable errors plus usage.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Process exit code: 0 execution-ok (even when the verdict
|
|
265
|
+
is FAIL), 1 engine error, 2 usage error.
|
|
266
|
+
|
|
267
|
+
Caution: Exit zero is not PASS — the verdict lives in the
|
|
268
|
+
payload; read it, never the exit code.
|
|
269
|
+
"""
|
|
270
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
271
|
+
stdin = sys.stdin if stdin is None else stdin
|
|
272
|
+
stdout = sys.stdout if stdout is None else stdout
|
|
273
|
+
stderr = sys.stderr if stderr is None else stderr
|
|
274
|
+
try:
|
|
275
|
+
options, rest = _parse_argv(argv)
|
|
276
|
+
except UsageError as exc:
|
|
277
|
+
_emit_error(None, exc, False, stdout, stderr)
|
|
278
|
+
return EXIT_USAGE
|
|
279
|
+
if options["help"]:
|
|
280
|
+
stdout.write(USAGE)
|
|
281
|
+
return EXIT_OK
|
|
282
|
+
if options["batch"] is not None and rest[:1] == ["run"]:
|
|
283
|
+
_emit_error(None, UsageError("invalid_command",
|
|
284
|
+
"Cannot combine `run` with `--batch`."),
|
|
285
|
+
options["json"], stdout, stderr)
|
|
286
|
+
return EXIT_USAGE
|
|
287
|
+
if engine is None:
|
|
288
|
+
engine = Engine(options["project"], actor=options["actor"])
|
|
289
|
+
dispatch = OpDispatcher(engine, project_id=options["project"],
|
|
290
|
+
actor=options["actor"])
|
|
291
|
+
if rest[:1] == ["run"]:
|
|
292
|
+
return _run_single(dispatch, options, rest[1:], stdout, stderr)
|
|
293
|
+
if rest:
|
|
294
|
+
_emit_error(None, UsageError("invalid_command",
|
|
295
|
+
f"Unknown command: {rest[0]} (want `run`)."),
|
|
296
|
+
options["json"], stdout, stderr)
|
|
297
|
+
return EXIT_USAGE
|
|
298
|
+
if options["batch"] is not None:
|
|
299
|
+
return _run_batch(dispatch, options, options["batch"], stdin,
|
|
300
|
+
stdout, stderr)
|
|
301
|
+
return _run_repl(dispatch, options, stdin, stdout, stderr)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
if __name__ == "__main__":
|
|
305
|
+
raise SystemExit(main())
|