Skip to content

Commit 8612bed

Browse files
committed
Merge remote-tracking branch 'origin/orchestrator/py-eval'
2 parents 0f8ce1d + 45dee1a commit 8612bed

3 files changed

Lines changed: 1482 additions & 0 deletions

File tree

python/arbiter/cli.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from . import __version__
1212
from .compiler import CompileOptions, compile_model
1313
from .diagnostics import DiagnosticCollector
14+
from .evaluator import ArbiterEvaluator
1415
from .parser import parse_model
1516
from .schema import validate_schema
1617
from .validator import validate_model
@@ -134,6 +135,76 @@ def emit_docs_cmd(model: Path, out: Path) -> None:
134135
click.echo(f"✓ Documentation written to {out}")
135136

136137

138+
@main.command()
139+
@click.argument("model", type=click.Path(exists=True, path_type=Path))
140+
@click.option(
141+
"--facts",
142+
multiple=True,
143+
help="Set facts as key=value pairs (e.g. battery.voltage_mv=3300).",
144+
)
145+
@click.option(
146+
"--timestamps",
147+
multiple=True,
148+
help="Set fact timestamps as key=ms pairs (e.g. battery.voltage_mv=100).",
149+
)
150+
@click.option(
151+
"--snapshot-ts",
152+
type=int,
153+
default=0,
154+
help="Snapshot timestamp in ms (for staleness checks).",
155+
)
156+
@click.option("--json", "emit_json", is_flag=True, help="Output result as JSON.")
157+
def eval(model: Path, facts: tuple[str, ...], timestamps: tuple[str, ...],
158+
snapshot_ts: int, emit_json: bool) -> None:
159+
"""Evaluate a .arb.yaml model with given facts."""
160+
import json as json_mod
161+
162+
diag = DiagnosticCollector()
163+
data = parse_model(model, diag)
164+
if data is None:
165+
click.echo(diag.format(), err=True)
166+
sys.exit(1)
167+
168+
evaluator = ArbiterEvaluator(data)
169+
170+
for kv in facts:
171+
if "=" not in kv:
172+
click.echo(f"Error: invalid fact '{kv}', expected key=value", err=True)
173+
sys.exit(1)
174+
key, val = kv.split("=", 1)
175+
try:
176+
evaluator.set_fact(key, val)
177+
except KeyError as e:
178+
click.echo(f"Error: {e}", err=True)
179+
sys.exit(1)
180+
181+
for kv in timestamps:
182+
if "=" not in kv:
183+
click.echo(f"Error: invalid timestamp '{kv}', expected key=ms", err=True)
184+
sys.exit(1)
185+
key, val = kv.split("=", 1)
186+
try:
187+
evaluator.set_timestamp(key, int(val))
188+
except (KeyError, ValueError) as e:
189+
click.echo(f"Error: {e}", err=True)
190+
sys.exit(1)
191+
192+
evaluator.set_snapshot_timestamp(snapshot_ts)
193+
result = evaluator.eval()
194+
195+
if emit_json:
196+
click.echo(json_mod.dumps(result.to_dict(), indent=2))
197+
else:
198+
click.echo(f"Fired rules: {result.fired_rules}")
199+
if result.current_mode:
200+
click.echo(f"Mode: {result.current_mode}")
201+
if result.requested_actions:
202+
click.echo(f"Actions: {result.requested_actions}")
203+
if result.raised_faults:
204+
click.echo(f"Faults: {sorted(result.raised_faults)}")
205+
click.echo(f"Op count: {result.op_count}")
206+
207+
137208
@main.command("emit-tests")
138209
@click.argument("model", type=click.Path(exists=True, path_type=Path))
139210
@click.option("--out", type=click.Path(path_type=Path), required=True)

0 commit comments

Comments
 (0)