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.
Files changed (153) hide show
  1. speclogician/agent/funcs.py +29 -0
  2. speclogician/cmd/agent_cmd.py +89 -0
  3. speclogician/cmd/data_cmd.py +24 -0
  4. speclogician/cmd/model_cmd.py +42 -0
  5. speclogician/cmd/overlay_cmd.py +30 -0
  6. speclogician/cmd/scenario_cmd.py +61 -0
  7. speclogician/cmd/state_cmd.py +52 -0
  8. speclogician/data/artifact.py +8 -50
  9. speclogician/data/container.py +18 -384
  10. speclogician/data/mapping.py +18 -17
  11. speclogician/data/refs.py +12 -11
  12. speclogician/data/reports.py +11 -0
  13. speclogician/data/traces.py +15 -6
  14. speclogician/llms/llmtools.py +102 -0
  15. speclogician/llms/overlay.py +264 -0
  16. speclogician/main.py +36 -102
  17. speclogician/modeling/__init__.py +0 -31
  18. speclogician/modeling/component.py +4 -60
  19. speclogician/modeling/conflict.py +5 -19
  20. speclogician/modeling/domain.py +93 -280
  21. speclogician/modeling/model.py +206 -0
  22. speclogician/modeling/predicates.py +20 -22
  23. speclogician/modeling/report.py +33 -0
  24. speclogician/modeling/scenario.py +119 -87
  25. speclogician/sl_cmd.py +76 -0
  26. speclogician/state/change.py +98 -378
  27. speclogician/state/state.py +183 -399
  28. speclogician/tui/box.tcss +10 -0
  29. speclogician/tui/tui.py +131 -0
  30. speclogician/utils/__init__.py +1 -70
  31. speclogician/utils/imx.py +195 -0
  32. speclogician/utils/load.py +25 -147
  33. speclogician/utils/prompt.md +1 -325
  34. speclogician-0.0.0.dev1.dist-info/METADATA +21 -0
  35. speclogician-0.0.0.dev1.dist-info/RECORD +43 -0
  36. speclogician/commands/__init__.py +0 -15
  37. speclogician/commands/cmd_ch.py +0 -616
  38. speclogician/commands/cmd_find.py +0 -256
  39. speclogician/commands/cmd_view.py +0 -202
  40. speclogician/commands/runner.py +0 -149
  41. speclogician/commands/utils.py +0 -101
  42. speclogician/demos/.DS_Store +0 -0
  43. speclogician/demos/cmd_demo.py +0 -278
  44. speclogician/demos/loader.py +0 -135
  45. speclogician/demos/model.py +0 -27
  46. speclogician/demos/runner.py +0 -51
  47. speclogician/logic/__init__.py +0 -11
  48. speclogician/logic/api/__init__.py +0 -29
  49. speclogician/logic/api/client.py +0 -606
  50. speclogician/logic/api/decomp.py +0 -67
  51. speclogician/logic/api/scenario.py +0 -102
  52. speclogician/logic/api/traces.py +0 -59
  53. speclogician/logic/lib/__init__.py +0 -19
  54. speclogician/logic/lib/complement.py +0 -107
  55. speclogician/logic/lib/domain_model.py +0 -59
  56. speclogician/logic/lib/predicates.py +0 -151
  57. speclogician/logic/lib/scenarios.py +0 -369
  58. speclogician/logic/lib/traces.py +0 -114
  59. speclogician/logic/lib/transitions.py +0 -104
  60. speclogician/logic/main.py +0 -246
  61. speclogician/logic/strings.py +0 -194
  62. speclogician/logic/utils.py +0 -135
  63. speclogician/modeling/complement.py +0 -104
  64. speclogician/modeling/spec.py +0 -306
  65. speclogician/modeling/spec_stats.py +0 -39
  66. speclogician/presentation/api.py +0 -244
  67. speclogician/presentation/builders/_links.py +0 -44
  68. speclogician/presentation/builders/container.py +0 -53
  69. speclogician/presentation/builders/data_artifact.py +0 -42
  70. speclogician/presentation/builders/domain.py +0 -54
  71. speclogician/presentation/builders/instances_list.py +0 -38
  72. speclogician/presentation/builders/predicate.py +0 -51
  73. speclogician/presentation/builders/recommendations.py +0 -41
  74. speclogician/presentation/builders/scenario.py +0 -41
  75. speclogician/presentation/builders/scenario_complement.py +0 -82
  76. speclogician/presentation/builders/smart_find.py +0 -39
  77. speclogician/presentation/builders/spec.py +0 -39
  78. speclogician/presentation/builders/state_diff.py +0 -150
  79. speclogician/presentation/builders/state_instance.py +0 -42
  80. speclogician/presentation/builders/state_instance_summary.py +0 -84
  81. speclogician/presentation/builders/trace.py +0 -58
  82. speclogician/presentation/ctx.py +0 -38
  83. speclogician/presentation/models/container.py +0 -44
  84. speclogician/presentation/models/data_artifact.py +0 -33
  85. speclogician/presentation/models/domain.py +0 -50
  86. speclogician/presentation/models/instances_list.py +0 -23
  87. speclogician/presentation/models/predicate.py +0 -60
  88. speclogician/presentation/models/recommendations.py +0 -34
  89. speclogician/presentation/models/scenario.py +0 -31
  90. speclogician/presentation/models/scenario_complement.py +0 -40
  91. speclogician/presentation/models/smart_find.py +0 -34
  92. speclogician/presentation/models/spec.py +0 -32
  93. speclogician/presentation/models/state_diff.py +0 -34
  94. speclogician/presentation/models/state_instance.py +0 -31
  95. speclogician/presentation/models/state_instance_summary.py +0 -102
  96. speclogician/presentation/models/trace.py +0 -42
  97. speclogician/presentation/preview/__init__.py +0 -13
  98. speclogician/presentation/preview/cli.py +0 -50
  99. speclogician/presentation/preview/fixtures/__init__.py +0 -205
  100. speclogician/presentation/preview/fixtures/artifact_container.py +0 -150
  101. speclogician/presentation/preview/fixtures/data_artifact.py +0 -144
  102. speclogician/presentation/preview/fixtures/domain_model.py +0 -162
  103. speclogician/presentation/preview/fixtures/instances_list.py +0 -162
  104. speclogician/presentation/preview/fixtures/predicate.py +0 -184
  105. speclogician/presentation/preview/fixtures/scenario.py +0 -84
  106. speclogician/presentation/preview/fixtures/scenario_complement.py +0 -81
  107. speclogician/presentation/preview/fixtures/smart_find.py +0 -140
  108. speclogician/presentation/preview/fixtures/spec.py +0 -95
  109. speclogician/presentation/preview/fixtures/state_diff.py +0 -158
  110. speclogician/presentation/preview/fixtures/state_instance.py +0 -128
  111. speclogician/presentation/preview/fixtures/state_instance_summary.py +0 -80
  112. speclogician/presentation/preview/fixtures/trace.py +0 -206
  113. speclogician/presentation/preview/registry.py +0 -42
  114. speclogician/presentation/renderers/__init__.py +0 -24
  115. speclogician/presentation/renderers/container.py +0 -136
  116. speclogician/presentation/renderers/data_artifact.py +0 -144
  117. speclogician/presentation/renderers/domain.py +0 -123
  118. speclogician/presentation/renderers/instances_list.py +0 -120
  119. speclogician/presentation/renderers/predicate.py +0 -180
  120. speclogician/presentation/renderers/recommendations.py +0 -90
  121. speclogician/presentation/renderers/scenario.py +0 -94
  122. speclogician/presentation/renderers/scenario_complement.py +0 -59
  123. speclogician/presentation/renderers/smart_find.py +0 -307
  124. speclogician/presentation/renderers/spec.py +0 -105
  125. speclogician/presentation/renderers/state_diff.py +0 -102
  126. speclogician/presentation/renderers/state_instance.py +0 -82
  127. speclogician/presentation/renderers/state_instance_summary.py +0 -143
  128. speclogician/presentation/renderers/trace.py +0 -122
  129. speclogician/shell/app.py +0 -170
  130. speclogician/shell/shell_ch.py +0 -263
  131. speclogician/shell/shell_view.py +0 -153
  132. speclogician/state/change_result.py +0 -32
  133. speclogician/state/diff.py +0 -191
  134. speclogician/state/inst.py +0 -574
  135. speclogician/state/recommendation.py +0 -13
  136. speclogician/state/recommender.py +0 -577
  137. speclogician/state/state_stats.py +0 -133
  138. speclogician/tui/__init__.py +0 -0
  139. speclogician/tui/app.py +0 -257
  140. speclogician/tui/app.tcss +0 -160
  141. speclogician/tui/demo.py +0 -45
  142. speclogician/tui/images/speclogician-full.png +0 -0
  143. speclogician/tui/images/speclogician-minimal.png +0 -0
  144. speclogician/tui/main_screen.py +0 -454
  145. speclogician/tui/splash_screen.py +0 -51
  146. speclogician/tui/stats_screen.py +0 -125
  147. speclogician/utils/testing.py +0 -151
  148. speclogician-0.0.0b1.dist-info/METADATA +0 -116
  149. speclogician-0.0.0b1.dist-info/RECORD +0 -139
  150. /speclogician/{presentation → agent}/__init__.py +0 -0
  151. /speclogician/{presentation/builders → cmd}/__init__.py +0 -0
  152. /speclogician/{presentation/models → llms}/__init__.py +0 -0
  153. {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))
@@ -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
-