relationalai 1.0.0a3__py3-none-any.whl → 1.0.0a4__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.
- relationalai/config/shims.py +1 -0
- relationalai/semantics/__init__.py +7 -1
- relationalai/semantics/frontend/base.py +19 -13
- relationalai/semantics/frontend/core.py +30 -2
- relationalai/semantics/frontend/front_compiler.py +38 -11
- relationalai/semantics/frontend/pprint.py +1 -1
- relationalai/semantics/metamodel/rewriter.py +6 -2
- relationalai/semantics/metamodel/typer.py +70 -26
- relationalai/semantics/reasoners/__init__.py +11 -0
- relationalai/semantics/reasoners/graph/__init__.py +38 -0
- relationalai/semantics/reasoners/graph/core.py +9015 -0
- relationalai/shims/hoister.py +9 -0
- relationalai/shims/mm2v0.py +32 -24
- relationalai/tools/cli/cli.py +138 -0
- relationalai/tools/cli/docs.py +394 -0
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a4.dist-info}/METADATA +5 -3
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a4.dist-info}/RECORD +29 -24
- v0/relationalai/clients/exec_txn_poller.py +91 -0
- v0/relationalai/clients/resources/snowflake/__init__.py +2 -2
- v0/relationalai/clients/resources/snowflake/direct_access_resources.py +16 -10
- v0/relationalai/clients/resources/snowflake/snowflake.py +43 -14
- v0/relationalai/clients/resources/snowflake/use_index_poller.py +8 -0
- v0/relationalai/errors.py +18 -0
- v0/relationalai/semantics/lqp/executor.py +3 -1
- v0/relationalai/semantics/lqp/rewrite/extract_keys.py +25 -3
- v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +335 -84
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a4.dist-info}/WHEEL +0 -0
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a4.dist-info}/entry_points.txt +0 -0
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a4.dist-info}/top_level.txt +0 -0
relationalai/shims/hoister.py
CHANGED
|
@@ -130,6 +130,15 @@ class Hoister(ContainerWalker):
|
|
|
130
130
|
self.hoists[child] = parent_hoists
|
|
131
131
|
self.allowed_vars[child] = allowed
|
|
132
132
|
self.allowed_vars[container] = allowed
|
|
133
|
+
# this might be a match/union that just acts as a filter for some sibling logical,
|
|
134
|
+
# so we need to create an artificial demand for the allowed vars in the parent container
|
|
135
|
+
# to hoist them up if there's no other output providing them.
|
|
136
|
+
# ex: define(foo(Person)).where(union(Person.age > 10, Person.age < 5))
|
|
137
|
+
# parent = self.scope.parent.get(container, None)
|
|
138
|
+
# if parent:
|
|
139
|
+
# for allowed_var in allowed:
|
|
140
|
+
# self._register_use(allowed_var, input=True, container=parent)
|
|
141
|
+
# self.scope.var_refs[allowed_var]
|
|
133
142
|
|
|
134
143
|
if container.scope:
|
|
135
144
|
# when leaving the scope, compute hoisted vars
|
relationalai/shims/mm2v0.py
CHANGED
|
@@ -217,7 +217,7 @@ class Translator():
|
|
|
217
217
|
"like": "like_match",
|
|
218
218
|
"regex_escape": "escape_regex_metachars",
|
|
219
219
|
}
|
|
220
|
-
for lib in ["core", "aggregates", "common", "datetime", "floats", "math", "numbers", "strings", "re", "lqp"]:
|
|
220
|
+
for lib in ["core", "aggregates", "constraints", "common", "datetime", "floats", "math", "numbers", "strings", "re", "lqp"]:
|
|
221
221
|
for x in b._libraries[lib].data.values():
|
|
222
222
|
v0_name = RENAMES.get(x.name, x.name)
|
|
223
223
|
if v0_name in v0_builtins.builtin_relations_by_name:
|
|
@@ -320,7 +320,13 @@ class Translator():
|
|
|
320
320
|
|
|
321
321
|
def translate_annotation(self, a: mm.Annotation, parent, ctx) -> v0.Annotation:
|
|
322
322
|
if a.relation.name in v0_builtins.builtin_annotations_by_name:
|
|
323
|
-
|
|
323
|
+
if not a.args:
|
|
324
|
+
return getattr(v0_builtins, a.relation.name + "_annotation") # type: ignore
|
|
325
|
+
else:
|
|
326
|
+
return v0.Annotation(
|
|
327
|
+
relation=getattr(v0_builtins, a.relation.name), # type: ignore
|
|
328
|
+
args=tuple(self.translate_value(arg, a, ctx) for arg in a.args)
|
|
329
|
+
)
|
|
324
330
|
|
|
325
331
|
return v0.Annotation(
|
|
326
332
|
relation=self.translate_node(a.relation, a, Context.MODEL), # type: ignore
|
|
@@ -335,7 +341,7 @@ class Translator():
|
|
|
335
341
|
# Abstract types (should be removed once our typer is done because they should not
|
|
336
342
|
# show up in the typed metamodel.)
|
|
337
343
|
b.core.Any: v0_types.Any,
|
|
338
|
-
|
|
344
|
+
b.core.AnyEntity: v0_types.AnyEntity,
|
|
339
345
|
b.core.Number: v0_types.Number, # v0 typer can figure the rest out
|
|
340
346
|
# Concrete types
|
|
341
347
|
b.core.Boolean: v0_types.Bool,
|
|
@@ -362,6 +368,7 @@ class Translator():
|
|
|
362
368
|
annotations.append(v0_builtins.external_annotation)
|
|
363
369
|
name = self.translate_table_name(t)
|
|
364
370
|
fields.insert(0, v0.Field(name="symbol", type=v0_types.Symbol, input=False)) # type: ignore
|
|
371
|
+
annotations.extend(self.translate_seq(t.annotations, t, ctx)) # type: ignore
|
|
365
372
|
|
|
366
373
|
type_relation = v0.Relation(
|
|
367
374
|
name=name,
|
|
@@ -372,9 +379,8 @@ class Translator():
|
|
|
372
379
|
)
|
|
373
380
|
for super_type in t.super_types:
|
|
374
381
|
super_rel = self.translate_node(super_type, t, Context.TASK)
|
|
375
|
-
if not super_rel:
|
|
382
|
+
if not isinstance(super_rel, v0.Relation):
|
|
376
383
|
continue
|
|
377
|
-
assert isinstance(super_rel, v0.Relation)
|
|
378
384
|
v = v0.Var(type=actual_type, name=type_relation.name.lower())
|
|
379
385
|
self.maintenance_rules.append(v0.Logical(
|
|
380
386
|
engine=None,
|
|
@@ -745,7 +751,6 @@ class Translator():
|
|
|
745
751
|
assert isinstance(r.domain, mm.Logical)
|
|
746
752
|
assert isinstance(r.check, mm.Logical)
|
|
747
753
|
if not r.domain.body and all(isinstance(c, mm.Lookup) and c.relation is b.constraints.unique_fields for c in r.check.body):
|
|
748
|
-
return
|
|
749
754
|
v0_reqs = []
|
|
750
755
|
# v0 expects a check with both the domain and the relation in it followed by the unique
|
|
751
756
|
# constraint, so we construct a logical with all of that in it
|
|
@@ -754,7 +759,7 @@ class Translator():
|
|
|
754
759
|
fields = c.args[0] # tuple of fields
|
|
755
760
|
assert isinstance(fields, tuple) and all(isinstance(f, mm.Field) for f in fields)
|
|
756
761
|
first_field = fields[0]
|
|
757
|
-
if isinstance(first_field.type, mm.Table):
|
|
762
|
+
if isinstance(first_field.type, mm.Table): #type: ignore
|
|
758
763
|
# skip union types for now
|
|
759
764
|
continue
|
|
760
765
|
assert isinstance(first_field, mm.Field)
|
|
@@ -779,17 +784,17 @@ class Translator():
|
|
|
779
784
|
))
|
|
780
785
|
return v0_reqs # type: ignore
|
|
781
786
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
787
|
+
check = v0.Check(
|
|
788
|
+
check=self.translate_node(r.check, r, ctx), # type: ignore
|
|
789
|
+
error=self.translate_node(r.error, r, ctx) if r.error else None, # type: ignore
|
|
790
|
+
annotations=self.translate_frozen(r.annotations, r, ctx) # type: ignore
|
|
791
|
+
)
|
|
792
|
+
return v0.Require(
|
|
793
|
+
engine=self.translate_reasoner(r.reasoner, r, Context.MODEL),
|
|
794
|
+
domain=self.translate_node(r.domain, r, ctx), # type: ignore
|
|
795
|
+
checks=(check,), # type: ignore
|
|
796
|
+
annotations=self.translate_frozen(r.annotations, r, ctx) # type: ignore
|
|
797
|
+
)
|
|
793
798
|
return None
|
|
794
799
|
|
|
795
800
|
# -----------------------------
|
|
@@ -1114,7 +1119,7 @@ class Translator():
|
|
|
1114
1119
|
# subtract 1 from the index to convert from 1-based to 0-based
|
|
1115
1120
|
tasks = []
|
|
1116
1121
|
for index in indices:
|
|
1117
|
-
arg = args[index]
|
|
1122
|
+
arg = l.args[index]
|
|
1118
1123
|
if isinstance(arg, mm.Literal):
|
|
1119
1124
|
if l.relation.fields[index].input:
|
|
1120
1125
|
new = mm.Literal(arg.type, arg.value + 1) # type: ignore
|
|
@@ -1122,7 +1127,7 @@ class Translator():
|
|
|
1122
1127
|
new = mm.Literal(arg.type, arg.value + 1) # type: ignore
|
|
1123
1128
|
tasks.append(v0.Lookup(
|
|
1124
1129
|
engine=None,
|
|
1125
|
-
relation=v0_builtins.eq,
|
|
1130
|
+
relation=v0_builtins.eq,
|
|
1126
1131
|
args=(
|
|
1127
1132
|
self.translate_value(tmps[index], lookup, ctx),
|
|
1128
1133
|
self.translate_value(new, lookup, ctx),
|
|
@@ -1131,9 +1136,9 @@ class Translator():
|
|
|
1131
1136
|
elif l.relation.fields[index].input:
|
|
1132
1137
|
tasks.append(v0.Lookup(
|
|
1133
1138
|
engine=None,
|
|
1134
|
-
relation=v0_builtins.plus,
|
|
1139
|
+
relation=v0_builtins.plus,
|
|
1135
1140
|
args=(
|
|
1136
|
-
self.translate_value(
|
|
1141
|
+
self.translate_value(arg, lookup, ctx),
|
|
1137
1142
|
v0.Literal(v0_types.Int128, 1),
|
|
1138
1143
|
self.translate_value(tmps[index], lookup, ctx),
|
|
1139
1144
|
),
|
|
@@ -1146,7 +1151,7 @@ class Translator():
|
|
|
1146
1151
|
args=(
|
|
1147
1152
|
self.translate_value(tmps[index], lookup, ctx),
|
|
1148
1153
|
v0.Literal(v0_types.Int128, 1),
|
|
1149
|
-
self.translate_value(
|
|
1154
|
+
self.translate_value(arg, lookup, ctx),
|
|
1150
1155
|
),
|
|
1151
1156
|
)
|
|
1152
1157
|
)
|
|
@@ -1217,7 +1222,10 @@ class Translator():
|
|
|
1217
1222
|
if target_type is None or not isinstance(arg, (v0.Var, v0.Literal)) or arg.type == target_type:
|
|
1218
1223
|
args.append(arg)
|
|
1219
1224
|
continue
|
|
1220
|
-
if
|
|
1225
|
+
if isinstance(arg, v0.Literal):
|
|
1226
|
+
# for literals, we can just create a new literal with the target type
|
|
1227
|
+
args.append(v0.Literal(type=target_type, value=arg.value))
|
|
1228
|
+
elif field.input:
|
|
1221
1229
|
cast_var = v0.Var(type=target_type, name="cast_var")
|
|
1222
1230
|
args.append(cast_var)
|
|
1223
1231
|
inputs.append(v0.Lookup(
|
relationalai/tools/cli/cli.py
CHANGED
|
@@ -5,6 +5,7 @@ For more documentation on these commands, visit: https://docs.relationalai.com
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
import sys
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
|
|
10
11
|
import click
|
|
@@ -86,5 +87,142 @@ def test():
|
|
|
86
87
|
console.print("test")
|
|
87
88
|
|
|
88
89
|
|
|
90
|
+
@cli.group()
|
|
91
|
+
def docs():
|
|
92
|
+
"""Generate and serve documentation site."""
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@docs.command()
|
|
97
|
+
@click.option(
|
|
98
|
+
"--output",
|
|
99
|
+
"-o",
|
|
100
|
+
default=None,
|
|
101
|
+
help="Output directory for generated documentation (defaults to docs/dist)",
|
|
102
|
+
)
|
|
103
|
+
def generate(output: str | None):
|
|
104
|
+
"""Generate static documentation site from models."""
|
|
105
|
+
from .docs import generate_docs
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
generate_docs(output_dir=output)
|
|
109
|
+
except click.ClickException as e:
|
|
110
|
+
# Print formatted error message and exit
|
|
111
|
+
console.print(f"[red]✗ Failed to generate documentation: {e.message}[/red]")
|
|
112
|
+
sys.exit(1)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
console.print(f"[red]✗ Failed to generate documentation: {e}[/red]")
|
|
115
|
+
sys.exit(1)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@docs.command()
|
|
119
|
+
@click.option(
|
|
120
|
+
"--port",
|
|
121
|
+
"-p",
|
|
122
|
+
default=8000,
|
|
123
|
+
type=int,
|
|
124
|
+
help="Port to serve documentation on (default: 8000)",
|
|
125
|
+
)
|
|
126
|
+
@click.option(
|
|
127
|
+
"--host",
|
|
128
|
+
default="127.0.0.1",
|
|
129
|
+
help="Host to bind to (default: 127.0.0.1)",
|
|
130
|
+
)
|
|
131
|
+
@click.option(
|
|
132
|
+
"--build-dir",
|
|
133
|
+
default=None,
|
|
134
|
+
help="Directory containing built documentation (defaults to docs/dist)",
|
|
135
|
+
)
|
|
136
|
+
@click.option(
|
|
137
|
+
"--no-open",
|
|
138
|
+
is_flag=True,
|
|
139
|
+
help="Don't open browser automatically",
|
|
140
|
+
)
|
|
141
|
+
def serve(port: int, host: str, build_dir: str | None, no_open: bool):
|
|
142
|
+
"""Serve generated documentation site."""
|
|
143
|
+
from .docs import find_docs_dir, serve_docs
|
|
144
|
+
|
|
145
|
+
# Determine build directory
|
|
146
|
+
if build_dir is None:
|
|
147
|
+
docs_dir = find_docs_dir()
|
|
148
|
+
build_dir = str(docs_dir / "dist")
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
serve_docs(
|
|
152
|
+
build_dir=build_dir,
|
|
153
|
+
host=host,
|
|
154
|
+
port=port,
|
|
155
|
+
open_browser=not no_open,
|
|
156
|
+
)
|
|
157
|
+
except click.ClickException as e:
|
|
158
|
+
# Print formatted error message and exit
|
|
159
|
+
console.print(f"[red]✗ Failed to start server: {e.message}[/red]")
|
|
160
|
+
sys.exit(1)
|
|
161
|
+
except Exception as e:
|
|
162
|
+
console.print(f"[red]✗ Failed to start server: {e}[/red]")
|
|
163
|
+
sys.exit(1)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@docs.command()
|
|
167
|
+
@click.option(
|
|
168
|
+
"--port",
|
|
169
|
+
"-p",
|
|
170
|
+
default=5173,
|
|
171
|
+
type=int,
|
|
172
|
+
help="Port to serve development server on (default: 5173)",
|
|
173
|
+
)
|
|
174
|
+
@click.option(
|
|
175
|
+
"--host",
|
|
176
|
+
default="127.0.0.1",
|
|
177
|
+
help="Host to bind to (default: 127.0.0.1)",
|
|
178
|
+
)
|
|
179
|
+
@click.option(
|
|
180
|
+
"--no-open",
|
|
181
|
+
is_flag=True,
|
|
182
|
+
help="Don't open browser automatically",
|
|
183
|
+
)
|
|
184
|
+
def dev(port: int, host: str, no_open: bool):
|
|
185
|
+
"""Start development server with hot module replacement (HMR).
|
|
186
|
+
|
|
187
|
+
This command runs Vite's dev server which provides:
|
|
188
|
+
- Hot module replacement - changes reflect instantly
|
|
189
|
+
- Fast refresh - no need to rebuild on every change
|
|
190
|
+
- Source maps for debugging
|
|
191
|
+
|
|
192
|
+
Use this for development instead of 'rai docs generate' + 'rai docs serve'.
|
|
193
|
+
"""
|
|
194
|
+
from .docs import dev_docs
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
dev_docs(
|
|
198
|
+
host=host,
|
|
199
|
+
port=port,
|
|
200
|
+
open_browser=not no_open,
|
|
201
|
+
)
|
|
202
|
+
except click.ClickException as e:
|
|
203
|
+
# Print formatted error message and exit
|
|
204
|
+
console.print(f"[red]✗ Failed to start dev server: {e.message}[/red]")
|
|
205
|
+
sys.exit(1)
|
|
206
|
+
except Exception as e:
|
|
207
|
+
console.print(f"[red]✗ Failed to start dev server: {e}[/red]")
|
|
208
|
+
sys.exit(1)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@docs.command(name="clean")
|
|
212
|
+
def clean_docs():
|
|
213
|
+
"""Remove auto generated files and directories."""
|
|
214
|
+
from .docs import cleanup_docs
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
cleanup_docs()
|
|
218
|
+
except click.ClickException as e:
|
|
219
|
+
# Print formatted error message and exit
|
|
220
|
+
console.print(f"[red]✗ Failed to cleanup: {e.message}[/red]")
|
|
221
|
+
sys.exit(1)
|
|
222
|
+
except Exception as e:
|
|
223
|
+
console.print(f"[red]✗ Failed to cleanup: {e}[/red]")
|
|
224
|
+
sys.exit(1)
|
|
225
|
+
|
|
226
|
+
|
|
89
227
|
if __name__ == "__main__":
|
|
90
228
|
cli()
|