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
@@ -0,0 +1,264 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # overlay.py
5
+ #
6
+
7
+
8
+ import yaml, os
9
+ from pydantic import BaseModel
10
+ from rich import print
11
+ from rich.console import Console
12
+ from typing import List, Optional
13
+ from rich.panel import Panel
14
+ from rich.table import Table
15
+ from rich.syntax import Syntax
16
+ from rich.markdown import Markdown
17
+ from rich.console import RenderableType
18
+
19
+ # ----------------------------
20
+ # Example field
21
+ # ----------------------------
22
+
23
+ class Example(BaseModel):
24
+ source: str # original snippet in the source DSL/language
25
+ iml: str # expected IML output
26
+
27
+
28
+ # ----------------------------
29
+ # Mapping section
30
+ # ----------------------------
31
+
32
+ class TitleMapping(BaseModel):
33
+ type: str
34
+ pattern: str
35
+ extract: int
36
+
37
+
38
+ class SectionMapping(BaseModel):
39
+ keywords: Optional[List[str]] = None
40
+ chained_keywords: Optional[List[str]] = None
41
+ section: Optional[str] = None
42
+ transform: Optional[str] = None
43
+
44
+
45
+ class Mapping(BaseModel):
46
+ title: TitleMapping
47
+ given: SectionMapping
48
+ when: SectionMapping
49
+ then: SectionMapping
50
+
51
+
52
+ # ----------------------------
53
+ # Background
54
+ # ----------------------------
55
+
56
+ class Background(BaseModel):
57
+ enabled: bool
58
+ keywords: List[str]
59
+ given_keywords: List[str]
60
+
61
+
62
+ # ----------------------------
63
+ # State configuration
64
+ # ----------------------------
65
+
66
+ class StateConfig(BaseModel):
67
+ initial_name: str
68
+ transition_prefix: str
69
+
70
+
71
+ # ----------------------------
72
+ # Rules and constraints
73
+ # ----------------------------
74
+
75
+ class Rules(BaseModel):
76
+ state_transitions: List[str]
77
+ predicates: List[str]
78
+
79
+
80
+ class YamlConstraints(BaseModel):
81
+ double_quote_strings: bool
82
+ escape_quotes: bool
83
+
84
+
85
+ # ----------------------------
86
+ # MAIN OVERLAY MODEL
87
+ # ----------------------------
88
+
89
+ class Overlay(BaseModel):
90
+ language: str
91
+ name: str
92
+ description: str
93
+ ext : str
94
+
95
+ mapping: Mapping
96
+ background: Background
97
+ state: StateConfig
98
+ rules: Rules
99
+ yaml_constraints: YamlConstraints
100
+
101
+ notes: List[str]
102
+
103
+ # NEW: structured example consisting of source + IML
104
+ example: Optional[Example] = None
105
+
106
+ def to_yaml (self):
107
+ data = self.model_dump() # Pydantic → dict
108
+ return yaml.safe_dump(data, sort_keys=False)
109
+
110
+ # --------------------------------------------------
111
+ # Rich pretty-printer
112
+ # --------------------------------------------------
113
+ def __rich__(self) -> RenderableType:
114
+ root = Table.grid(padding=1)
115
+ root.title = f"[bold cyan]Overlay ({self.language})[/bold cyan]"
116
+
117
+ # Metadata
118
+ meta = Table(show_header=False, box=None)
119
+ meta.add_row("[bold]Name[/bold]", self.name)
120
+ meta.add_row("[bold]Language[/bold]", self.language)
121
+ meta.add_row("[bold]Description[/bold]", self.description)
122
+ root.add_row(Panel(meta, title="Metadata", border_style="cyan"))
123
+
124
+ # Mapping
125
+ map_table = Table(show_header=True, header_style="bold magenta")
126
+ map_table.add_column("Section")
127
+ map_table.add_column("Keywords")
128
+ map_table.add_column("Chained")
129
+ map_table.add_column("Transform")
130
+
131
+ def fmt(xs):
132
+ return ", ".join(xs) if xs else ""
133
+
134
+ map_table.add_row(
135
+ "Title",
136
+ f"{self.mapping.title.type} / {self.mapping.title.pattern}",
137
+ "",
138
+ f"extract={self.mapping.title.extract}",
139
+ )
140
+
141
+ for name, sec in [
142
+ ("Given", self.mapping.given),
143
+ ("When", self.mapping.when),
144
+ ("Then", self.mapping.then),
145
+ ]:
146
+ map_table.add_row(
147
+ name,
148
+ fmt(sec.keywords),
149
+ fmt(sec.chained_keywords),
150
+ sec.transform or "",
151
+ )
152
+
153
+ root.add_row(Panel(map_table, title="Mapping", border_style="magenta"))
154
+
155
+ # Background
156
+ bg = Table(show_header=False, box=None)
157
+ bg.add_row("[bold]Enabled[/bold]", str(self.background.enabled))
158
+ bg.add_row("[bold]Keywords[/bold]", fmt(self.background.keywords))
159
+ bg.add_row("[bold]Given Keywords[/bold]", fmt(self.background.given_keywords))
160
+ root.add_row(Panel(bg, title="Background", border_style="green"))
161
+
162
+ # State Config
163
+ st = Table(show_header=False, box=None)
164
+ st.add_row("[bold]Initial[/bold]", self.state.initial_name)
165
+ st.add_row("[bold]Transition Prefix[/bold]", self.state.transition_prefix)
166
+ root.add_row(Panel(st, title="State Config", border_style="yellow"))
167
+
168
+ # Rules
169
+ rules_t = Table(show_header=True, header_style="bold blue")
170
+ rules_t.add_column("State Transitions")
171
+ rules_t.add_column("Predicates")
172
+
173
+ max_len = max(len(self.rules.state_transitions), len(self.rules.predicates))
174
+ for i in range(max_len):
175
+ left = self.rules.state_transitions[i] if i < len(self.rules.state_transitions) else ""
176
+ right = self.rules.predicates[i] if i < len(self.rules.predicates) else ""
177
+ rules_t.add_row(left, right)
178
+
179
+ root.add_row(Panel(rules_t, title="Rules", border_style="blue"))
180
+
181
+ # YAML Constraints
182
+ yc = Table(show_header=False, box=None)
183
+ yc.add_row("[bold]Double Quote Strings[/bold]", str(self.yaml_constraints.double_quote_strings))
184
+ yc.add_row("[bold]Escape Quotes[/bold]", str(self.yaml_constraints.escape_quotes))
185
+ root.add_row(Panel(yc, title="YAML Constraints", border_style="red"))
186
+
187
+ # Notes
188
+ notes_md = Markdown("\n".join(f"- {n}" for n in self.notes))
189
+ root.add_row(Panel(notes_md, title="Notes", border_style="white"))
190
+
191
+ # Structured Example
192
+ if self.example:
193
+ # Original source language
194
+ source_block = Syntax(
195
+ self.example.source,
196
+ self.language, # use overlay.language for syntax highlighting
197
+ theme="monokai",
198
+ line_numbers=False,
199
+ )
200
+ root.add_row(Panel(source_block, title="Example (Source)", border_style="bright_magenta"))
201
+
202
+ # IML output
203
+ iml_block = Syntax(
204
+ self.example.iml,
205
+ "ocaml", # Imandra IML is OCaml-like
206
+ theme="monokai",
207
+ line_numbers=False,
208
+ )
209
+ root.add_row(Panel(iml_block, title="Example (IML)", border_style="bright_green"))
210
+
211
+ return Panel(root, border_style="bright_white")
212
+
213
+ @staticmethod
214
+ def from_file(path:str) -> 'Overlay':
215
+ """Load an overlay YAML file and parse it into the Overlay model."""
216
+ with open(path, "r", encoding="utf-8") as f:
217
+ data = yaml.safe_load(f)
218
+
219
+ overlay : Overlay = Overlay.model_validate(data)
220
+ return overlay
221
+
222
+ class Overlays(BaseModel):
223
+ """
224
+ """
225
+ overlays : list[Overlay]
226
+
227
+ def list_overlays(self):
228
+ for o in self.overlays:
229
+ print ('---' * 10)
230
+ print (f"Name: {o.name}")
231
+ print (f"Language: {o.language}")
232
+ print (f"Description: {o.description}")
233
+
234
+ def names(self) -> list[str]:
235
+ return list(map(lambda x: x.name, self.overlays))
236
+
237
+ def get(self, name:str) -> Overlay|None:
238
+ if name in self.names():
239
+ return next(filter(lambda x: x.name == name, self.overlays))
240
+ else:
241
+ return None
242
+
243
+ @staticmethod
244
+ def from_dir(dir_path:str) -> 'Overlays':
245
+ overlays : list[Overlay] = []
246
+ for file in os.listdir(dir_path):
247
+ if file.endswith('.yaml'):
248
+ overlays.append(Overlay.from_file(os.path.join(dir_path, file)))
249
+
250
+ return Overlays(overlays=overlays)
251
+
252
+
253
+ if __name__ == "__main__":
254
+ import sys
255
+
256
+ if len(sys.argv) < 2:
257
+ print("[red]Usage: python load_overlay.py <overlay.yaml>[/red]")
258
+ sys.exit(1)
259
+
260
+ overlay_file = sys.argv[1]
261
+ overlay = Overlay.from_file(overlay_file)
262
+
263
+ console = Console()
264
+ console.print(overlay)
speclogician/main.py CHANGED
@@ -1,47 +1,26 @@
1
1
  #
