relationalai 0.12.8__py3-none-any.whl → 0.12.10__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/__init__.py +9 -0
- relationalai/clients/__init__.py +2 -2
- relationalai/clients/local.py +571 -0
- relationalai/clients/snowflake.py +106 -83
- relationalai/debugging.py +5 -2
- relationalai/semantics/__init__.py +2 -2
- relationalai/semantics/internal/__init__.py +2 -2
- relationalai/semantics/internal/internal.py +53 -14
- relationalai/semantics/lqp/README.md +34 -0
- relationalai/semantics/lqp/compiler.py +1 -1
- relationalai/semantics/lqp/constructors.py +7 -0
- relationalai/semantics/lqp/executor.py +35 -39
- relationalai/semantics/lqp/intrinsics.py +4 -3
- relationalai/semantics/lqp/ir.py +4 -0
- relationalai/semantics/lqp/model2lqp.py +47 -14
- relationalai/semantics/lqp/passes.py +7 -4
- relationalai/semantics/lqp/rewrite/__init__.py +4 -1
- relationalai/semantics/lqp/rewrite/annotate_constraints.py +55 -0
- relationalai/semantics/lqp/rewrite/extract_keys.py +22 -3
- relationalai/semantics/lqp/rewrite/function_annotations.py +91 -56
- relationalai/semantics/lqp/rewrite/functional_dependencies.py +314 -0
- relationalai/semantics/lqp/rewrite/quantify_vars.py +14 -0
- relationalai/semantics/lqp/validators.py +3 -0
- relationalai/semantics/metamodel/builtins.py +10 -0
- relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +5 -4
- relationalai/semantics/metamodel/rewrite/flatten.py +10 -4
- relationalai/semantics/metamodel/typer/typer.py +13 -0
- relationalai/semantics/metamodel/types.py +2 -1
- relationalai/semantics/reasoners/graph/core.py +44 -53
- relationalai/semantics/rel/compiler.py +19 -1
- relationalai/semantics/tests/test_snapshot_abstract.py +3 -0
- relationalai/tools/debugger.py +4 -2
- relationalai/tools/qb_debugger.py +5 -3
- relationalai/util/otel_handler.py +10 -4
- {relationalai-0.12.8.dist-info → relationalai-0.12.10.dist-info}/METADATA +2 -2
- {relationalai-0.12.8.dist-info → relationalai-0.12.10.dist-info}/RECORD +39 -35
- {relationalai-0.12.8.dist-info → relationalai-0.12.10.dist-info}/WHEEL +0 -0
- {relationalai-0.12.8.dist-info → relationalai-0.12.10.dist-info}/entry_points.txt +0 -0
- {relationalai-0.12.8.dist-info → relationalai-0.12.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -20,7 +20,7 @@ from relationalai.semantics import (
|
|
|
20
20
|
count, sum, avg,
|
|
21
21
|
)
|
|
22
22
|
from relationalai.docutils import include_in_docs
|
|
23
|
-
from relationalai.semantics.internal import annotations
|
|
23
|
+
from relationalai.semantics.internal import annotations, AnyEntity
|
|
24
24
|
from relationalai.semantics.internal import internal as builder_internal # For primitive graph algorithms.
|
|
25
25
|
from relationalai.semantics.std.math import abs, isnan, isinf, maximum, natural_log, sqrt
|
|
26
26
|
from relationalai.semantics.std.integers import int64
|
|
@@ -158,7 +158,7 @@ class Graph():
|
|
|
158
158
|
f"but is a `{type(weighted).__name__}`."
|
|
159
159
|
)
|
|
160
160
|
assert isinstance(model, Model), (
|
|
161
|
-
"The `model` argument must be a `
|
|
161
|
+
"The `model` argument must be a `relationalai.semantics.Model`, "
|
|
162
162
|
f"but is a `{type(model).__name__}`."
|
|
163
163
|
)
|
|
164
164
|
self.directed = directed
|
|
@@ -355,7 +355,7 @@ class Graph():
|
|
|
355
355
|
@cached_property
|
|
356
356
|
def Node(self) -> Concept:
|
|
357
357
|
"""Lazily define and cache the self.Node concept."""
|
|
358
|
-
_Node = self._user_node_concept or self._model.Concept(self._NodeConceptStr)
|
|
358
|
+
_Node = self._user_node_concept or self._model.Concept(self._NodeConceptStr, extends=[AnyEntity])
|
|
359
359
|
_Node.annotate(annotations.track("graphs", "Node"))
|
|
360
360
|
return _Node
|
|
361
361
|
|
|
@@ -2304,14 +2304,14 @@ class Graph():
|
|
|
2304
2304
|
# neighbor_a_rel = self._neighbor_of(node_subset_from)
|
|
2305
2305
|
#
|
|
2306
2306
|
# domain_w = Relationship(f"{{node:{self._NodeConceptStr}}} is the domain of `w` in `common_neighbor(u, v, w)`")
|
|
2307
|
-
#
|
|
2308
|
-
# where(neighbor_a_rel(node_x, node_y)).define(domain_w(node_y))
|
|
2307
|
+
# where(neighbor_a_rel(node_a, node_b)).define(domain_w(node_b))
|
|
2309
2308
|
# neighbor_b_rel = self._neighbor_of(domain_w)
|
|
2310
2309
|
#
|
|
2311
2310
|
# node_constraint = []
|
|
2312
2311
|
#
|
|
2313
|
-
# #
|
|
2314
|
-
# #
|
|
2312
|
+
# # For this case only, we reverse the args of `neighbor_b_rel()`, which
|
|
2313
|
+
# # is allowed by the symmetry of `neighbor`, in order to take advantage
|
|
2314
|
+
# # of domain constraint on `neighbor_b_rel()`.
|
|
2315
2315
|
# where(
|
|
2316
2316
|
# *node_constraint,
|
|
2317
2317
|
# neighbor_a_rel(node_a, neighbor_node),
|
|
@@ -2748,15 +2748,15 @@ class Graph():
|
|
|
2748
2748
|
if node_subset is None:
|
|
2749
2749
|
# No constraint - use cached count_inneighbor relationship and all nodes
|
|
2750
2750
|
count_inneighbor_rel = self._count_inneighbor
|
|
2751
|
-
|
|
2751
|
+
node_constraint = []
|
|
2752
2752
|
else:
|
|
2753
2753
|
# Constrained to nodes in the subset - use constrained count_inneighbor relationship
|
|
2754
2754
|
count_inneighbor_rel = self._count_inneighbor_of(node_subset)
|
|
2755
|
-
|
|
2755
|
+
node_constraint = [node_subset(self.Node)]
|
|
2756
2756
|
|
|
2757
2757
|
# Apply the same indegree logic for both cases
|
|
2758
2758
|
where(
|
|
2759
|
-
|
|
2759
|
+
*node_constraint,
|
|
2760
2760
|
_indegree := where(count_inneighbor_rel(self.Node, Integer)).select(Integer) | 0,
|
|
2761
2761
|
).define(_indegree_rel(self.Node, _indegree))
|
|
2762
2762
|
|
|
@@ -2933,15 +2933,15 @@ class Graph():
|
|
|
2933
2933
|
if node_subset is None:
|
|
2934
2934
|
# No constraint - use cached count_outneighbor relationship and all nodes
|
|
2935
2935
|
count_outneighbor_rel = self._count_outneighbor
|
|
2936
|
-
|
|
2936
|
+
node_constraint = []
|
|
2937
2937
|
else:
|
|
2938
2938
|
# Constrained to nodes in the subset - use constrained count_outneighbor relationship
|
|
2939
2939
|
count_outneighbor_rel = self._count_outneighbor_of(node_subset)
|
|
2940
|
-
|
|
2940
|
+
node_constraint = [node_subset(self.Node)]
|
|
2941
2941
|
|
|
2942
2942
|
# Apply the same outdegree logic for both cases
|
|
2943
2943
|
where(
|
|
2944
|
-
|
|
2944
|
+
*node_constraint,
|
|
2945
2945
|
_outdegree := where(count_outneighbor_rel(self.Node, Integer)).select(Integer) | 0,
|
|
2946
2946
|
).define(_outdegree_rel(self.Node, _outdegree))
|
|
2947
2947
|
|
|
@@ -3099,12 +3099,12 @@ class Graph():
|
|
|
3099
3099
|
node, neighbor, weight = self.Node.ref(), self.Node.ref(), Float.ref()
|
|
3100
3100
|
|
|
3101
3101
|
if node_subset is None:
|
|
3102
|
-
node_constraint =
|
|
3102
|
+
node_constraint = [] # No constraint on nodes.
|
|
3103
3103
|
else:
|
|
3104
|
-
node_constraint = node_subset(node) # Nodes constrained to given subset.
|
|
3104
|
+
node_constraint = [node_subset(node)] # Nodes constrained to given subset.
|
|
3105
3105
|
|
|
3106
3106
|
where(
|
|
3107
|
-
node_constraint,
|
|
3107
|
+
*node_constraint,
|
|
3108
3108
|
weighted_degree_no_loops := sum(neighbor, weight).per(node).where(
|
|
3109
3109
|
self._weight(node, neighbor, weight),
|
|
3110
3110
|
node != neighbor,
|
|
@@ -3172,7 +3172,7 @@ class Graph():
|
|
|
3172
3172
|
>>> define(n1, n2, n3)
|
|
3173
3173
|
>>> define(
|
|
3174
3174
|
... Edge.new(src=n1, dst=n2, weight=1.0),
|
|
3175
|
-
... Edge.new(src=n2, dst=n1, weight
|
|
3175
|
+
... Edge.new(src=n2, dst=n1, weight=0.0),
|
|
3176
3176
|
... Edge.new(src=n2, dst=n3, weight=1.0),
|
|
3177
3177
|
... )
|
|
3178
3178
|
>>>
|
|
@@ -3186,7 +3186,7 @@ class Graph():
|
|
|
3186
3186
|
... ).inspect()
|
|
3187
3187
|
▰▰▰▰ Setup complete
|
|
3188
3188
|
id node_weighted_indegree
|
|
3189
|
-
0 1
|
|
3189
|
+
0 1 0.0
|
|
3190
3190
|
1 2 1.0
|
|
3191
3191
|
2 3 1.0
|
|
3192
3192
|
>>>
|
|
@@ -3218,9 +3218,6 @@ class Graph():
|
|
|
3218
3218
|
weighted_outdegree
|
|
3219
3219
|
|
|
3220
3220
|
"""
|
|
3221
|
-
# TODO: It looks like the weights in the example in the docstring above
|
|
3222
|
-
# are holdovers from a version of the library that did not disallow
|
|
3223
|
-
# negative weights. Need to update the example to use only non-negative weights.
|
|
3224
3221
|
if of is None:
|
|
3225
3222
|
return self._weighted_indegree
|
|
3226
3223
|
else:
|
|
@@ -3251,20 +3248,15 @@ class Graph():
|
|
|
3251
3248
|
# Choose the appropriate node set
|
|
3252
3249
|
if node_subset is None:
|
|
3253
3250
|
# No constraint - use all nodes
|
|
3254
|
-
|
|
3251
|
+
node_constraint = []
|
|
3255
3252
|
else:
|
|
3256
3253
|
# Constrained to nodes in the subset
|
|
3257
|
-
|
|
3258
|
-
# TODO: In a future cleanup pass, replace `node_set` with a `node_constraint`
|
|
3259
|
-
# that replaces the `node_set(self.Node)` in the where clause below,
|
|
3260
|
-
# and generates only `self.Node` (rather than `self.Node(self.Node)`)
|
|
3261
|
-
# in the `subset is None` case. This applies to a couple other
|
|
3262
|
-
# degree-of type relations as well.
|
|
3254
|
+
node_constraint = [node_subset(self.Node)]
|
|
3263
3255
|
|
|
3264
3256
|
# Apply the weighted indegree logic for both cases
|
|
3265
3257
|
src, inweight = self.Node.ref(), Float.ref()
|
|
3266
3258
|
where(
|
|
3267
|
-
|
|
3259
|
+
*node_constraint,
|
|
3268
3260
|
_weighted_indegree := sum(src, inweight).per(self.Node).where(self._weight(src, self.Node, inweight)) | 0.0,
|
|
3269
3261
|
).define(_weighted_indegree_rel(self.Node, _weighted_indegree))
|
|
3270
3262
|
|
|
@@ -3324,7 +3316,7 @@ class Graph():
|
|
|
3324
3316
|
>>> define(n1, n2, n3)
|
|
3325
3317
|
>>> define(
|
|
3326
3318
|
... Edge.new(src=n1, dst=n2, weight=1.0),
|
|
3327
|
-
... Edge.new(src=n2, dst=n1, weight
|
|
3319
|
+
... Edge.new(src=n2, dst=n1, weight=0.0),
|
|
3328
3320
|
... Edge.new(src=n2, dst=n3, weight=1.0),
|
|
3329
3321
|
... )
|
|
3330
3322
|
>>>
|
|
@@ -3339,7 +3331,7 @@ class Graph():
|
|
|
3339
3331
|
▰▰▰▰ Setup complete
|
|
3340
3332
|
id node_weighted_outdegree
|
|
3341
3333
|
0 1 1.0
|
|
3342
|
-
1 2
|
|
3334
|
+
1 2 1.0
|
|
3343
3335
|
2 3 0.0
|
|
3344
3336
|
>>>
|
|
3345
3337
|
>>> # 4. Use 'of' parameter to constrain the set of nodes to compute weighted outdegree of
|
|
@@ -3355,7 +3347,7 @@ class Graph():
|
|
|
3355
3347
|
▰▰▰▰ Setup complete
|
|
3356
3348
|
id node_weighted_outdegree
|
|
3357
3349
|
0 1 1.0
|
|
3358
|
-
1 2
|
|
3350
|
+
1 2 1.0
|
|
3359
3351
|
|
|
3360
3352
|
Notes
|
|
3361
3353
|
-----
|
|
@@ -3404,15 +3396,15 @@ class Graph():
|
|
|
3404
3396
|
# Choose the appropriate node set
|
|
3405
3397
|
if node_subset is None:
|
|
3406
3398
|
# No constraint - use all nodes
|
|
3407
|
-
|
|
3399
|
+
node_constraint = []
|
|
3408
3400
|
else:
|
|
3409
3401
|
# Constrained to nodes in the subset
|
|
3410
|
-
|
|
3402
|
+
node_constraint = [node_subset(self.Node)]
|
|
3411
3403
|
|
|
3412
3404
|
# Apply the weighted outdegree logic for both cases
|
|
3413
3405
|
dst, outweight = self.Node.ref(), Float.ref()
|
|
3414
3406
|
where(
|
|
3415
|
-
|
|
3407
|
+
*node_constraint,
|
|
3416
3408
|
_weighted_outdegree := sum(dst, outweight).per(self.Node).where(self._weight(self.Node, dst, outweight)) | 0.0,
|
|
3417
3409
|
).define(_weighted_outdegree_rel(self.Node, _weighted_outdegree))
|
|
3418
3410
|
|
|
@@ -4537,12 +4529,12 @@ class Graph():
|
|
|
4537
4529
|
_triangle_count_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} belongs to {{count:Integer}} triangles")
|
|
4538
4530
|
|
|
4539
4531
|
if node_subset is None:
|
|
4540
|
-
node_constraint =
|
|
4532
|
+
node_constraint = [] # No constraint on nodes.
|
|
4541
4533
|
else:
|
|
4542
|
-
node_constraint = node_subset(self.Node) # Nodes constrained to given subset.
|
|
4534
|
+
node_constraint = [node_subset(self.Node)] # Nodes constrained to given subset.
|
|
4543
4535
|
|
|
4544
4536
|
where(
|
|
4545
|
-
node_constraint,
|
|
4537
|
+
*node_constraint,
|
|
4546
4538
|
_count := self._nonzero_triangle_count_fragment(self.Node) | 0
|
|
4547
4539
|
).define(_triangle_count_rel(self.Node, _count))
|
|
4548
4540
|
|
|
@@ -4819,16 +4811,16 @@ class Graph():
|
|
|
4819
4811
|
if node_subset is None:
|
|
4820
4812
|
degree_no_self_rel = self._degree_no_self
|
|
4821
4813
|
triangle_count_rel = self._triangle_count
|
|
4822
|
-
node_constraint =
|
|
4814
|
+
node_constraint = [] # No constraint on nodes.
|
|
4823
4815
|
else:
|
|
4824
4816
|
degree_no_self_rel = self._degree_no_self_of(node_subset)
|
|
4825
4817
|
triangle_count_rel = self._triangle_count_of(node_subset)
|
|
4826
|
-
node_constraint = node_subset(node) # Nodes constrained to given subset.
|
|
4818
|
+
node_constraint = [node_subset(node)] # Nodes constrained to given subset.
|
|
4827
4819
|
|
|
4828
4820
|
degree_no_self = Integer.ref()
|
|
4829
4821
|
triangle_count = Integer.ref()
|
|
4830
4822
|
where(
|
|
4831
|
-
node_constraint,
|
|
4823
|
+
*node_constraint,
|
|
4832
4824
|
_lcc := where(
|
|
4833
4825
|
degree_no_self_rel(node, degree_no_self),
|
|
4834
4826
|
triangle_count_rel(node, triangle_count),
|
|
@@ -4866,12 +4858,12 @@ class Graph():
|
|
|
4866
4858
|
node, neighbor = self.Node.ref(), self.Node.ref()
|
|
4867
4859
|
|
|
4868
4860
|
if node_subset is None:
|
|
4869
|
-
node_constraint =
|
|
4861
|
+
node_constraint = [] # No constraint on nodes.
|
|
4870
4862
|
else:
|
|
4871
|
-
node_constraint = node_subset(node) # Nodes constrained to given subset.
|
|
4863
|
+
node_constraint = [node_subset(node)] # Nodes constrained to given subset.
|
|
4872
4864
|
|
|
4873
4865
|
where(
|
|
4874
|
-
node_constraint,
|
|
4866
|
+
*node_constraint,
|
|
4875
4867
|
_dns := count(neighbor).per(node).where(self._no_loop_edge(node, neighbor)) | 0,
|
|
4876
4868
|
).define(_degree_no_self_rel(node, _dns))
|
|
4877
4869
|
|
|
@@ -7296,7 +7288,7 @@ class Graph():
|
|
|
7296
7288
|
|
|
7297
7289
|
# TODO: Optimization opportunity. In some of the cases below
|
|
7298
7290
|
# (unweighted in particular), the node_constraint is redundant with
|
|
7299
|
-
# the constraints baked into the
|
|
7291
|
+
# the constraints baked into the _count_outneighbor_of and
|
|
7300
7292
|
# _outneighbor_of relationships. The join with node_constraint
|
|
7301
7293
|
# could be eliminated in those cases. Possibly also relevant to
|
|
7302
7294
|
# other domain-constrained relations.
|
|
@@ -7366,19 +7358,18 @@ class Graph():
|
|
|
7366
7358
|
# Define cosine similarity logic for both weighted and unweighted cases.
|
|
7367
7359
|
if not self.weighted:
|
|
7368
7360
|
# Unweighted case: use count of common outneighbors.
|
|
7369
|
-
|
|
7361
|
+
count_outneighbor_u, count_outneighbor_v = Integer.ref(), Integer.ref()
|
|
7370
7362
|
common_outneighbor_node = self.Node.ref()
|
|
7371
|
-
score = Float.ref()
|
|
7372
7363
|
|
|
7373
7364
|
where(
|
|
7374
7365
|
*node_constraints,
|
|
7375
|
-
count_outneighbor_u_rel(node_u,
|
|
7376
|
-
count_outneighbor_v_rel(node_v,
|
|
7366
|
+
count_outneighbor_u_rel(node_u, count_outneighbor_u),
|
|
7367
|
+
count_outneighbor_v_rel(node_v, count_outneighbor_v),
|
|
7377
7368
|
c_common := count(common_outneighbor_node).per(node_u, node_v).where(
|
|
7378
7369
|
outneighbor_u_rel(node_u, common_outneighbor_node),
|
|
7379
7370
|
outneighbor_v_rel(node_v, common_outneighbor_node),
|
|
7380
7371
|
),
|
|
7381
|
-
score := c_common / sqrt(
|
|
7372
|
+
score := c_common / sqrt(count_outneighbor_u * count_outneighbor_v),
|
|
7382
7373
|
).define(
|
|
7383
7374
|
_cosine_similarity_rel(node_u, node_v, score)
|
|
7384
7375
|
)
|
|
@@ -8186,13 +8177,13 @@ class Graph():
|
|
|
8186
8177
|
neighbor_node = self.Node.ref()
|
|
8187
8178
|
if node_subset is not None:
|
|
8188
8179
|
neighbor_rel = self._neighbor_of(node_subset)
|
|
8189
|
-
node_constraint = node_subset(self.Node)
|
|
8180
|
+
node_constraint = [node_subset(self.Node)]
|
|
8190
8181
|
else:
|
|
8191
8182
|
neighbor_rel = self._neighbor
|
|
8192
|
-
node_constraint =
|
|
8183
|
+
node_constraint = []
|
|
8193
8184
|
|
|
8194
8185
|
where(
|
|
8195
|
-
node_constraint,
|
|
8186
|
+
*node_constraint,
|
|
8196
8187
|
not_(neighbor_rel(self.Node, neighbor_node))
|
|
8197
8188
|
).define(_isolated_node_rel(self.Node))
|
|
8198
8189
|
|
|
@@ -12,7 +12,7 @@ from relationalai.semantics.metamodel.util import OrderedSet, group_by, NameCach
|
|
|
12
12
|
from relationalai.semantics.rel import rel, rel_utils as u, builtins as rel_bt
|
|
13
13
|
|
|
14
14
|
from ..metamodel.rewrite import (Flatten, ExtractNestedLogicals, DNFUnionSplitter, DischargeConstraints, FormatOutputs)
|
|
15
|
-
from ..lqp.rewrite import CDC, ExtractCommon, ExtractKeys, FunctionAnnotations, QuantifyVars, Splinter
|
|
15
|
+
from ..lqp.rewrite import CDC, ExtractCommon, ExtractKeys, FunctionAnnotations, QuantifyVars, Splinter, SplitMultiCheckRequires
|
|
16
16
|
|
|
17
17
|
import math
|
|
18
18
|
|
|
@@ -24,6 +24,7 @@ import math
|
|
|
24
24
|
class Compiler(c.Compiler):
|
|
25
25
|
def __init__(self):
|
|
26
26
|
super().__init__([
|
|
27
|
+
SplitMultiCheckRequires(),
|
|
27
28
|
FunctionAnnotations(),
|
|
28
29
|
DischargeConstraints(),
|
|
29
30
|
Checker(),
|
|
@@ -696,7 +697,24 @@ class ModelToRel:
|
|
|
696
697
|
rel_annos = cast(Tuple[rel.Annotation, ...], self.handle_list(filtered_annos))
|
|
697
698
|
return rel_annos
|
|
698
699
|
|
|
700
|
+
# standard handling mistreats the integer arg of ranked `@function(:checked, k)`
|
|
701
|
+
def handle_ranked_function_annotation(self, n: ir.Annotation):
|
|
702
|
+
assert n.relation == bt.function_ranked and len(n.args) == 2
|
|
703
|
+
checked_lit = n.args[0]
|
|
704
|
+
rank_lit = n.args[1]
|
|
705
|
+
assert isinstance(checked_lit, ir.Literal) and isinstance(rank_lit, ir.Literal)
|
|
706
|
+
checked = rel.MetaValue(checked_lit.value)
|
|
707
|
+
rank = rank_lit.value
|
|
708
|
+
return rel.Annotation(
|
|
709
|
+
n.relation.name,
|
|
710
|
+
(checked, rank)
|
|
711
|
+
)
|
|
712
|
+
|
|
699
713
|
def handle_annotation(self, n: ir.Annotation):
|
|
714
|
+
# special treatment for (ranked) @function(:checked, k)
|
|
715
|
+
if n.relation == bt.function_ranked:
|
|
716
|
+
return self.handle_ranked_function_annotation(n)
|
|
717
|
+
|
|
700
718
|
# we know that annotations won't have vars, so we can ignore that type warning
|
|
701
719
|
return rel.Annotation(
|
|
702
720
|
n.relation.name,
|
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
import uuid
|
|
4
4
|
import sys
|
|
5
|
+
import datetime
|
|
5
6
|
from abc import abstractmethod, ABC
|
|
6
7
|
|
|
7
8
|
from relationalai.semantics.tests.logging import Capturer
|
|
@@ -47,6 +48,8 @@ class AbstractSnapshotTest(ABC):
|
|
|
47
48
|
'use_sql': use_sql,
|
|
48
49
|
'reasoner.rule.use_lqp': use_lqp,
|
|
49
50
|
'keep_model': False,
|
|
51
|
+
# fix the current time to keep snapshots stable
|
|
52
|
+
'datetime_now': datetime.datetime.fromisoformat("2025-12-01T12:00:00+00:00"),
|
|
50
53
|
}
|
|
51
54
|
if use_direct_access:
|
|
52
55
|
# for direct access we can not use user/password authentication
|
relationalai/tools/debugger.py
CHANGED
|
@@ -3,6 +3,8 @@ import re
|
|
|
3
3
|
from nicegui import ui
|
|
4
4
|
import json
|
|
5
5
|
|
|
6
|
+
from relationalai.debugging import DEBUG_LOG_FILE
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
last_mod_time = None
|
|
8
10
|
current_json_objects = []
|
|
@@ -141,12 +143,12 @@ def poll():
|
|
|
141
143
|
global current_json_objects
|
|
142
144
|
# Check the last modification time of the file
|
|
143
145
|
try:
|
|
144
|
-
mod_time = os.path.getmtime(
|
|
146
|
+
mod_time = os.path.getmtime(DEBUG_LOG_FILE)
|
|
145
147
|
if last_mod_time is None or mod_time > last_mod_time:
|
|
146
148
|
last_mod_time = mod_time
|
|
147
149
|
# File has changed, read and parse the new content
|
|
148
150
|
new_objects = []
|
|
149
|
-
with open(
|
|
151
|
+
with open(DEBUG_LOG_FILE, 'r') as file:
|
|
150
152
|
for line in file:
|
|
151
153
|
try:
|
|
152
154
|
# Parse each JSON object and add it to the list
|
|
@@ -3,6 +3,8 @@ import re
|
|
|
3
3
|
from nicegui import ui
|
|
4
4
|
import json
|
|
5
5
|
|
|
6
|
+
from relationalai.debugging import DEBUG_LOG_FILE
|
|
7
|
+
|
|
6
8
|
#--------------------------------------------------
|
|
7
9
|
# Terminal nodes
|
|
8
10
|
#--------------------------------------------------
|
|
@@ -12,7 +14,7 @@ TERMINAL_NODES = [
|
|
|
12
14
|
]
|
|
13
15
|
|
|
14
16
|
#--------------------------------------------------
|
|
15
|
-
#
|
|
17
|
+
# Debug log helpers
|
|
16
18
|
#--------------------------------------------------
|
|
17
19
|
|
|
18
20
|
class SpanNode:
|
|
@@ -344,11 +346,11 @@ def poll():
|
|
|
344
346
|
global current_json_objects
|
|
345
347
|
# Check the last modification time of the file
|
|
346
348
|
try:
|
|
347
|
-
mod_time = os.path.getmtime(
|
|
349
|
+
mod_time = os.path.getmtime(DEBUG_LOG_FILE)
|
|
348
350
|
if last_mod_time is None or mod_time > last_mod_time:
|
|
349
351
|
last_mod_time = mod_time
|
|
350
352
|
# File has changed, read and parse the new content
|
|
351
|
-
with open(
|
|
353
|
+
with open(DEBUG_LOG_FILE, 'r') as file:
|
|
352
354
|
content = file.read()
|
|
353
355
|
if content:
|
|
354
356
|
new_tree = parse_jsonl_to_tree(content)
|
|
@@ -20,6 +20,7 @@ from relationalai.debugging import logger, Span, filter_span_attrs, otel_traceid
|
|
|
20
20
|
from relationalai.clients.snowflake import Resources
|
|
21
21
|
|
|
22
22
|
MAX_PAYLOAD_SIZE = 25*1024 # 25KB
|
|
23
|
+
MAX_ATTRIBUTE_LENGTH = 1000
|
|
23
24
|
_otel_initialized = False
|
|
24
25
|
|
|
25
26
|
|
|
@@ -44,8 +45,6 @@ class CachedSpanExporter(SpanExporter):
|
|
|
44
45
|
|
|
45
46
|
def get_spans(self):
|
|
46
47
|
return self.spans
|
|
47
|
-
|
|
48
|
-
|
|
49
48
|
class NativeAppSpanExporter(SpanExporter):
|
|
50
49
|
def __init__(self, resource:Resources, app_name:str):
|
|
51
50
|
self.resource = resource
|
|
@@ -252,6 +251,12 @@ def encode_otlp_attribute(k, v):
|
|
|
252
251
|
try:
|
|
253
252
|
valueObj = {}
|
|
254
253
|
if isinstance(v, str):
|
|
254
|
+
# Truncate metamodel attribute value if it's too large
|
|
255
|
+
if k == "metamodel" and len(v) > MAX_ATTRIBUTE_LENGTH:
|
|
256
|
+
original_length = len(v)
|
|
257
|
+
v = v[:MAX_ATTRIBUTE_LENGTH] + f"... (truncated from {original_length} chars)"
|
|
258
|
+
logging.warning(f"{k} attribute truncated from {original_length} to {MAX_ATTRIBUTE_LENGTH} characters")
|
|
259
|
+
|
|
255
260
|
valueObj = {
|
|
256
261
|
"stringValue": html.escape(v).replace("\n", "; ")
|
|
257
262
|
}
|
|
@@ -369,7 +374,7 @@ def encode_spans_to_otlp_json(spans: Sequence[ReadableSpan], max_bytes: int) ->
|
|
|
369
374
|
except Exception as e:
|
|
370
375
|
logging.warning(f"Error encoding resource to OTLP JSON: {e}")
|
|
371
376
|
return "", spans # Return the unencoded spans if resource encoding fails
|
|
372
|
-
|
|
377
|
+
|
|
373
378
|
# TODO encode pyrel version for real
|
|
374
379
|
header_str = f'{{"resourceSpans": [{{"resource":{resource_str}, "scopeSpans": [{{"scope":{{"name":"pyrel", "version":"v0.4.0"}},"spans":['
|
|
375
380
|
footer_str = ']}]}]}'
|
|
@@ -378,7 +383,7 @@ def encode_spans_to_otlp_json(spans: Sequence[ReadableSpan], max_bytes: int) ->
|
|
|
378
383
|
encoded_spans = []
|
|
379
384
|
for span in spans:
|
|
380
385
|
try:
|
|
381
|
-
span_str = encode_span_to_otlp_json(span)
|
|
386
|
+
span_str = encode_span_to_otlp_json(span)
|
|
382
387
|
except Exception as e:
|
|
383
388
|
logging.warning(f"Error encoding span {span}: {e}")
|
|
384
389
|
continue # Skip this span if encoding fails
|
|
@@ -460,6 +465,7 @@ def enable_otel_export(client_resources: Resources, app_name):
|
|
|
460
465
|
if _otel_initialized:
|
|
461
466
|
logging.warning("OTel already initialized, skipping.")
|
|
462
467
|
return
|
|
468
|
+
|
|
463
469
|
_otel_initialized = True
|
|
464
470
|
|
|
465
471
|
if TRACE_PROVIDER is None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: relationalai
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.10
|
|
4
4
|
Summary: RelationalAI Library and CLI
|
|
5
5
|
Author-email: RelationalAI <support@relational.ai>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -11,7 +11,7 @@ Requires-Dist: colorama
|
|
|
11
11
|
Requires-Dist: cryptography
|
|
12
12
|
Requires-Dist: gravis
|
|
13
13
|
Requires-Dist: inquirerpy
|
|
14
|
-
Requires-Dist: lqp==0.1.
|
|
14
|
+
Requires-Dist: lqp==0.1.19
|
|
15
15
|
Requires-Dist: nicegui==2.16.1
|
|
16
16
|
Requires-Dist: numpy<2
|
|
17
17
|
Requires-Dist: opentelemetry-api
|