speclogician 0.0.0b1__py3-none-any.whl → 0.0.0.dev1__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.
- speclogician/agent/funcs.py +29 -0
- speclogician/cmd/agent_cmd.py +89 -0
- speclogician/cmd/data_cmd.py +24 -0
- speclogician/cmd/model_cmd.py +42 -0
- speclogician/cmd/overlay_cmd.py +30 -0
- speclogician/cmd/scenario_cmd.py +61 -0
- speclogician/cmd/state_cmd.py +52 -0
- speclogician/data/artifact.py +8 -50
- speclogician/data/container.py +18 -384
- speclogician/data/mapping.py +18 -17
- speclogician/data/refs.py +12 -11
- speclogician/data/reports.py +11 -0
- speclogician/data/traces.py +15 -6
- speclogician/llms/llmtools.py +102 -0
- speclogician/llms/overlay.py +264 -0
- speclogician/main.py +36 -102
- speclogician/modeling/__init__.py +0 -31
- speclogician/modeling/component.py +4 -60
- speclogician/modeling/conflict.py +5 -19
- speclogician/modeling/domain.py +93 -280
- speclogician/modeling/model.py +206 -0
- speclogician/modeling/predicates.py +20 -22
- speclogician/modeling/report.py +33 -0
- speclogician/modeling/scenario.py +119 -87
- speclogician/sl_cmd.py +76 -0
- speclogician/state/change.py +98 -378
- speclogician/state/state.py +183 -399
- speclogician/tui/box.tcss +10 -0
- speclogician/tui/tui.py +131 -0
- speclogician/utils/__init__.py +1 -70
- speclogician/utils/imx.py +195 -0
- speclogician/utils/load.py +25 -147
- speclogician/utils/prompt.md +1 -325
- speclogician-0.0.0.dev1.dist-info/METADATA +21 -0
- speclogician-0.0.0.dev1.dist-info/RECORD +43 -0
- speclogician/commands/__init__.py +0 -15
- speclogician/commands/cmd_ch.py +0 -616
- speclogician/commands/cmd_find.py +0 -256
- speclogician/commands/cmd_view.py +0 -202
- speclogician/commands/runner.py +0 -149
- speclogician/commands/utils.py +0 -101
- speclogician/demos/.DS_Store +0 -0
- speclogician/demos/cmd_demo.py +0 -278
- speclogician/demos/loader.py +0 -135
- speclogician/demos/model.py +0 -27
- speclogician/demos/runner.py +0 -51
- speclogician/logic/__init__.py +0 -11
- speclogician/logic/api/__init__.py +0 -29
- speclogician/logic/api/client.py +0 -606
- speclogician/logic/api/decomp.py +0 -67
- speclogician/logic/api/scenario.py +0 -102
- speclogician/logic/api/traces.py +0 -59
- speclogician/logic/lib/__init__.py +0 -19
- speclogician/logic/lib/complement.py +0 -107
- speclogician/logic/lib/domain_model.py +0 -59
- speclogician/logic/lib/predicates.py +0 -151
- speclogician/logic/lib/scenarios.py +0 -369
- speclogician/logic/lib/traces.py +0 -114
- speclogician/logic/lib/transitions.py +0 -104
- speclogician/logic/main.py +0 -246
- speclogician/logic/strings.py +0 -194
- speclogician/logic/utils.py +0 -135
- speclogician/modeling/complement.py +0 -104
- speclogician/modeling/spec.py +0 -306
- speclogician/modeling/spec_stats.py +0 -39
- speclogician/presentation/api.py +0 -244
- speclogician/presentation/builders/_links.py +0 -44
- speclogician/presentation/builders/container.py +0 -53
- speclogician/presentation/builders/data_artifact.py +0 -42
- speclogician/presentation/builders/domain.py +0 -54
- speclogician/presentation/builders/instances_list.py +0 -38
- speclogician/presentation/builders/predicate.py +0 -51
- speclogician/presentation/builders/recommendations.py +0 -41
- speclogician/presentation/builders/scenario.py +0 -41
- speclogician/presentation/builders/scenario_complement.py +0 -82
- speclogician/presentation/builders/smart_find.py +0 -39
- speclogician/presentation/builders/spec.py +0 -39
- speclogician/presentation/builders/state_diff.py +0 -150
- speclogician/presentation/builders/state_instance.py +0 -42
- speclogician/presentation/builders/state_instance_summary.py +0 -84
- speclogician/presentation/builders/trace.py +0 -58
- speclogician/presentation/ctx.py +0 -38
- speclogician/presentation/models/container.py +0 -44
- speclogician/presentation/models/data_artifact.py +0 -33
- speclogician/presentation/models/domain.py +0 -50
- speclogician/presentation/models/instances_list.py +0 -23
- speclogician/presentation/models/predicate.py +0 -60
- speclogician/presentation/models/recommendations.py +0 -34
- speclogician/presentation/models/scenario.py +0 -31
- speclogician/presentation/models/scenario_complement.py +0 -40
- speclogician/presentation/models/smart_find.py +0 -34
- speclogician/presentation/models/spec.py +0 -32
- speclogician/presentation/models/state_diff.py +0 -34
- speclogician/presentation/models/state_instance.py +0 -31
- speclogician/presentation/models/state_instance_summary.py +0 -102
- speclogician/presentation/models/trace.py +0 -42
- speclogician/presentation/preview/__init__.py +0 -13
- speclogician/presentation/preview/cli.py +0 -50
- speclogician/presentation/preview/fixtures/__init__.py +0 -205
- speclogician/presentation/preview/fixtures/artifact_container.py +0 -150
- speclogician/presentation/preview/fixtures/data_artifact.py +0 -144
- speclogician/presentation/preview/fixtures/domain_model.py +0 -162
- speclogician/presentation/preview/fixtures/instances_list.py +0 -162
- speclogician/presentation/preview/fixtures/predicate.py +0 -184
- speclogician/presentation/preview/fixtures/scenario.py +0 -84
- speclogician/presentation/preview/fixtures/scenario_complement.py +0 -81
- speclogician/presentation/preview/fixtures/smart_find.py +0 -140
- speclogician/presentation/preview/fixtures/spec.py +0 -95
- speclogician/presentation/preview/fixtures/state_diff.py +0 -158
- speclogician/presentation/preview/fixtures/state_instance.py +0 -128
- speclogician/presentation/preview/fixtures/state_instance_summary.py +0 -80
- speclogician/presentation/preview/fixtures/trace.py +0 -206
- speclogician/presentation/preview/registry.py +0 -42
- speclogician/presentation/renderers/__init__.py +0 -24
- speclogician/presentation/renderers/container.py +0 -136
- speclogician/presentation/renderers/data_artifact.py +0 -144
- speclogician/presentation/renderers/domain.py +0 -123
- speclogician/presentation/renderers/instances_list.py +0 -120
- speclogician/presentation/renderers/predicate.py +0 -180
- speclogician/presentation/renderers/recommendations.py +0 -90
- speclogician/presentation/renderers/scenario.py +0 -94
- speclogician/presentation/renderers/scenario_complement.py +0 -59
- speclogician/presentation/renderers/smart_find.py +0 -307
- speclogician/presentation/renderers/spec.py +0 -105
- speclogician/presentation/renderers/state_diff.py +0 -102
- speclogician/presentation/renderers/state_instance.py +0 -82
- speclogician/presentation/renderers/state_instance_summary.py +0 -143
- speclogician/presentation/renderers/trace.py +0 -122
- speclogician/shell/app.py +0 -170
- speclogician/shell/shell_ch.py +0 -263
- speclogician/shell/shell_view.py +0 -153
- speclogician/state/change_result.py +0 -32
- speclogician/state/diff.py +0 -191
- speclogician/state/inst.py +0 -574
- speclogician/state/recommendation.py +0 -13
- speclogician/state/recommender.py +0 -577
- speclogician/state/state_stats.py +0 -133
- speclogician/tui/__init__.py +0 -0
- speclogician/tui/app.py +0 -257
- speclogician/tui/app.tcss +0 -160
- speclogician/tui/demo.py +0 -45
- speclogician/tui/images/speclogician-full.png +0 -0
- speclogician/tui/images/speclogician-minimal.png +0 -0
- speclogician/tui/main_screen.py +0 -454
- speclogician/tui/splash_screen.py +0 -51
- speclogician/tui/stats_screen.py +0 -125
- speclogician/utils/testing.py +0 -151
- speclogician-0.0.0b1.dist-info/METADATA +0 -116
- speclogician-0.0.0b1.dist-info/RECORD +0 -139
- /speclogician/{presentation → agent}/__init__.py +0 -0
- /speclogician/{presentation/builders → cmd}/__init__.py +0 -0
- /speclogician/{presentation/models → llms}/__init__.py +0 -0
- {speclogician-0.0.0b1.dist-info → speclogician-0.0.0.dev1.dist-info}/WHEEL +0 -0
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/logic/api/scenario.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
import asyncio
|
|
8
|
-
from .client import SpecLogicianImandraX
|
|
9
|
-
from speclogician.modeling.scenario import Scenario
|
|
10
|
-
from speclogician.modeling.spec import Spec
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
async def check_state_preds_async(sc: Scenario, spec: Spec) -> bool:
|
|
14
|
-
if len(sc.given) == 0:
|
|
15
|
-
return True
|
|
16
|
-
|
|
17
|
-
async with SpecLogicianImandraX() as ix:
|
|
18
|
-
res = await ix.check_scenario_consistent(
|
|
19
|
-
domain_model=spec.domain_model,
|
|
20
|
-
scenario=sc,
|
|
21
|
-
check_state_preds=True,
|
|
22
|
-
check_action_preds=False
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
return res.is_consistent
|
|
26
|
-
|
|
27
|
-
def check_state_preds(sc: Scenario, spec: Spec) -> bool:
|
|
28
|
-
return asyncio.run(check_state_preds_async(sc, spec))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
async def check_action_preds_async(sc: Scenario, spec: Spec) -> bool:
|
|
32
|
-
if len(sc.when) == 0:
|
|
33
|
-
return True
|
|
34
|
-
|
|
35
|
-
async with SpecLogicianImandraX() as ix:
|
|
36
|
-
res = await ix.check_scenario_consistent(
|
|
37
|
-
domain_model=spec.domain_model,
|
|
38
|
-
scenario=sc,
|
|
39
|
-
check_state_preds=False,
|
|
40
|
-
check_action_preds=True
|
|
41
|
-
)
|
|
42
|
-
return res.is_consistent
|
|
43
|
-
|
|
44
|
-
def check_action_preds (sc: Scenario, spec: Spec) -> bool:
|
|
45
|
-
return asyncio.run(check_action_preds_async(sc, spec))
|
|
46
|
-
|
|
47
|
-
async def check_all_preds_async(sc : Scenario, spec : Spec) -> bool:
|
|
48
|
-
if len(sc.given) == 0 and len(sc.when) == 0:
|
|
49
|
-
return True
|
|
50
|
-
|
|
51
|
-
async with SpecLogicianImandraX() as ix:
|
|
52
|
-
res = await ix.check_scenario_consistent(
|
|
53
|
-
domain_model=spec.domain_model,
|
|
54
|
-
scenario=sc,
|
|
55
|
-
check_state_preds=True,
|
|
56
|
-
check_action_preds=True
|
|
57
|
-
)
|
|
58
|
-
return res.is_consistent
|
|
59
|
-
|
|
60
|
-
def check_all_preds (sc: Scenario, spec: Spec) -> bool:
|
|
61
|
-
return asyncio.run(check_all_preds_async(sc, spec))
|
|
62
|
-
|
|
63
|
-
def check_scenario(sc : Scenario, spec : Spec) -> None:
|
|
64
|
-
"""
|
|
65
|
-
Check if all the specified predicates/transitions are actually present in the domain model,
|
|
66
|
-
and if so, check that the predicates are consistent
|
|
67
|
-
"""
|
|
68
|
-
|
|
69
|
-
# First, let's check that all the predicates exist..
|
|
70
|
-
sc.preds_missing = []
|
|
71
|
-
for p in list(sc.given) + list(sc.when):
|
|
72
|
-
if not spec.domain_model.pred_exists(p):
|
|
73
|
-
sc.preds_missing.append(p)
|
|
74
|
-
|
|
75
|
-
sc.trans_missing = []
|
|
76
|
-
for t in sc.then:
|
|
77
|
-
if not spec.domain_model.trans_exists(t):
|
|
78
|
-
sc.trans_missing.append(t)
|
|
79
|
-
|
|
80
|
-
sc.given_preds_consistent = check_state_preds(sc, spec)
|
|
81
|
-
sc.when_preds_consistent = check_action_preds(sc, spec)
|
|
82
|
-
sc.all_preds_consistent = check_all_preds(sc, spec)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
async def check_intersection_async(sc1: Scenario, sc2: Scenario, spec: Spec) -> bool:
|
|
86
|
-
"""
|
|
87
|
-
Async: return True iff scenarios intersect (SAT).
|
|
88
|
-
"""
|
|
89
|
-
async with SpecLogicianImandraX() as ix:
|
|
90
|
-
res = await ix.check_scenarios_intersect(
|
|
91
|
-
domain_model=spec.domain_model,
|
|
92
|
-
sc_a=sc1,
|
|
93
|
-
sc_b=sc2,
|
|
94
|
-
)
|
|
95
|
-
return res.do_they_intersect
|
|
96
|
-
|
|
97
|
-
def check_intersection(sc1: Scenario, sc2: Scenario, spec: Spec) -> bool:
|
|
98
|
-
"""
|
|
99
|
-
Sync wrapper around `check_intersection_async`.
|
|
100
|
-
Returns True iff scenarios intersect (SAT).
|
|
101
|
-
"""
|
|
102
|
-
return asyncio.run(check_intersection_async(sc1, sc2, spec))
|
speclogician/logic/api/traces.py
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/logic/api/traces.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import asyncio
|
|
10
|
-
|
|
11
|
-
from .client import SpecLogicianImandraX
|
|
12
|
-
from speclogician.modeling.domain import DomainModel
|
|
13
|
-
from speclogician.modeling.scenario import Scenario
|
|
14
|
-
from speclogician.data.artifact import TraceArtifact, TraceIMLValidity, TraceScenarioMatchResult
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
async def check_trace_iml_validity_async(
|
|
18
|
-
tr: TraceArtifact,
|
|
19
|
-
model: DomainModel,
|
|
20
|
-
) -> TraceIMLValidity:
|
|
21
|
-
"""
|
|
22
|
-
Check that trace snippets typecheck against the domain model.
|
|
23
|
-
"""
|
|
24
|
-
async with SpecLogicianImandraX() as ix:
|
|
25
|
-
v = await ix.check_trace_iml_validity(tr=tr, model=model)
|
|
26
|
-
tr.iml_validity = v
|
|
27
|
-
return v
|
|
28
|
-
|
|
29
|
-
def check_trace_iml_validity (
|
|
30
|
-
tr: TraceArtifact,
|
|
31
|
-
model: DomainModel,
|
|
32
|
-
) -> TraceIMLValidity:
|
|
33
|
-
"""
|
|
34
|
-
Sync wrapper around check_trace_iml_validity_async.
|
|
35
|
-
"""
|
|
36
|
-
return asyncio.run(check_trace_iml_validity_async(tr, model))
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
async def check_trace_match_async(
|
|
40
|
-
tr: TraceArtifact,
|
|
41
|
-
s: Scenario,
|
|
42
|
-
model: DomainModel,
|
|
43
|
-
) -> TraceScenarioMatchResult:
|
|
44
|
-
"""
|
|
45
|
-
Check whether the trace matches a scenario (given/when predicates + transition result).
|
|
46
|
-
Assumes check_trace_iml_validity is either called inside or is handled by the async method.
|
|
47
|
-
"""
|
|
48
|
-
async with SpecLogicianImandraX() as ix:
|
|
49
|
-
return await ix.check_trace_match(tr=tr, s=s, model=model)
|
|
50
|
-
|
|
51
|
-
def check_trace_match(
|
|
52
|
-
tr: TraceArtifact,
|
|
53
|
-
s: Scenario,
|
|
54
|
-
model: DomainModel,
|
|
55
|
-
) -> TraceScenarioMatchResult:
|
|
56
|
-
"""
|
|
57
|
-
Sync wrapper around check_trace_match_async.
|
|
58
|
-
"""
|
|
59
|
-
return asyncio.run(check_trace_match_async(tr, s, model))
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/logic/lib/__init__.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from .domain_model import check_domain_model
|
|
8
|
-
from .predicates import check_predicates
|
|
9
|
-
from .transitions import check_transitions
|
|
10
|
-
from .scenarios import check_scenarios
|
|
11
|
-
from .traces import check_traces
|
|
12
|
-
|
|
13
|
-
__all__ = [
|
|
14
|
-
"check_domain_model",
|
|
15
|
-
"check_predicates",
|
|
16
|
-
"check_transitions",
|
|
17
|
-
"check_scenarios",
|
|
18
|
-
"check_traces",
|
|
19
|
-
]
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/logic/lib/complement.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import asyncio
|
|
10
|
-
from typing import Optional
|
|
11
|
-
|
|
12
|
-
from speclogician.modeling.domain import BaseStatus
|
|
13
|
-
from speclogician.modeling.complement import ScenarioComplement
|
|
14
|
-
from speclogician.modeling.spec import Spec
|
|
15
|
-
|
|
16
|
-
from ..api.decomp import decomp_complement_async
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _is_iml_invalid(x: object) -> bool:
|
|
20
|
-
"""
|
|
21
|
-
Best-effort check for IML invalidity without hard-coding exact enum imports.
|
|
22
|
-
Works with:
|
|
23
|
-
- IMLValidity.INVALID enum
|
|
24
|
-
- string/repr containing "IMLValidity.INVALID"
|
|
25
|
-
- literal "invalid"
|
|
26
|
-
"""
|
|
27
|
-
v = getattr(x, "is_iml_valid", None)
|
|
28
|
-
if v is None:
|
|
29
|
-
return False
|
|
30
|
-
|
|
31
|
-
# enum-style
|
|
32
|
-
try:
|
|
33
|
-
if getattr(v, "name", None) == "INVALID":
|
|
34
|
-
return True
|
|
35
|
-
except Exception:
|
|
36
|
-
pass
|
|
37
|
-
|
|
38
|
-
# string-style
|
|
39
|
-
s = str(v)
|
|
40
|
-
return (s == "invalid") or ("IMLValidity.INVALID" in s)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def _spec_is_decomposable_for_complement(spec: Spec) -> tuple[bool, str]:
|
|
44
|
-
dm = spec.domain_model
|
|
45
|
-
|
|
46
|
-
# 1) base must be good
|
|
47
|
-
if getattr(dm, "base_status", None) != BaseStatus.VALID:
|
|
48
|
-
return False, f"base_status={getattr(dm, 'base_status', None)}"
|
|
49
|
-
|
|
50
|
-
# 2) must have scenarios
|
|
51
|
-
scs = list(getattr(spec, "scenarios", []) or [])
|
|
52
|
-
if len(scs) == 0:
|
|
53
|
-
return False, "no_scenarios"
|
|
54
|
-
|
|
55
|
-
# 3) scenarios should be internally consistent (avoid wasting time)
|
|
56
|
-
if any(getattr(sc, "all_preds_consistent", True) is False for sc in scs):
|
|
57
|
-
return False, "has_inconsistent_scenarios"
|
|
58
|
-
|
|
59
|
-
# 4) avoid complement if any predicate/transition is IML-invalid
|
|
60
|
-
bad: list[tuple[str, str]] = []
|
|
61
|
-
|
|
62
|
-
for p in list(getattr(dm, "state_preds", [])) + list(getattr(dm, "action_preds", [])):
|
|
63
|
-
if _is_iml_invalid(p):
|
|
64
|
-
bad.append(("pred", getattr(p, "name", "<unknown>")))
|
|
65
|
-
|
|
66
|
-
for t in list(getattr(dm, "transitions", [])):
|
|
67
|
-
if _is_iml_invalid(t):
|
|
68
|
-
bad.append(("trans", getattr(t, "name", "<unknown>")))
|
|
69
|
-
|
|
70
|
-
if bad:
|
|
71
|
-
return False, f"iml_invalid={bad[:3]}{'...' if len(bad) > 3 else ''}"
|
|
72
|
-
|
|
73
|
-
return True, "ok"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def maybe_update_spec_complement(spec: Spec) -> Optional[ScenarioComplement]:
|
|
77
|
-
"""
|
|
78
|
-
Policy-level helper (SYNC):
|
|
79
|
-
|
|
80
|
-
- decides whether it is *appropriate* to compute complement
|
|
81
|
-
- if not appropriate, clears stale complement
|
|
82
|
-
- if appropriate, calls ImandraX via API-layer decomp and stores result
|
|
83
|
-
|
|
84
|
-
NOTE:
|
|
85
|
-
- This is intentionally sync to fit the state pipeline.
|
|
86
|
-
- It runs the async decomp using asyncio.run().
|
|
87
|
-
- If called inside an existing event loop, it will gracefully skip and
|
|
88
|
-
leave complement cleared (to avoid stale info).
|
|
89
|
-
"""
|
|
90
|
-
ok, _why = _spec_is_decomposable_for_complement(spec)
|
|
91
|
-
if not ok:
|
|
92
|
-
# Prevent stale coverage info from hanging around.
|
|
93
|
-
spec.scenario_comp = None
|
|
94
|
-
return None
|
|
95
|
-
|
|
96
|
-
try:
|
|
97
|
-
comp = asyncio.run(decomp_complement_async(spec))
|
|
98
|
-
except RuntimeError:
|
|
99
|
-
# "asyncio.run() cannot be called from a running event loop"
|
|
100
|
-
# This should be rare in CLI, but can happen in notebooks/tests.
|
|
101
|
-
# Prefer "clear rather than stale".
|
|
102
|
-
spec.scenario_comp = None
|
|
103
|
-
return None
|
|
104
|
-
|
|
105
|
-
spec.scenario_comp = comp
|
|
106
|
-
return comp
|
|
107
|
-
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/logic/lib/domain_model.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
import asyncio
|
|
8
|
-
|
|
9
|
-
from speclogician.modeling.domain import BaseStatus
|
|
10
|
-
from speclogician.modeling.spec import Spec
|
|
11
|
-
from speclogician.logic.api import SpecLogicianImandraX
|
|
12
|
-
from speclogician.utils import console
|
|
13
|
-
|
|
14
|
-
def check_domain_model(spec: Spec, json_only: bool = False) -> None:
|
|
15
|
-
"""
|
|
16
|
-
Updates:
|
|
17
|
-
- spec.domain_model.base_has_state
|
|
18
|
-
- spec.domain_model.base_has_action
|
|
19
|
-
- spec.domain_model.base_status (via ImandraX)
|
|
20
|
-
"""
|
|
21
|
-
dm = spec.domain_model
|
|
22
|
-
|
|
23
|
-
if not json_only:
|
|
24
|
-
console.print("[bold cyan]Logician[/bold cyan] Checking domain model base")
|
|
25
|
-
|
|
26
|
-
# Local checks
|
|
27
|
-
dm.base_has_state = dm.state_specified()
|
|
28
|
-
dm.base_has_action = dm.action_specified()
|
|
29
|
-
|
|
30
|
-
if not json_only:
|
|
31
|
-
console.print(
|
|
32
|
-
f" • has state type : {'✓' if dm.base_has_state else '✗'}\n"
|
|
33
|
-
f" • has action type: {'✓' if dm.base_has_action else '✗'}"
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
# Build "base-only" model and ask ImandraX to load/typecheck it
|
|
37
|
-
base_src = dm.make_iml_model([], [], [])
|
|
38
|
-
|
|
39
|
-
async def _check() -> bool:
|
|
40
|
-
async with SpecLogicianImandraX() as ix:
|
|
41
|
-
res = await ix.load_model(base_src)
|
|
42
|
-
return not bool(getattr(res, "has_errors", False))
|
|
43
|
-
|
|
44
|
-
try:
|
|
45
|
-
ok = asyncio.run(_check())
|
|
46
|
-
except Exception as e:
|
|
47
|
-
dm.base_status = BaseStatus.UNKNOWN
|
|
48
|
-
if not json_only:
|
|
49
|
-
console.print("[yellow]⚠ Unable to check base model via ImandraX[/yellow]")
|
|
50
|
-
console.print(f"[dim]{e}[/dim]")
|
|
51
|
-
return
|
|
52
|
-
|
|
53
|
-
dm.base_status = BaseStatus.VALID if ok else BaseStatus.INVALID_IML
|
|
54
|
-
|
|
55
|
-
if not json_only:
|
|
56
|
-
if dm.base_status is BaseStatus.VALID:
|
|
57
|
-
console.print("[green]✓ Base domain model is valid IML[/green]")
|
|
58
|
-
else:
|
|
59
|
-
console.print("[red]✗ Base domain model is invalid IML[/red]")
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/logic/lib/predicates.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
import asyncio
|
|
8
|
-
|
|
9
|
-
from rich.progress import Progress
|
|
10
|
-
|
|
11
|
-
from speclogician.modeling.spec import Spec
|
|
12
|
-
from speclogician.logic.api import SpecLogicianImandraX
|
|
13
|
-
from speclogician.utils import IMLValidity, console
|
|
14
|
-
|
|
15
|
-
def check_predicates(
|
|
16
|
-
spec: Spec,
|
|
17
|
-
names: list[str] = [],
|
|
18
|
-
json_only: bool = False,
|
|
19
|
-
) -> None:
|
|
20
|
-
"""
|
|
21
|
-
Check predicate IML validity via ImandraX and write results into predicate objects:
|
|
22
|
-
- predicate.is_iml_valid
|
|
23
|
-
- predicate.iml_error
|
|
24
|
-
If `names` is non-empty, checks only those predicate names; otherwise checks all.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
dm = spec.domain_model
|
|
28
|
-
|
|
29
|
-
# Resolve which predicates to check (by name), across state+action predicates.
|
|
30
|
-
all_state = list(dm.state_preds)
|
|
31
|
-
all_action = list(dm.action_preds)
|
|
32
|
-
|
|
33
|
-
if names:
|
|
34
|
-
name_set = set(names)
|
|
35
|
-
state_to_check = [p for p in all_state if p.name in name_set]
|
|
36
|
-
action_to_check = [p for p in all_action if p.name in name_set]
|
|
37
|
-
|
|
38
|
-
# If a name was requested but doesn't exist, we can't attach the result to a predicate.
|
|
39
|
-
# We *log* it (non-json) but do not raise.
|
|
40
|
-
if not json_only:
|
|
41
|
-
known = {p.name for p in all_state} | {p.name for p in all_action}
|
|
42
|
-
missing = [n for n in names if n not in known]
|
|
43
|
-
if missing:
|
|
44
|
-
console.print(f"[yellow]Logician[/yellow] Unknown predicate name(s) requested: {missing}")
|
|
45
|
-
else:
|
|
46
|
-
state_to_check = all_state
|
|
47
|
-
action_to_check = all_action
|
|
48
|
-
|
|
49
|
-
# Helper: stringify ImandraX eval_model errors (best effort)
|
|
50
|
-
def _errs_to_str(res: object) -> str:
|
|
51
|
-
es = getattr(res, "errors", None) or []
|
|
52
|
-
if es:
|
|
53
|
-
return "\n".join(str(e) for e in es)
|
|
54
|
-
return "ImandraX eval_model: has_errors=True"
|
|
55
|
-
|
|
56
|
-
async def _check_all() -> None:
|
|
57
|
-
async with SpecLogicianImandraX() as ix:
|
|
58
|
-
# --- State predicates ---
|
|
59
|
-
for p in state_to_check:
|
|
60
|
-
src = dm.make_iml_model([p.name], [], [])
|
|
61
|
-
try:
|
|
62
|
-
r = await ix.load_model(src)
|
|
63
|
-
has_err = bool(getattr(r, "has_errors", False))
|
|
64
|
-
p.is_iml_valid = IMLValidity.INVALID if has_err else IMLValidity.VALID
|
|
65
|
-
p.iml_error = None if not has_err else _errs_to_str(r)
|
|
66
|
-
except Exception as e:
|
|
67
|
-
p.is_iml_valid = IMLValidity.UNKNOWN
|
|
68
|
-
p.iml_error = str(e)
|
|
69
|
-
|
|
70
|
-
# --- Action predicates ---
|
|
71
|
-
for p in action_to_check:
|
|
72
|
-
src = dm.make_iml_model([], [p.name], [])
|
|
73
|
-
try:
|
|
74
|
-
r = await ix.load_model(src)
|
|
75
|
-
has_err = bool(getattr(r, "has_errors", False))
|
|
76
|
-
p.is_iml_valid = IMLValidity.INVALID if has_err else IMLValidity.VALID
|
|
77
|
-
p.iml_error = None if not has_err else _errs_to_str(r)
|
|
78
|
-
except Exception as e:
|
|
79
|
-
p.is_iml_valid = IMLValidity.UNKNOWN
|
|
80
|
-
p.iml_error = str(e)
|
|
81
|
-
|
|
82
|
-
if not json_only:
|
|
83
|
-
console.print("[bold cyan]Logician[/bold cyan] Checking predicate IML validity")
|
|
84
|
-
|
|
85
|
-
# Show progress only in non-json mode
|
|
86
|
-
if not json_only:
|
|
87
|
-
with Progress(transient=True) as progress:
|
|
88
|
-
t_state = progress.add_task("Checking state predicates", total=len(state_to_check))
|
|
89
|
-
t_action = progress.add_task("Checking action predicates", total=len(action_to_check))
|
|
90
|
-
|
|
91
|
-
# We still do the actual work in one ImandraX session; progress is updated as we go.
|
|
92
|
-
async def _check_with_progress() -> None:
|
|
93
|
-
async with SpecLogicianImandraX() as ix:
|
|
94
|
-
for p in state_to_check:
|
|
95
|
-
src = dm.make_iml_model([p.name], [], [])
|
|
96
|
-
try:
|
|
97
|
-
r = await ix.load_model(src)
|
|
98
|
-
has_err = bool(getattr(r, "has_errors", False))
|
|
99
|
-
p.is_iml_valid = IMLValidity.INVALID if has_err else IMLValidity.VALID
|
|
100
|
-
p.iml_error = None if not has_err else _errs_to_str(r)
|
|
101
|
-
except Exception as e:
|
|
102
|
-
p.is_iml_valid = IMLValidity.UNKNOWN
|
|
103
|
-
p.iml_error = str(e)
|
|
104
|
-
progress.advance(t_state)
|
|
105
|
-
|
|
106
|
-
for p in action_to_check:
|
|
107
|
-
src = dm.make_iml_model([], [p.name], [])
|
|
108
|
-
try:
|
|
109
|
-
r = await ix.load_model(src)
|
|
110
|
-
has_err = bool(getattr(r, "has_errors", False))
|
|
111
|
-
p.is_iml_valid = IMLValidity.INVALID if has_err else IMLValidity.VALID
|
|
112
|
-
p.iml_error = None if not has_err else _errs_to_str(r)
|
|
113
|
-
except Exception as e:
|
|
114
|
-
p.is_iml_valid = IMLValidity.UNKNOWN
|
|
115
|
-
p.iml_error = str(e)
|
|
116
|
-
progress.advance(t_action)
|
|
117
|
-
|
|
118
|
-
try:
|
|
119
|
-
asyncio.run(_check_with_progress())
|
|
120
|
-
except Exception as e:
|
|
121
|
-
# If the whole checking run fails (e.g. credentials/network),
|
|
122
|
-
# we don't have per-predicate errors; mark checked ones as unknown.
|
|
123
|
-
for p in state_to_check + action_to_check:
|
|
124
|
-
p.is_iml_valid = IMLValidity.UNKNOWN
|
|
125
|
-
p.iml_error = str(e)
|
|
126
|
-
else:
|
|
127
|
-
# No progress bar, just run.
|
|
128
|
-
try:
|
|
129
|
-
asyncio.run(_check_all())
|
|
130
|
-
except Exception as e:
|
|
131
|
-
for p in state_to_check + action_to_check:
|
|
132
|
-
p.is_iml_valid = IMLValidity.UNKNOWN
|
|
133
|
-
p.iml_error = str(e)
|
|
134
|
-
|
|
135
|
-
# Refresh domain-model predicate stats (global, not just filtered set)
|
|
136
|
-
dm.num_state_preds_total = len(all_state)
|
|
137
|
-
dm.num_action_preds_total = len(all_action)
|
|
138
|
-
|
|
139
|
-
dm.num_state_preds_valid_logic = sum(1 for p in all_state if getattr(p, "is_iml_valid", IMLValidity.UNKNOWN) == IMLValidity.VALID)
|
|
140
|
-
dm.num_action_preds_valid_logic = sum(1 for p in all_action if getattr(p, "is_iml_valid", IMLValidity.UNKNOWN) == IMLValidity.VALID)
|
|
141
|
-
|
|
142
|
-
dm.num_preds_total = dm.num_state_preds_total + dm.num_action_preds_total
|
|
143
|
-
dm.num_preds_valid_logic = dm.num_state_preds_valid_logic + dm.num_action_preds_valid_logic
|
|
144
|
-
|
|
145
|
-
if not json_only:
|
|
146
|
-
console.print(
|
|
147
|
-
f"[green]✓[/green] Predicates checked. "
|
|
148
|
-
f"state_valid={dm.num_state_preds_valid_logic}/{dm.num_state_preds_total}, "
|
|
149
|
-
f"action_valid={dm.num_action_preds_valid_logic}/{dm.num_action_preds_total}"
|
|
150
|
-
)
|
|
151
|
-
|