2
2
  # Imandra Inc.
3
3
  #
4
- # speclogician/main.py
4
+ # main.py
5
5
  #
6
6
 
7
- __version__ = "1.0"
8
-
9
7
  import typer
8
+ import os
10
9
  from typing import Final
11
10
  from pathlib import Path
12
11
 
13
- from speclogician.commands import ch_app, view_app, find_app
14
- from speclogician.presentation.preview import preview_app
15
- from speclogician.demos.cmd_demo import app as demo_app
16
- from speclogician.utils import console, require_imandra_key
17
- from speclogician.utils.load import load_state
18
- from speclogician.state.state import State
19
-
20
-
21
- SL_HELP : Final = """
22
- :robot: [bold deep_sky_blue1][italic]SpecLogician[/italic] is an AI framework for data-driven
23
- formal program specification synthesis, verification, and analysis.[/bold deep_sky_blue1] :rocket:
24
-
25
- SpecLogician provides an LLM-native alternative to classical predicate abstraction and
26
- Counterexample-Guided Abstraction Refinement (CEGAR).
12
+ from speclogician.cmd.agent_cmd import app as agent_app
13
+ from speclogician.cmd.data_cmd import app as data_app
14
+ from speclogician.cmd.model_cmd import app as model_app
15
+ from speclogician.cmd.overlay_cmd import app as overlay_app
16
+ from speclogician.cmd.scenario_cmd import app as scenario_app
17
+ from speclogician.cmd.state_cmd import app as state_app
27
18
 
28
- Instead of requiring users or agents to discover and maintain global Boolean abstractions,
29
- SpecLogician models behavior directly using structured scenarios composed from state and
30
- action predicates. Specifications are built incrementally by defining a domain base
31
- (types and core invariants), predicates, transition functions, and scenarios that capture
32
- intent explicitly.
19
+ from speclogician.utils import console
33
20
 
34
- Each change creates a new immutable state, triggers precise logical analysis, and produces
35
- a semantic diff against the previous state. Using ImandraX’s exact symbolic reasoning and
36
- region decomposition, SpecLogician computes coverage, conflicts, reachability, and
37
- behavioral complements—identifying real, reachable behaviors not yet explained by any
38
- scenario.
39
21
 
40
- Rather than reacting to spurious counterexamples, users and AI agents receive positive,
41
- actionable refinement signals in the form of uncovered or inconsistent behavioral regions.
42
- This enables scalable, explainable refinement toward correctness and completeness across
43
- source code, tests, documentation, and real-world traces—using small, explicit change
44
- commands instead of manual file editing.
22
+ SL_HELP : Final = """
23
+ :robot: [bold deep_sky_blue1][italic]SpecLogician[/italic] is an AI framework for data-driven formal program specification synthesis, verification and analysis.[/bold deep_sky_blue1] :rocket:
45
24
 
