imandrax-api-models 1.0.0__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.
@@ -0,0 +1,93 @@
1
+ from .proto_models import (
2
+ Art,
3
+ CounterSat,
4
+ DecomposeReq,
5
+ DecomposeRes,
6
+ Empty,
7
+ Error,
8
+ ErrorKind,
9
+ ErrorMessage,
10
+ EvalOutput,
11
+ EvalRes,
12
+ EvalSrcReq,
13
+ InferredType,
14
+ InstanceNameReq,
15
+ InstanceRes,
16
+ InstanceSrcReq,
17
+ LiftBool,
18
+ Location,
19
+ Model,
20
+ ModelType,
21
+ OneshotReq,
22
+ OneshotRes,
23
+ Origin,
24
+ PO_Res,
25
+ Position,
26
+ Proved,
27
+ Refuted,
28
+ RegionStr,
29
+ Sat,
30
+ Session,
31
+ SessionCreate,
32
+ SessionCreateReq,
33
+ SessionOpen,
34
+ StorageEntry,
35
+ StringMsg,
36
+ Task,
37
+ TaskID,
38
+ TaskKind,
39
+ TypecheckReq,
40
+ TypecheckRes,
41
+ Unsat,
42
+ Verified_upto,
43
+ VerifyNameReq,
44
+ VerifyRes,
45
+ VerifySrcReq,
46
+ )
47
+
48
+ __all__ = [
49
+ 'Art',
50
+ 'CounterSat',
51
+ 'DecomposeReq',
52
+ 'DecomposeRes',
53
+ 'Empty',
54
+ 'Error',
55
+ 'ErrorKind',
56
+ 'ErrorMessage',
57
+ 'EvalOutput',
58
+ 'EvalRes',
59
+ 'EvalSrcReq',
60
+ 'InferredType',
61
+ 'InstanceNameReq',
62
+ 'InstanceRes',
63
+ 'InstanceSrcReq',
64
+ 'LiftBool',
65
+ 'Location',
66
+ 'Model',
67
+ 'ModelType',
68
+ 'OneshotReq',
69
+ 'OneshotRes',
70
+ 'Origin',
71
+ 'PO_Res',
72
+ 'Position',
73
+ 'Proved',
74
+ 'Refuted',
75
+ 'RegionStr',
76
+ 'Sat',
77
+ 'Session',
78
+ 'SessionCreate',
79
+ 'SessionCreateReq',
80
+ 'SessionOpen',
81
+ 'StorageEntry',
82
+ 'StringMsg',
83
+ 'Task',
84
+ 'TaskID',
85
+ 'TaskKind',
86
+ 'TypecheckReq',
87
+ 'TypecheckRes',
88
+ 'Unsat',
89
+ 'Verified_upto',
90
+ 'VerifyNameReq',
91
+ 'VerifyRes',
92
+ 'VerifySrcReq',
93
+ ]
@@ -0,0 +1,207 @@
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ from dataclasses import dataclass
5
+ from typing import Any, assert_never
6
+
7
+ from imandrax_api.lib import (
8
+ Artifact,
9
+ Common_Applied_symbol_t_poly,
10
+ Common_Fun_decomp_t_poly,
11
+ Common_Model_t_poly,
12
+ Common_Region_meta_Assoc,
13
+ Common_Region_meta_String,
14
+ Common_Region_meta_Term,
15
+ Common_Region_t_poly,
16
+ Common_Var_t_poly,
17
+ Mir_Fun_decomp,
18
+ Mir_Term,
19
+ Mir_Term_view_Const,
20
+ Mir_Term_view_Construct,
21
+ Mir_Type,
22
+ Uid,
23
+ read_artifact_data,
24
+ )
25
+
26
+
27
+ @dataclass
28
+ class RegionStr:
29
+ constraints_str: list[str] | None
30
+ invariant_str: str
31
+ model_str: dict[str, str]
32
+ model_eval_str: str | None
33
+
34
+
35
+ class ArtifactDecodeError(Exception):
36
+ pass
37
+
38
+
39
+ def decode_artifact(
40
+ data: bytes | str,
41
+ kind: str,
42
+ ) -> list[RegionStr] | dict[str, str] | None:
43
+ data_b: bytes = base64.b64decode(data) if isinstance(data, str) else data
44
+ art: Artifact = read_artifact_data(data=data_b, kind=kind)
45
+ match (art, kind):
46
+ case (
47
+ Common_Fun_decomp_t_poly(
48
+ f_id=_f_id,
49
+ f_args=_f_args,
50
+ regions=regions,
51
+ ) as _fun_decomp,
52
+ 'mir.fun_decomp',
53
+ ):
54
+ _fun_decomp: Mir_Fun_decomp
55
+ _f_id: Uid
56
+ _f_args: list[Common_Var_t_poly[Mir_Type]]
57
+ regions: list[Common_Region_t_poly[Mir_Term, Mir_Type]]
58
+
59
+ return unwrap_region_str(regions)
60
+ case (
61
+ Common_Model_t_poly(
62
+ # list[tuple[Mir_Type, Common_Model_ty_def[Mir_Term, Mir_Type]]]
63
+ tys=_,
64
+ consts=consts,
65
+ # list[
66
+ # tuple[
67
+ # Common_Applied_symbol_t_poly[Mir_Type],
68
+ # Common_Model_fi[Mir_Term, Mir_Type],
69
+ # ]
70
+ # ]
71
+ funs=_,
72
+ # bool
73
+ representable=_,
74
+ # bool
75
+ completed=_,
76
+ # list[tuple[Uid, Mir_Type]]
77
+ ty_subst=_,
78
+ ) as _mir_model,
79
+ 'mir.model',
80
+ ):
81
+ consts: list[tuple[Common_Applied_symbol_t_poly[Mir_Type], Mir_Term]]
82
+ consts_d: dict[str, Any] = unwrap_model_constants(consts)
83
+ return consts_d
84
+ case _:
85
+ raise ArtifactDecodeError(
86
+ f'Unknown artifact type: {type(art)}, with {kind = }'
87
+ )
88
+
89
+
90
+ def unwrap_model_constants(
91
+ consts: list[tuple[Common_Applied_symbol_t_poly[Mir_Type], Mir_Term]],
92
+ ) -> dict[str, Any]:
93
+ constants: dict[str, Any] = {}
94
+
95
+ for applied_symbol, term in consts:
96
+ match applied_symbol, term:
97
+ case (
98
+ Common_Applied_symbol_t_poly(
99
+ sym=applied_symbol_sym,
100
+ args=_,
101
+ ty=_,
102
+ ),
103
+ Mir_Term(view=term_view, ty=_, sub_anchor=_),
104
+ ):
105
+ var_name = applied_symbol_sym.id.name
106
+
107
+ # Extract the value from the term
108
+ match term_view:
109
+ case Mir_Term_view_Const(arg=term_view_const):
110
+ constants[var_name] = term_view_const.arg
111
+ case Mir_Term_view_Construct():
112
+ raise NotImplementedError(
113
+ 'Term view type of Mir_Term_view_Construct is not supported'
114
+ )
115
+ case _:
116
+ raise NotImplementedError(
117
+ f'Unexpected term view type: {type(term_view)}'
118
+ )
119
+
120
+ return constants
121
+
122
+
123
+ type region_meta_value = (
124
+ Common_Region_meta_Assoc[Mir_Term]
125
+ | Common_Region_meta_Term[Mir_Term]
126
+ | Common_Region_meta_String[Mir_Term]
127
+ )
128
+
129
+
130
+ def unwrap_region_str(
131
+ regions: list[Common_Region_t_poly[Mir_Term, Mir_Type]],
132
+ ) -> list[RegionStr]:
133
+ """
134
+ Get `RegionStr`s from a list of `Region.t`.
135
+
136
+ A region object looks like:
137
+ {
138
+ "constraints": [...],
139
+ "invariant": ...,
140
+ "meta": [
141
+ ("str", ...) # What we want
142
+ ("model", ...)
143
+ ("model_eval", ...)
144
+ ("id", "...")
145
+ ]
146
+ "status": ...
147
+ }.
148
+ """
149
+ regions_str: list[RegionStr] = []
150
+ for region in regions:
151
+ match region:
152
+ case Common_Region_t_poly(
153
+ constraints=_constraints,
154
+ invariant=_invariant,
155
+ meta=meta,
156
+ status=_status,
157
+ ):
158
+ # Convert meta list to dict
159
+ meta_d: dict[Any, Any] = dict(meta)
160
+
161
+ # get `str` dict
162
+ meta_str_raw = meta_d.get('str')
163
+ assert meta_str_raw is not None, "Never: no 'str' in meta"
164
+
165
+ # meta_str should be Common_Region_meta_Assoc[Mir_Term]
166
+ if not isinstance(meta_str_raw, Common_Region_meta_Assoc):
167
+ raise ValueError(
168
+ f'Expected Common_Region_meta_Assoc, got {type(meta_str_raw)}'
169
+ )
170
+
171
+ meta_str_d: dict[Any, Any] = dict(meta_str_raw.arg) # type: ignore[arg-type]
172
+
173
+ # Extract constraints
174
+ constraints_raw = meta_str_d.get('constraints')
175
+ constraints: list[str] | None
176
+ if constraints_raw is not None:
177
+ constraints = [c.arg for c in constraints_raw.arg]
178
+ else:
179
+ constraints = None
180
+
181
+ # Extract invariant
182
+ invariant_raw = meta_str_d.get('invariant')
183
+ invariant: str = invariant_raw.arg if invariant_raw is not None else ''
184
+
185
+ # Extract model
186
+ model_raw = meta_str_d.get('model')
187
+ model: dict[str, str] = {}
188
+ if model_raw is not None:
189
+ model = {k: v.arg for (k, v) in model_raw.arg}
190
+
191
+ # Extract model_eval (optional)
192
+ model_eval: str | None = None
193
+ if 'model_eval' in meta_str_d:
194
+ model_eval_raw = meta_str_d['model_eval']
195
+ if model_eval_raw is not None:
196
+ model_eval = model_eval_raw.arg
197
+
198
+ region_str = RegionStr(
199
+ invariant_str=invariant,
200
+ constraints_str=constraints,
201
+ model_str=model,
202
+ model_eval_str=model_eval,
203
+ )
204
+ regions_str.append(region_str)
205
+ case _:
206
+ assert_never(region)
207
+ return regions_str
@@ -0,0 +1,83 @@
1
+ from .artmsg import Art, StorageEntry
2
+ from .error import Error, ErrorKind, ErrorMessage
3
+ from .locs import Location, Position
4
+ from .session import Session, SessionCreate, SessionOpen
5
+ from .simple_api import (
6
+ CounterSat,
7
+ DecomposeReq,
8
+ DecomposeRes,
9
+ EvalOutput,
10
+ EvalRes,
11
+ EvalSrcReq,
12
+ InferredType,
13
+ InstanceNameReq,
14
+ InstanceRes,
15
+ InstanceSrcReq,
16
+ LiftBool,
17
+ Model,
18
+ ModelType,
19
+ OneshotReq,
20
+ OneshotRes,
21
+ PO_Res,
22
+ Proved,
23
+ Refuted,
24
+ RegionStr,
25
+ Sat,
26
+ SessionCreateReq,
27
+ TypecheckReq,
28
+ TypecheckRes,
29
+ Unsat,
30
+ Verified_upto,
31
+ VerifyNameReq,
32
+ VerifyRes,
33
+ VerifySrcReq,
34
+ )
35
+ from .task import Origin, Task, TaskID, TaskKind
36
+ from .utils import Empty, StringMsg
37
+
38
+ __all__ = [
39
+ 'Art',
40
+ 'CounterSat',
41
+ 'DecomposeReq',
42
+ 'DecomposeRes',
43
+ 'Empty',
44
+ 'Error',
45
+ 'ErrorKind',
46
+ 'ErrorMessage',
47
+ 'EvalOutput',
48
+ 'EvalRes',
49
+ 'EvalSrcReq',
50
+ 'InferredType',
51
+ 'InstanceNameReq',
52
+ 'InstanceRes',
53
+ 'InstanceSrcReq',
54
+ 'LiftBool',
55
+ 'Location',
56
+ 'Model',
57
+ 'ModelType',
58
+ 'OneshotReq',
59
+ 'OneshotRes',
60
+ 'Origin',
61
+ 'PO_Res',
62
+ 'Position',
63
+ 'Proved',
64
+ 'Refuted',
65
+ 'RegionStr',
66
+ 'Sat',
67
+ 'Session',
68
+ 'SessionCreate',
69
+ 'SessionCreateReq',
70
+ 'SessionOpen',
71
+ 'StorageEntry',
72
+ 'StringMsg',
73
+ 'Task',
74
+ 'TaskID',
75
+ 'TaskKind',
76
+ 'TypecheckReq',
77
+ 'TypecheckRes',
78
+ 'Unsat',
79
+ 'Verified_upto',
80
+ 'VerifyNameReq',
81
+ 'VerifyRes',
82
+ 'VerifySrcReq',
83
+ ]
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import ConfigDict, Field
4
+
5
+ from ..proto_utils import BaseModel
6
+
7
+
8
+ class StorageEntry(BaseModel):
9
+ model_config = ConfigDict(ser_json_bytes='base64', val_json_bytes='base64')
10
+
11
+ key: str = Field(description='the CA store key')
12
+ value: bytes = Field(description='the stored value')
13
+
14
+
15
+ # We tend to generate this using `google.protobuf.json_format.MessageToDict`
16
+ # This converts protobuf bytes to base64 strings
17
+ class Art(BaseModel):
18
+ model_config = ConfigDict(ser_json_bytes='base64', val_json_bytes='base64')
19
+
20
+ kind: str = Field(description='The kind of artifact')
21
+ data: bytes = Field(description='Serialized data, in twine')
22
+ api_version: str = Field(
23
+ description=(
24
+ 'Version of the API. This is mandatory and must match with the imandrax-api'
25
+ ' library version.'
26
+ )
27
+ )
28
+ storage: list[StorageEntry] = Field(
29
+ default_factory=lambda: [], description='Additional definitions on the side'
30
+ )
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from enum import Enum
5
+ from typing import Self
6
+
7
+ from pydantic import Field
8
+
9
+ from ..proto_utils import BaseModel
10
+ from .locs import Location
11
+
12
+
13
+ # from https://github.com/imandra-ai/imandrax-api/blob/1a4abb4eb59d9545c6f0af9698956437b5b5dcb8/src/internal/error_data/imandrax_errors.ml
14
+ class ErrorKind(str, Enum):
15
+ APPLIED_SYMBOL_TYPE_ERR = 'AppliedSymbolTypeErr'
16
+ CONFIG_ERROR = 'ConfigError'
17
+ CPTR_GET_OTHER_ERR = 'CptrGetOtherErr'
18
+ CPTR_NOT_FOUND_IN_STORAGE = 'CptrNotFoundInStorage'
19
+ GENERIC_USER_ERROR = 'GenericUserError'
20
+ INVALID_FUN_DEFINITION = 'InvalidFunDefinition'
21
+ INVALID_QIDENT = 'InvalidQident'
22
+ INVALID_TYPE_ALIAS = 'InvalidTypeAlias'
23
+ INVALID_TYPE_DEFINITION = 'InvalidTypeDefinition'
24
+ INVALID_CNAME = 'InvalidCname'
25
+ LEVAL_ERROR = 'LevalError'
26
+ LOWER_CIR_ERROR = 'LowerCirError'
27
+ LOWER_MIR_ERROR = 'LowerMirError'
28
+ LOWER_RIR_ERROR = 'LowerRirError'
29
+ PARSER_NOT_REGISTERED = 'ParserNotRegistered'
30
+ PATMATCH_ERROR = 'PatmatchError'
31
+ SYN_TERM_CREATE_ERROR = 'SynTermCreateError'
32
+ SYNTAX_ATTRIBUTE_ERR = 'SyntaxAttributeErr'
33
+ SYNTAX_ERR = 'SyntaxErr'
34
+ SYNTAX_UNSUGAR_ERR = 'SyntaxUnsugarErr'
35
+ TACTIC_EVAL_ERR = 'TacticEvalErr'
36
+ TERM_INVALID_SHAPE = 'TermInvalidShape'
37
+ TERM_MODEL_FIERR = 'TermModelFIErr'
38
+ THREAD_FUT_FAILURE = 'ThreadFutFailure'
39
+ THREAD_TIMER_FAILURE = 'ThreadTimerFailure'
40
+ TY_INFER_INTERNAL_ERROR = 'TyInferInternalError'
41
+ TYPE_ARITY_MISMATCH = 'TypeArityMismatch'
42
+ TYPE_CYCLE_DETECTED = 'TypeCycleDetected'
43
+ TYPE_ERR = 'TypeErr'
44
+ TYPE_VAR_ALREADY_BOUND = 'TypeVarAlreadyBound'
45
+ TYPED_SYMBOL_NON_GROUND = 'TypedSymbolNonGround'
46
+ UID_CONTENT_ADDRESSING_NAME_MISMATCH = 'UidContentAddressingNameMismatch'
47
+ UID_CONTENT_ADDRESSING_NOT_TEMPORARY = 'UidContentAddressingNotTemporary'
48
+ UID_NOT_CONTENT_ADDRESSED = 'UidNotContentAddressed'
49
+ UNKNOWN_BUILTIN_SYMBOL_FOR_UID = 'UnknownBuiltinSymbolForUid'
50
+ UNKNOWN_TYPE_DEFINITION = 'UnknownTypeDefinition'
51
+ INTERACTIVE_PROOF_ERR = 'InteractiveProofErr'
52
+ PROOF_DESER_ERROR = 'ProofDeserError'
53
+ PROOF_CHECK_ERROR = 'ProofCheckError'
54
+ INVALID_ANCHOR = 'InvalidAnchor'
55
+ INDUCT_SCHEMA_ERROR = 'InductSchemaError'
56
+ UNSUPPORTED = 'Unsupported'
57
+ LOWER_FOL_ERROR = 'LowerFolError'
58
+ VALIDATION_ERROR = 'ValidationError'
59
+ LSP_ERROR = 'LspError'
60
+ INTERRUPTED = 'Interrupted'
61
+ REDIS_ERROR = 'RedisError'
62
+ CIRDECL_NOT_FOUND_IN_STORAGE = 'CIRDeclNotFoundInStorage'
63
+ SERIALIZATION_ERROR = 'SerializationError'
64
+ DESERIALIZATION_ERROR = 'DeserializationError'
65
+ DEBUG_MODE = 'DebugMode'
66
+ IMPORT_ERROR = 'ImportError'
67
+ GENERIC_IOERROR = 'GenericIOError'
68
+ DUNE_ERROR = 'DuneError'
69
+ RPC_ERROR = 'RpcError'
70
+ RPC_DESER_ERROR = 'RpcDeserError'
71
+ RPC_NETWORK_ERROR = 'RpcNetworkError'
72
+ RPC_TIMEOUT = 'RpcTimeout'
73
+ AUTH_ERROR = 'AuthError'
74
+ FILE_EXISTS = 'FileExists'
75
+ DIRECTORY_CREATION_ERROR = 'DirectoryCreationError'
76
+ DECOMP_ERROR = 'DecompError'
77
+ VERSION_MISMATCH_ERROR = 'VersionMismatchError'
78
+ OH_NO_ERROR = 'OhNoError'
79
+ DEBOUNCED = 'Debounced'
80
+
81
+ @classmethod
82
+ def from_proto_kind(cls, proto_kind: str) -> Self:
83
+ kinds = re.findall(r'\{ Kind.name = "(.+)" \}', proto_kind)
84
+ if len(kinds) != 1:
85
+ raise ValueError('Unable to parse kind')
86
+ return cls(kinds[0])
87
+
88
+
89
+ class ErrorMessage(BaseModel):
90
+ msg: str
91
+ locs: list[Location] = Field(
92
+ default_factory=lambda: [], description='Locations for this message'
93
+ )
94
+ backtrace: str | None = Field(default=None, description='Captured backtrace')
95
+
96
+
97
+ class Error(BaseModel):
98
+ msg: ErrorMessage | None = Field(
99
+ default=None, description='The toplevel error message'
100
+ )
101
+ kind: str = Field(description='A string description of the kind of error')
102
+ stack: list[ErrorMessage] = Field(
103
+ default_factory=lambda: [], description='Context for the error'
104
+ )
105
+ process: str | None = Field(default=None)
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import Field
4
+
5
+ from ..proto_utils import BaseModel
6
+
7
+
8
+ class Position(BaseModel):
9
+ line: int
10
+ col: int
11
+
12
+
13
+ class Location(BaseModel):
14
+ file: str | None = Field(default=None)
15
+ start: Position | None = Field(default=None)
16
+ stop: Position | None = Field(default=None)
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import Field
4
+
5
+ from ..proto_utils import BaseModel
6
+
7
+
8
+ class Session(BaseModel):
9
+ """A session identifier."""
10
+
11
+ id: str = Field(description="The session's unique ID (e.g a uuid)")
12
+
13
+
14
+ class SessionCreate(BaseModel):
15
+ """Create a new session."""
16
+
17
+ po_check: bool = Field(default=True, description='Do we check Proof Obligations?')
18
+ api_version: str = Field(description='the API types version (mandatory)')
19
+
20
+
21
+ class SessionOpen(BaseModel):
22
+ """Reconnect to the given session."""
23
+
24
+ id: Session | None = Field(
25
+ default=None, description="The session's unique ID (e.g a uuid)"
26
+ )
27
+ api_version: str = Field(description='the API types version (mandatory)')
@@ -0,0 +1,411 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from typing import Any, Literal, Self
5
+
6
+ from devtools import pformat
7
+ from imandrax_api.lib import (
8
+ RegionStr,
9
+ get_region_str_from_decomp_artifact,
10
+ )
11
+ from pydantic import (
12
+ Field,
13
+ TypeAdapter,
14
+ field_validator,
15
+ model_validator,
16
+ )
17
+
18
+ from ..proto_utils import BaseModel
19
+ from .artmsg import Art
20
+ from .error import Error
21
+ from .session import Session
22
+ from .task import Origin, Task
23
+ from .utils import Empty, StringMsg
24
+
25
+
26
+ class SessionCreateReq(BaseModel):
27
+ api_version: str = Field(description='the API types version (mandatory)')
28
+
29
+
30
+ class LiftBool(Enum):
31
+ Default = 0
32
+ NestedEqualities = 1
33
+ Equalities = 2
34
+ All = 3
35
+
36
+
37
+ class DecomposeReq(BaseModel):
38
+ session: Session | None = Field(default=None)
39
+ name: str = Field(description='name of function to decompose')
40
+ assuming: str | None = Field(
41
+ default=None, description='name of side condition function'
42
+ )
43
+ basis: list[str]
44
+ rule_specs: list[str]
45
+ prune: bool
46
+ ctx_simp: bool | None = Field(default=None)
47
+ lift_bool: LiftBool | None = Field(default=None)
48
+ str_: bool = Field(default=True, alias='str')
49
+ timeout: int | None = Field(default=None)
50
+
51
+
52
+ class DecomposeResProto(BaseModel):
53
+ artifact: Art | None = Field(default=None)
54
+ err: Empty | None = Field(default=None)
55
+ errors: list[Error] = Field(default_factory=lambda: [])
56
+ task: Task | None = Field(default=None)
57
+
58
+ @model_validator(mode='after')
59
+ def one_of_res(self) -> Self:
60
+ if (self.artifact is None) == (self.err is None):
61
+ raise ValueError('One of artifact or err must be set')
62
+ return self
63
+
64
+
65
+ class string_kv(BaseModel):
66
+ k: str
67
+ v: str
68
+
69
+
70
+ class DecomposeRes(DecomposeResProto):
71
+ """Result of a decomposition."""
72
+
73
+ regions_str: list[RegionStr] | None = Field(
74
+ default=None, description="None if there's decomposition error"
75
+ )
76
+
77
+ @model_validator(mode='after')
78
+ def unwrap_region_str(self) -> Self:
79
+ if self.regions_str is not None:
80
+ return self
81
+ elif self.errors:
82
+ return self
83
+ else:
84
+ assert self.artifact is not None, 'artifact must be present when no errors'
85
+ regions_str = get_region_str_from_decomp_artifact(
86
+ data=self.artifact.data, kind=self.artifact.kind
87
+ )
88
+ return self.model_copy(update={'regions_str': regions_str})
89
+
90
+ def __repr__(self) -> str:
91
+ return pformat(self)
92
+
93
+ @property
94
+ def iml_test_cases(self) -> list[dict[str, Any]]:
95
+ """
96
+ Format region strings as test cases.
97
+
98
+ Eg: [
99
+ {"args": {"x": "1", "y": "2"}, "expected_output": "3"},
100
+ {"args": {"x": "3", "y": "4"}, "expected_output": "7"},
101
+ ].
102
+ """
103
+ if not self.regions_str:
104
+ return []
105
+ test_cases: list[dict[str, Any]] = []
106
+ for region_str in self.regions_str:
107
+ if (model_eval_str := region_str.model_eval_str) is None:
108
+ continue
109
+ test_cases.append(
110
+ {'args': region_str.model_str, 'expected_output': model_eval_str}
111
+ )
112
+ return test_cases
113
+
114
+ @property
115
+ def test_docstrs(self) -> list[str]:
116
+ docstrs: list[str] = []
117
+ if not self.regions_str:
118
+ return docstrs
119
+ for region_str in self.regions_str:
120
+ s = ''
121
+ if region_str.constraints_str:
122
+ s += 'Constraints:\n'
123
+ for c in region_str.constraints_str:
124
+ s += f' - `{c}`\n'
125
+ if region_str.invariant_str:
126
+ s += 'Invariant:\n'
127
+ s += f' - `{region_str.invariant_str}`\n'
128
+ docstrs.append(s)
129
+ return docstrs
130
+
131
+
132
+ class EvalSrcReq(BaseModel):
133
+ session: Session | None = Field(default=None)
134
+ src: str = Field(description='source code to evaluate')
135
+ async_only: bool | None = Field(
136
+ default=None,
137
+ description='if true, do not wait for tasks results, only return the task list '
138
+ 'and not the task results. Use `get_artifact` to get the results.',
139
+ )
140
+
141
+
142
+ class EvalOutput(BaseModel):
143
+ """Output of an `eval` statement."""
144
+
145
+ success: bool
146
+ value_as_ocaml: str | None = Field(
147
+ default=None, description='result as a OCaml value, if any'
148
+ )
149
+ errors: list[Error] = Field(default_factory=lambda: [])
150
+
151
+
152
+ class EvalRes(BaseModel):
153
+ success: bool = Field()
154
+ messages: list[str] = Field(
155
+ default_factory=lambda: [], description='"normal" messages'
156
+ )
157
+ errors: list[Error] = Field(
158
+ default_factory=lambda: [], description='akin to stderr'
159
+ )
160
+
161
+ # All tasks started during eval
162
+ tasks: list[Task] = Field(
163
+ default_factory=lambda: [], description='all tasks started during eval'
164
+ )
165
+ po_results: list[PO_Res] = Field(default_factory=lambda: [])
166
+ eval_results: list[EvalOutput] = Field(default_factory=lambda: [])
167
+ decomp_results: list[DecomposeRes] = Field(default_factory=lambda: [])
168
+
169
+ def __repr__(self) -> str:
170
+ return pformat(self, indent=2)
171
+
172
+ @property
173
+ def po_errors(self) -> list[Error]:
174
+ return [err for po_res in self.po_results for err in po_res.errors]
175
+
176
+ @property
177
+ def all_errors(self) -> list[Error]:
178
+ """
179
+ Non-proof-obligation errors and proof-obligation errors.
180
+
181
+ Non-proof-obligation errors always come first
182
+ """
183
+ return self.errors + self.po_errors
184
+
185
+ @property
186
+ def has_errors(self) -> bool:
187
+ return len(self.all_errors) > 0
188
+
189
+
190
+ class VerifySrcReq(BaseModel):
191
+ session: Session | None = Field(default=None)
192
+ src: str = Field(description='source code')
193
+ hints: str | None = Field(default=None)
194
+
195
+
196
+ class VerifyNameReq(BaseModel):
197
+ session: Session | None = Field(default=None)
198
+ name: str = Field(description='name of the predicate to verify')
199
+ hints: str | None = Field(default=None)
200
+
201
+
202
+ class InstanceSrcReq(BaseModel):
203
+ session: Session | None = Field(default=None)
204
+ src: str = Field(description='source code')
205
+ hints: str | None = Field(default=None)
206
+
207
+
208
+ class InstanceNameReq(BaseModel):
209
+ session: Session | None = Field(default=None)
210
+ name: str = Field(description='name of the predicate to verify')
211
+ hints: str | None = Field(default=None)
212
+
213
+
214
+ class Proved(BaseModel):
215
+ proof_pp: str | None = Field(default=None)
216
+
217
+
218
+ class Verified_upto(BaseModel):
219
+ msg: str | None = Field(default=None)
220
+
221
+
222
+ class Unsat(BaseModel):
223
+ proof_pp: str | None = Field(default=None)
224
+
225
+
226
+ class ModelType(Enum):
227
+ Counter_example = 'Counter_example'
228
+ Instance = 'Instance'
229
+
230
+
231
+ class Model(BaseModel):
232
+ m_type: ModelType # NOTE: proto file has this but it's not returned?
233
+ src: str = Field(description='iml source code for the model')
234
+ artifact: Art | None = Field(default=None, description='the model as an artifact')
235
+
236
+
237
+ class Refuted(BaseModel):
238
+ model: Model | None = Field(default=None)
239
+
240
+
241
+ class Sat(BaseModel):
242
+ model: Model | None = Field(default=None)
243
+
244
+
245
+ class CounterSat(BaseModel):
246
+ model: Model | None = Field(default=None)
247
+
248
+
249
+ class PO_Res(BaseModel):
250
+ unknown: StringMsg | None = Field(default=None)
251
+ err: Empty | None = Field(default=None)
252
+ proof: Proved | None = Field(default=None)
253
+ instance: CounterSat | None = Field(default=None)
254
+ verified_upto: Verified_upto | None = Field(default=None)
255
+
256
+ errors: list[Error] = Field(default_factory=lambda: [])
257
+ task: Task | None = Field(default=None, description='the ID of the task')
258
+ origin: Origin | None = Field(
259
+ default=None, description='where did the task originate?'
260
+ )
261
+
262
+ @model_validator(mode='after')
263
+ def one_of_res(self) -> Self:
264
+ sum_of_res = sum(
265
+ 1
266
+ for r in [
267
+ self.unknown,
268
+ self.err,
269
+ self.proof,
270
+ self.instance,
271
+ self.verified_upto,
272
+ ]
273
+ if r is not None
274
+ )
275
+ if sum_of_res != 1:
276
+ raise ValueError(
277
+ 'Exactly one of unknown, err, proof, instance, verified_upto must be '
278
+ 'set'
279
+ )
280
+ return self
281
+
282
+
283
+ class VerifyRes(BaseModel):
284
+ unknown: StringMsg | None = Field(default=None)
285
+ err: Empty | None = Field(default=None)
286
+ proved: Proved | None = Field(default=None)
287
+ refuted: Refuted | None = Field(default=None)
288
+ verified_upto: Verified_upto | None = Field(default=None)
289
+
290
+ errors: list[Error] = Field(default_factory=lambda: [])
291
+ task: Task | None = Field(default=None, description='the ID of the task')
292
+
293
+ @property
294
+ def res_type(
295
+ self,
296
+ ) -> Literal['unknown', 'err', 'proved', 'refuted', 'verified_upto']:
297
+ if self.unknown is not None:
298
+ return 'unknown'
299
+ elif self.err is not None:
300
+ return 'err'
301
+ elif self.proved is not None:
302
+ return 'proved'
303
+ elif self.refuted is not None:
304
+ return 'refuted'
305
+ elif self.verified_upto is not None:
306
+ return 'verified_upto'
307
+ else:
308
+ raise AssertionError('Never')
309
+
310
+ @property
311
+ def res(self) -> StringMsg | Empty | Proved | Refuted | Verified_upto:
312
+ if self.unknown is not None:
313
+ return self.unknown
314
+ elif self.err is not None:
315
+ return self.err
316
+ elif self.proved is not None:
317
+ return self.proved
318
+ elif self.refuted is not None:
319
+ return self.refuted
320
+ elif self.verified_upto is not None:
321
+ return self.verified_upto
322
+ else:
323
+ raise AssertionError('Never')
324
+
325
+ @model_validator(mode='after')
326
+ def one_of_res(self) -> Self:
327
+ sum_of_res = sum(
328
+ 1
329
+ for r in [
330
+ self.unknown,
331
+ self.err,
332
+ self.proved,
333
+ self.refuted,
334
+ self.verified_upto,
335
+ ]
336
+ if r is not None
337
+ )
338
+ if sum_of_res != 1:
339
+ raise ValueError(
340
+ 'Exactly one of unknown, err, proved, refuted, verified_upto must be '
341
+ 'set'
342
+ )
343
+ return self
344
+
345
+
346
+ class InstanceRes(BaseModel):
347
+ unknown: StringMsg | None = Field(default=None)
348
+ err: Empty | None = Field(default=None)
349
+ unsat: Unsat | None = Field(default=None)
350
+ sat: Sat | None = Field(default=None)
351
+
352
+ errors: list[Error] = Field(default_factory=lambda: [])
353
+ task: Task | None = Field(default=None, description='the ID of the task')
354
+
355
+ @model_validator(mode='after')
356
+ def one_of_res(self) -> Self:
357
+ sum_of_res = sum(
358
+ 1 for r in [self.unknown, self.err, self.unsat, self.sat] if r is not None
359
+ )
360
+ if sum_of_res != 1:
361
+ raise ValueError('Exactly one of unknown, err, unsat, sat must be set')
362
+ return self
363
+
364
+
365
+ class TypecheckReq(BaseModel):
366
+ session: Session | None = Field(default=None)
367
+ src: str = Field(description='source code to evaluate')
368
+
369
+
370
+ class TypecheckResProto(BaseModel):
371
+ success: bool
372
+ types: str = Field(description='JSON string of inferred types')
373
+ errors: list[Error] = Field(default_factory=lambda: [])
374
+
375
+
376
+ class InferredType(BaseModel):
377
+ name: str
378
+ ty: str = Field(description='inferred type')
379
+ line: int = Field(description='line number')
380
+ column: int = Field(description='column number')
381
+
382
+
383
+ InferredTypes = TypeAdapter(list[InferredType])
384
+
385
+
386
+ class TypecheckRes(TypecheckResProto):
387
+ types: list[InferredType] = Field(description='Parsed inferred types') # type: ignore[assignment]
388
+
389
+ @field_validator('types', mode='before')
390
+ @classmethod
391
+ def validate_types(cls, v: str | list[InferredType]) -> list[InferredType]:
392
+ if isinstance(v, str):
393
+ # Parse JSON string into list of dicts and validate with pydantic
394
+ types_list = InferredTypes.validate_json(v)
395
+ return types_list
396
+ return v
397
+
398
+
399
+ class OneshotReq(BaseModel):
400
+ input: str = Field(description='some iml code')
401
+ timeout: float | None = Field(default=None)
402
+
403
+
404
+ class OneshotRes(BaseModel):
405
+ class Stats(BaseModel):
406
+ time: float
407
+
408
+ results: list[str] = Field(default_factory=list)
409
+ errors: list[str] = Field(default_factory=list)
410
+ stats: Stats | None = Field(default=None)
411
+ detailed_results: list[str] = Field(default_factory=list)
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+
5
+ from pydantic import Field
6
+
7
+ from ..proto_utils import BaseModel
8
+
9
+
10
+ class TaskKind(Enum):
11
+ TASK_UNSPECIFIED = 'TASK_UNSPECIFIED'
12
+ TASK_EVAL = 'TASK_EVAL'
13
+ TASK_CHECK_PO = 'TASK_CHECK_PO'
14
+ TASK_PROOF_CHECK = 'TASK_PROOF_CHECK'
15
+ TASK_DECOMP = 'TASK_DECOMP'
16
+
17
+
18
+ class TaskID(BaseModel):
19
+ id: str = Field(description='The task identifier')
20
+
21
+
22
+ class Task(BaseModel):
23
+ id: TaskID | None = Field(default=None)
24
+ kind: TaskKind
25
+
26
+
27
+ class Origin(BaseModel):
28
+ from_sym: str = Field(description='Symbol from which the task originated')
29
+ count: int = Field(description='A counter for tasks for this symbol')
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+
3
+ from ..proto_utils import BaseModel
4
+
5
+
6
+ class Empty(BaseModel):
7
+ """Void type, used for messages without arguments or return value."""
8
+
9
+ pass
10
+
11
+
12
+ class StringMsg(BaseModel):
13
+ msg: str
@@ -0,0 +1,21 @@
1
+ from typing import Any
2
+
3
+ from google.protobuf.json_format import MessageToDict
4
+ from google.protobuf.message import Message
5
+ from pydantic import BaseModel as PydanticBaseModel, model_validator
6
+
7
+
8
+ def proto_to_dict(proto_obj: Message) -> dict[Any, Any]:
9
+ return MessageToDict(
10
+ proto_obj,
11
+ preserving_proto_field_name=True,
12
+ always_print_fields_with_no_presence=True,
13
+ )
14
+
15
+
16
+ class BaseModel(PydanticBaseModel):
17
+ @model_validator(mode='before')
18
+ def validate_proto(cls, v: Any) -> dict[Any, Any]:
19
+ if isinstance(v, Message):
20
+ return proto_to_dict(v)
21
+ return v
File without changes
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.3
2
+ Name: imandrax-api-models
3
+ Version: 1.0.0
4
+ Summary: Add your description here
5
+ Author: hongyu
6
+ Author-email: hongyu <hongyu@imandra.ai>
7
+ Requires-Dist: devtools>=0.12.2
8
+ Requires-Dist: imandrax-api[async]>=0.17.3.1
9
+ Requires-Dist: pydantic>=2.12.4
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+
13
+ # ImandraX API Models
14
+
15
+ Pydantic models for the ImandraX API.
@@ -0,0 +1,15 @@
1
+ imandrax_api_models/__init__.py,sha256=hPR01y2lYfwxl8nvR05urz_A-xyiTnAFGi4s4qJ7yc0,1469
2
+ imandrax_api_models/decode_artifact_local.py,sha256=XdoF5x64-Bizq01kOTeoEZUMw6GYdZVYMiiBsVHnyaA,6593
3
+ imandrax_api_models/proto_models/__init__.py,sha256=oRB7wPNGzXPgIchRrYb7xjB8Zk245X8WM1PEZ6j-RUE,1512
4
+ imandrax_api_models/proto_models/artmsg.py,sha256=9xZ-5p-3jmcZVtVdOZ9gx0byGIz1ArAYm6qNPjFUtfc,1011
5
+ imandrax_api_models/proto_models/error.py,sha256=VJ4jxH6jQ13bVlRjnjQsz6oaNz5uUuhsbzw5yLAn5N4,4003
6
+ imandrax_api_models/proto_models/locs.py,sha256=T0cA-2BY_pZH6ISrIgKGUoixq8iWoUvMTGjxwX7apqA,325
7
+ imandrax_api_models/proto_models/session.py,sha256=MxU3to98yqCLM4nLH60_2BLGKzI44ETAG4OJc8273j4,729
8
+ imandrax_api_models/proto_models/simple_api.py,sha256=dM_LBS92aj0jq8agLykF-UOwzA_JhzyCHfrp1k_jGas,12335
9
+ imandrax_api_models/proto_models/task.py,sha256=PcoGWekhcyyCywWuCcCMzF-SOd5Hm_fEWREOQeLADEw,677
10
+ imandrax_api_models/proto_models/utils.py,sha256=mC42fpYUrGiIgiIko_3lRkLBN0j-g5hHu72ABZt_gNs,225
11
+ imandrax_api_models/proto_utils.py,sha256=esHc2PdJp5A0XM-Sy0c0keQyttam_1eJN8mNYMPXTnw,611
12
+ imandrax_api_models/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ imandrax_api_models-1.0.0.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
14
+ imandrax_api_models-1.0.0.dist-info/METADATA,sha256=NdJkW9brX_Y9Wf3403rcLRJ65hiTgmjBWXPDkGOOdjs,389
15
+ imandrax_api_models-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.8.22
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any