46
25
  Learn more at [bold italic deep_sky_blue1]https://www.speclogician.dev![/bold italic deep_sky_blue1]
47
26
  """
@@ -49,91 +28,46 @@ Learn more at [bold italic deep_sky_blue1]https://www.speclogician.dev![/bold it
49
28
  app = typer.Typer(
50
29
  name="SpecLogician",
51
30
  help=SL_HELP,
52
- rich_markup_mode="rich",
53
- no_args_is_help=True
31
+ rich_markup_mode="rich"
54
32
  )
55
33
 
56
- class AppCtx:
57
- state_json_path: Path | None = None
58
- state: State | None = None
59
-
60
- @app.callback()
61
- def _cli_entry() -> None:
62
- """
63
- SpecLogician CLI entry point.
64
- """
65
- require_imandra_key()
66
-
67
- @app.callback()
68
- def main(ctx: typer.Context, state_json: Path | None = typer.Option(None, "--state-json", help="...")):
69
- ctx.obj = ctx.ensure_object(AppCtx)
70
-
71
- ctx.obj.state_json_path = state_json
72
-
73
- if state_json:
74
- try:
75
- ctx.obj.state = load_state(path=state_json)
76
- except Exception:
77
- ctx.obj.state = None
78
- else:
79
- ctx.obj.state = None # ✅ ensure attribute exists
80
-
81
- app.add_typer(view_app , name="view" , help="View components of the state")
82
- app.add_typer(ch_app , name="ch" , help="Apply change to the state")
83
- app.add_typer(find_app , name="find" , help="Search for a components of the state")
84
- app.add_typer(demo_app , name="demo" , help="A collection of SpecLogician demos")
85
- app.add_typer(preview_app , name="preview" , hidden=True) # This is an internal tool for manually inspecting renderers
86
-
34
+ app.add_typer(agent_app , name="agent" , help="Agentic functions")
35
+ app.add_typer(overlay_app , name="overlay" , help="Available overlays")
36
+ app.add_typer(data_app , name="data" , help="Functions for data artifacts")
37
+ app.add_typer(model_app , name="model" , help="Functions related to model")
38
+ app.add_typer(scenario_app , name="scenario" , help="Scenario access and modification functions")
39
+ app.add_typer(state_app , name="state" , help="State access and modification functions")
87
40
 
88
41
  @app.command(help="Run the TUI")
89
42
  def tui ():
90
- """ Run the TUI to help us navigate and explore the state """
91
- from speclogician.tui.app import SpecLogicianApp
92
- tui_app = SpecLogicianApp()
43
+ """
44
+ Launch the TUI view of the state
45
+ """
46
+ from tui.tui import SpecLogicianApp
47
+ tui_app = SpecLogicianApp(state)
93
48
  tui_app.run()
94
49
 
95
50
  @app.command(help="Generate a prompt for helping agents use SpecLogician")
96
- def prompt(
97
- out: Path | None = typer.Option(
98
- None,
99
- "--out",
100
- help="Write the prompt to a file instead of stdout.",
101
- ),
102
- ):
51
+ def prompt():
103
52
  """
104
- Generate a prompt for helping CLI agents use SpecLogician.
53
+ Generate a prompt for helping CLI agents use SpecLogician
105
54
  """
106
55
 
107
- here = Path(__file__).resolve()
108
- repo_root = here.parents[0]
109
- prompt_path = repo_root / "utils" / "prompt.md"
56
+ prompt_path = Path(os.path.abspath(__file__)).parent / "utils/prompt.md"
110
57
 
111
58
  try:
112
- txt = prompt_path.read_text()
59
+ contents = prompt_path.read_text()
113
60
  except Exception as e:
114
- console.print(f"🛑 Failed to read prompt file at {prompt_path}: {e}")
115
- raise typer.Exit(2)
116
-
117
- if out:
118
- try:
119
- out.write_text(txt)
120
- except Exception as e:
121
- console.print(f"🛑 Failed to write prompt to {out}: {e}")
122
- raise typer.Exit(2)
123
- typer.echo(str(out))
124
- else:
125
- typer.echo(txt)
61
+ console.print(f"🛑 Failed to read in the prompt contents: {e}")
62
+ return
126
63
 
127
-
128
- @app.command()
129
- def version(
130
- json_only: bool = typer.Option(False, "--json", help="Output JSON"),
131
- ):
132
- """Print SpecLogician version"""
133
- if json_only:
134
- typer.echo(f'{{"version": "{__version__}"}}')
135
- else:
136
- typer.echo(__version__)
64
+ console.rule("[bold]Start of SpecLogician prompt[/bold]")
65
+ console.print(contents)
66
+ console.rule("[bold]End of prompt[/bold]")
137
67
 
138
68
  if __name__ == '__main__':
139
69
  app()
70
+ import sys
71
+ sys.exit(0)
72
+ print ("hello")
73
+ state.save(dirpath=os.getcwd())
@@ -1,31 +0,0 @@
1
- #
2
- # Imandra Inc.
3
- #
4
- # speclogician/modeling/__init__.py
5
- #
6
-
7
-
8
- import re
9
- from typing import List
10
-
11
- IML_TYPE_REGEX = re.compile(
12
- r"""
13
- ^\s*type\s+ # 'type' keyword
14
- (?P<name>[a-zA-Z_][a-zA-Z0-9_]*) # type name
15
- \s*= # '=' sign
16
- """,
17
- re.MULTILINE | re.VERBOSE,
18
- )
19
-
20
- def strip_iml_comments(text: str) -> str:
21
- """Remove OCaml-style (* ... *) and // comments."""
22
- # Remove block comments
23
- text = re.sub(r"\(\*.*?\*\)", "", text, flags=re.DOTALL)
24
- # Remove line comments
25
- text = re.sub(r"//.*", "", text)
26
- return text
27
-
28
- def extract_declared_types(iml_text: str) -> List[str]:
29
- """Return all declared type names in an IML file."""
30
- clean = strip_iml_comments(iml_text)
31
- return [m.group("name") for m in IML_TYPE_REGEX.finditer(clean)]
@@ -1,71 +1,15 @@
1
1
  #
2
2
  # Imandra Inc.
3
3
  #
4
- # speclogician/modeling/component.py
4
+ # component.py
5
5
  #
6
6
 
7
7
  import uuid
8
- import re
9
- from datetime import datetime, timezone
10
- from pydantic import BaseModel, Field, field_validator, ConfigDict
11
- from ..utils import IMLValidity
12
-
13
- _NAME_RE = re.compile(r"^[a-z][a-z0-9_]*$")
14
-
15
- # Conservative reserved set (OCaml-ish + common IML surface syntax).
16
- # Add more as you run into them.
17
- _IML_RESERVED: set[str] = {
18
- # core keywords / syntax
19
- "let", "in", "and", "rec",
20
- "type", "match", "with", "function",
21
- "if", "then", "else",
22
- "try", "raise",
23
- "module", "open", "include",
24
- "begin", "end",
25
- "as", "of", "when",
26
-
27
- # booleans / common literals
28
- "true", "false",
29
-
30
- # common types / constructors that are often special-cased
31
- "some", "none",
32
- }
33
-
34
- def validate_iml_identifier(name: str) -> str:
35
- if not _NAME_RE.fullmatch(name):
36
- raise ValueError(
37
- f"Invalid name '{name}'. Must start with a lowercase letter and contain only "
38
- "lowercase letters, digits, and underscores."
39
- )
40
- if name in _IML_RESERVED:
41
- raise ValueError(f"Invalid name '{name}': reserved keyword in IML/OCaml surface syntax.")
42
- return name
8
+ from pydantic import BaseModel, Field
43
9
 
44
10
  class ModelComponent(BaseModel):
45
11
  """
46
- Base class for all model components (Predicates, transitions and scenarios)
47
12
  """
48
- model_config = ConfigDict(validate_assignment=True) # optional but recommended
49
-
50
- comp_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
51
- name: str
52
-
53
- # Track last update time (UTC)
54
- last_updated: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
55
-
56
- @field_validator("name")
57
- @classmethod
58
- def validate_name(cls, v: str) -> str:
59
- return validate_iml_identifier(v)
60
-
61
- def touch(self) -> None:
62
- self.last_updated = datetime.now(timezone.utc)
63
-
64
- class SrcComponent(ModelComponent):
65
- src_code: str = ""
66
- is_iml_valid : IMLValidity = IMLValidity.UNKNOWN
67
- iml_error: str | None = None
68
13
 
69
- def set_src_code(self, src_code: str) -> None:
70
- self.src_code = src_code
71
- self.touch()
14
+ comp_id : str = Field(default_factory=lambda: str(uuid.uuid4())) # assigned task ID, new one created if not provided
15
+
@@ -1,26 +1,12 @@
1
1
  #
2
2
  # Imandra Inc.
3
3
  #
4
- # speclogician/modeling/conflict.py
4
+ # conflict.py
5
5
  #
6
6
 
7
7
  from pydantic import BaseModel
8
- from typing import Literal, TypeAlias
9
8
 
10
- class ScenarioOverlap(BaseModel):
11
- """ Two scenarios overlap in constraints """
12
- kind: Literal["overlap"] = "overlap"
13
- scenario_name1 : str
14
- scenario_name2 : str
15
-
16
- details : str
17
-
18
- class ScenarioConsumed(BaseModel):
19
- """ One scenario fully absorbs another """
20
- kind: Literal["consumed"] = "consumed"
21
- scenario_name1 : str
22
- scenario_name2 : str
23
-
24
- details : str
25
-
26
- ScenarioConflict : TypeAlias = ScenarioOverlap | ScenarioConsumed
9
+ class Conflict(BaseModel):
10
+ """
11
+ """
12
+ pass