inputlayer-client-dev 0.1.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.
- inputlayer/__init__.py +151 -0
- inputlayer/_ast.py +133 -0
- inputlayer/_naming.py +44 -0
- inputlayer/_protocol.py +225 -0
- inputlayer/_proxy.py +244 -0
- inputlayer/aggregations.py +126 -0
- inputlayer/auth.py +70 -0
- inputlayer/client.py +178 -0
- inputlayer/client_sync.py +231 -0
- inputlayer/compiler.py +672 -0
- inputlayer/connection.py +314 -0
- inputlayer/derived.py +141 -0
- inputlayer/exceptions.py +76 -0
- inputlayer/functions.py +285 -0
- inputlayer/index.py +40 -0
- inputlayer/knowledge_graph.py +589 -0
- inputlayer/migrations/__init__.py +51 -0
- inputlayer/migrations/autodetector.py +120 -0
- inputlayer/migrations/cli.py +254 -0
- inputlayer/migrations/executor.py +95 -0
- inputlayer/migrations/loader.py +91 -0
- inputlayer/migrations/operations.py +298 -0
- inputlayer/migrations/recorder.py +44 -0
- inputlayer/migrations/state.py +107 -0
- inputlayer/migrations/writer.py +183 -0
- inputlayer/notifications.py +83 -0
- inputlayer/py.typed +0 -0
- inputlayer/relation.py +102 -0
- inputlayer/result.py +89 -0
- inputlayer/session.py +75 -0
- inputlayer/types.py +192 -0
- inputlayer_client_dev-0.1.0.dist-info/METADATA +364 -0
- inputlayer_client_dev-0.1.0.dist-info/RECORD +35 -0
- inputlayer_client_dev-0.1.0.dist-info/WHEEL +4 -0
- inputlayer_client_dev-0.1.0.dist-info/entry_points.txt +2 -0
inputlayer/__init__.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""inputlayer - Python Object-Logic Mapper for InputLayer knowledge graph engine."""
|
|
2
|
+
|
|
3
|
+
# Core types
|
|
4
|
+
from inputlayer.types import Timestamp, Vector, VectorInt8
|
|
5
|
+
|
|
6
|
+
# Relation system
|
|
7
|
+
from inputlayer.relation import Relation
|
|
8
|
+
from inputlayer.derived import Derived, From, RuleClause
|
|
9
|
+
|
|
10
|
+
# Aggregations
|
|
11
|
+
from inputlayer.aggregations import (
|
|
12
|
+
avg,
|
|
13
|
+
count,
|
|
14
|
+
count_distinct,
|
|
15
|
+
max_,
|
|
16
|
+
min_,
|
|
17
|
+
sum_,
|
|
18
|
+
top_k,
|
|
19
|
+
top_k_threshold,
|
|
20
|
+
within_radius,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Functions (re-export all)
|
|
24
|
+
from inputlayer import functions
|
|
25
|
+
|
|
26
|
+
# Index
|
|
27
|
+
from inputlayer.index import HnswIndex
|
|
28
|
+
|
|
29
|
+
# Exceptions
|
|
30
|
+
from inputlayer.exceptions import (
|
|
31
|
+
AuthenticationError,
|
|
32
|
+
CannotDropError,
|
|
33
|
+
ConnectionError,
|
|
34
|
+
IndexNotFoundError,
|
|
35
|
+
InputLayerError,
|
|
36
|
+
InternalError,
|
|
37
|
+
KnowledgeGraphExistsError,
|
|
38
|
+
KnowledgeGraphNotFoundError,
|
|
39
|
+
PermissionError,
|
|
40
|
+
QueryTimeoutError,
|
|
41
|
+
RelationNotFoundError,
|
|
42
|
+
RuleNotFoundError,
|
|
43
|
+
SchemaConflictError,
|
|
44
|
+
ValidationError,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Result
|
|
48
|
+
from inputlayer.result import ResultSet
|
|
49
|
+
|
|
50
|
+
# Client
|
|
51
|
+
from inputlayer.client import InputLayer
|
|
52
|
+
from inputlayer.client_sync import InputLayerSync
|
|
53
|
+
|
|
54
|
+
# Knowledge Graph
|
|
55
|
+
from inputlayer.knowledge_graph import (
|
|
56
|
+
ClearResult,
|
|
57
|
+
ColumnInfo,
|
|
58
|
+
DeleteResult,
|
|
59
|
+
ExplainResult,
|
|
60
|
+
IndexInfo,
|
|
61
|
+
IndexStats,
|
|
62
|
+
InsertResult,
|
|
63
|
+
KnowledgeGraph,
|
|
64
|
+
RelationDescription,
|
|
65
|
+
RelationInfo,
|
|
66
|
+
RuleInfo,
|
|
67
|
+
ServerStatus,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Auth
|
|
71
|
+
from inputlayer.auth import AclEntry, ApiKeyInfo, UserInfo
|
|
72
|
+
|
|
73
|
+
# Session
|
|
74
|
+
from inputlayer.session import Session
|
|
75
|
+
|
|
76
|
+
# Notifications
|
|
77
|
+
from inputlayer.notifications import NotificationEvent
|
|
78
|
+
|
|
79
|
+
# Migrations
|
|
80
|
+
from inputlayer.migrations import Migration
|
|
81
|
+
|
|
82
|
+
__version__ = "0.1.0"
|
|
83
|
+
|
|
84
|
+
__all__ = [
|
|
85
|
+
# Types
|
|
86
|
+
"Vector",
|
|
87
|
+
"VectorInt8",
|
|
88
|
+
"Timestamp",
|
|
89
|
+
# Relation
|
|
90
|
+
"Relation",
|
|
91
|
+
"Derived",
|
|
92
|
+
"From",
|
|
93
|
+
"RuleClause",
|
|
94
|
+
# Aggregations
|
|
95
|
+
"count",
|
|
96
|
+
"count_distinct",
|
|
97
|
+
"sum_",
|
|
98
|
+
"min_",
|
|
99
|
+
"max_",
|
|
100
|
+
"avg",
|
|
101
|
+
"top_k",
|
|
102
|
+
"top_k_threshold",
|
|
103
|
+
"within_radius",
|
|
104
|
+
# Functions
|
|
105
|
+
"functions",
|
|
106
|
+
# Index
|
|
107
|
+
"HnswIndex",
|
|
108
|
+
# Exceptions
|
|
109
|
+
"InputLayerError",
|
|
110
|
+
"ConnectionError",
|
|
111
|
+
"AuthenticationError",
|
|
112
|
+
"SchemaConflictError",
|
|
113
|
+
"ValidationError",
|
|
114
|
+
"QueryTimeoutError",
|
|
115
|
+
"PermissionError",
|
|
116
|
+
"KnowledgeGraphNotFoundError",
|
|
117
|
+
"KnowledgeGraphExistsError",
|
|
118
|
+
"CannotDropError",
|
|
119
|
+
"RelationNotFoundError",
|
|
120
|
+
"RuleNotFoundError",
|
|
121
|
+
"IndexNotFoundError",
|
|
122
|
+
"InternalError",
|
|
123
|
+
# Result
|
|
124
|
+
"ResultSet",
|
|
125
|
+
# Client
|
|
126
|
+
"InputLayer",
|
|
127
|
+
"InputLayerSync",
|
|
128
|
+
# KG
|
|
129
|
+
"KnowledgeGraph",
|
|
130
|
+
"RelationInfo",
|
|
131
|
+
"RelationDescription",
|
|
132
|
+
"ColumnInfo",
|
|
133
|
+
"RuleInfo",
|
|
134
|
+
"IndexInfo",
|
|
135
|
+
"IndexStats",
|
|
136
|
+
"InsertResult",
|
|
137
|
+
"DeleteResult",
|
|
138
|
+
"ClearResult",
|
|
139
|
+
"ExplainResult",
|
|
140
|
+
"ServerStatus",
|
|
141
|
+
# Auth
|
|
142
|
+
"UserInfo",
|
|
143
|
+
"ApiKeyInfo",
|
|
144
|
+
"AclEntry",
|
|
145
|
+
# Session
|
|
146
|
+
"Session",
|
|
147
|
+
# Notifications
|
|
148
|
+
"NotificationEvent",
|
|
149
|
+
# Migrations
|
|
150
|
+
"Migration",
|
|
151
|
+
]
|
inputlayer/_ast.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Internal AST nodes for expression trees compiled to Datalog."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# ── Base ──────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class Expr:
|
|
13
|
+
"""Base class for all expression AST nodes."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class BoolExpr:
|
|
18
|
+
"""Base class for boolean expression AST nodes (conditions)."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ── Leaf nodes ────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class Column(Expr):
|
|
25
|
+
"""Reference to a relation column."""
|
|
26
|
+
relation: str
|
|
27
|
+
name: str
|
|
28
|
+
ref_alias: str | None = None # For self-join disambiguation
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def qualified(self) -> str:
|
|
32
|
+
prefix = self.ref_alias or self.relation
|
|
33
|
+
return f"{prefix}.{self.name}"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class Literal(Expr):
|
|
38
|
+
"""A constant value."""
|
|
39
|
+
value: Any # int, float, str, bool, list (vector), None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ── Arithmetic ────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class Arithmetic(Expr):
|
|
46
|
+
"""Binary arithmetic: +, -, *, /, %."""
|
|
47
|
+
op: str # "+", "-", "*", "/", "%"
|
|
48
|
+
left: Expr
|
|
49
|
+
right: Expr
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# ── Function call ─────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True)
|
|
55
|
+
class FuncCall(Expr):
|
|
56
|
+
"""Built-in function call: distance(V1, V2), upper(S), etc."""
|
|
57
|
+
name: str
|
|
58
|
+
args: tuple[Expr, ...] = ()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# ── Aggregation ───────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
@dataclass(frozen=True)
|
|
64
|
+
class AggExpr(Expr):
|
|
65
|
+
"""Aggregation expression: count<X>, sum<X>, top_k<k, ...>, etc."""
|
|
66
|
+
func: str # "count", "sum", "min", "max", "avg", "count_distinct",
|
|
67
|
+
# "top_k", "top_k_threshold", "within_radius"
|
|
68
|
+
column: Expr | None = None # The aggregated column (None for count(*))
|
|
69
|
+
params: tuple[Any, ...] = () # Extra params (k, threshold, etc.)
|
|
70
|
+
passthrough: tuple[Expr, ...] = () # Passthrough columns
|
|
71
|
+
order_column: Expr | None = None # For top_k: the ordering column
|
|
72
|
+
desc: bool = True # For top_k: descending order
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ── Ordering ──────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True)
|
|
78
|
+
class OrderedColumn(Expr):
|
|
79
|
+
"""A column with sort direction."""
|
|
80
|
+
column: Expr
|
|
81
|
+
descending: bool = False
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ── Boolean expressions ──────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True)
|
|
87
|
+
class Comparison(BoolExpr):
|
|
88
|
+
"""Binary comparison: ==, !=, <, <=, >, >=."""
|
|
89
|
+
op: str # "=", "!=", "<", "<=", ">", ">="
|
|
90
|
+
left: Expr
|
|
91
|
+
right: Expr
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass(frozen=True)
|
|
95
|
+
class And(BoolExpr):
|
|
96
|
+
"""Logical AND of two conditions (Datalog comma)."""
|
|
97
|
+
left: BoolExpr
|
|
98
|
+
right: BoolExpr
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass(frozen=True)
|
|
102
|
+
class Or(BoolExpr):
|
|
103
|
+
"""Logical OR - requires splitting into multiple queries."""
|
|
104
|
+
left: BoolExpr
|
|
105
|
+
right: BoolExpr
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(frozen=True)
|
|
109
|
+
class Not(BoolExpr):
|
|
110
|
+
"""Negation: !relation(X, Y) in Datalog."""
|
|
111
|
+
operand: BoolExpr
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass(frozen=True)
|
|
115
|
+
class InExpr(BoolExpr):
|
|
116
|
+
"""Membership test: Column appears in another relation."""
|
|
117
|
+
column: Expr
|
|
118
|
+
target_column: Expr
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass(frozen=True)
|
|
122
|
+
class NegatedIn(BoolExpr):
|
|
123
|
+
"""Negated membership test."""
|
|
124
|
+
column: Expr
|
|
125
|
+
target_column: Expr
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass(frozen=True)
|
|
129
|
+
class MatchExpr(BoolExpr):
|
|
130
|
+
"""Multi-column negation/existence check against a relation."""
|
|
131
|
+
relation: str
|
|
132
|
+
bindings: dict[str, Expr] # target_col -> source expr
|
|
133
|
+
negated: bool = False
|
inputlayer/_naming.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Naming convention utilities: CamelCase ↔ snake_case, column → Datalog variable."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def camel_to_snake(name: str) -> str:
|
|
9
|
+
"""Convert CamelCase class name to snake_case relation name.
|
|
10
|
+
|
|
11
|
+
Examples:
|
|
12
|
+
Employee -> employee
|
|
13
|
+
UserProfile -> user_profile
|
|
14
|
+
HTTPRequest -> http_request
|
|
15
|
+
ABCDef -> abc_def
|
|
16
|
+
"""
|
|
17
|
+
# Insert underscore between sequences of uppercase and a following lower/digit
|
|
18
|
+
s = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", name)
|
|
19
|
+
# Insert underscore between lowercase/digit and uppercase
|
|
20
|
+
s = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s)
|
|
21
|
+
return s.lower()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def snake_to_camel(name: str) -> str:
|
|
25
|
+
"""Convert snake_case to CamelCase.
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
employee -> Employee
|
|
29
|
+
user_profile -> UserProfile
|
|
30
|
+
http_request -> HttpRequest
|
|
31
|
+
"""
|
|
32
|
+
return "".join(part.capitalize() for part in name.split("_"))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def column_to_variable(column_name: str) -> str:
|
|
36
|
+
"""Convert a snake_case column name to a Datalog variable (Capitalized).
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
id -> Id
|
|
40
|
+
name -> Name
|
|
41
|
+
department_name -> DepartmentName
|
|
42
|
+
x -> X
|
|
43
|
+
"""
|
|
44
|
+
return snake_to_camel(column_name)
|
inputlayer/_protocol.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""WebSocket wire protocol: message serialization and deserialization.
|
|
2
|
+
|
|
3
|
+
Matches the AsyncAPI spec at ``docs/spec/asyncapi.yaml``.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# ── Client → Server messages ──────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class LoginMessage:
|
|
17
|
+
username: str
|
|
18
|
+
password: str
|
|
19
|
+
|
|
20
|
+
def to_json(self) -> str:
|
|
21
|
+
return json.dumps({
|
|
22
|
+
"type": "login",
|
|
23
|
+
"username": self.username,
|
|
24
|
+
"password": self.password,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class AuthenticateMessage:
|
|
30
|
+
api_key: str
|
|
31
|
+
|
|
32
|
+
def to_json(self) -> str:
|
|
33
|
+
return json.dumps({
|
|
34
|
+
"type": "authenticate",
|
|
35
|
+
"api_key": self.api_key,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class ExecuteMessage:
|
|
41
|
+
program: str
|
|
42
|
+
|
|
43
|
+
def to_json(self) -> str:
|
|
44
|
+
return json.dumps({
|
|
45
|
+
"type": "execute",
|
|
46
|
+
"program": self.program,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True)
|
|
51
|
+
class PingMessage:
|
|
52
|
+
def to_json(self) -> str:
|
|
53
|
+
return json.dumps({"type": "ping"})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# ── Server → Client messages ─────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True)
|
|
59
|
+
class AuthenticatedResponse:
|
|
60
|
+
session_id: str
|
|
61
|
+
knowledge_graph: str
|
|
62
|
+
version: str
|
|
63
|
+
role: str
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class AuthErrorResponse:
|
|
68
|
+
message: str
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(frozen=True)
|
|
72
|
+
class ResultResponse:
|
|
73
|
+
columns: list[str]
|
|
74
|
+
rows: list[list[Any]]
|
|
75
|
+
row_count: int
|
|
76
|
+
total_count: int
|
|
77
|
+
truncated: bool
|
|
78
|
+
execution_time_ms: int
|
|
79
|
+
row_provenance: list[str] | None = None
|
|
80
|
+
metadata: dict[str, Any] | None = None
|
|
81
|
+
switched_kg: str | None = None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(frozen=True)
|
|
85
|
+
class ErrorResponse:
|
|
86
|
+
message: str
|
|
87
|
+
validation_errors: list[dict[str, Any]] | None = None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass(frozen=True)
|
|
91
|
+
class ResultStartResponse:
|
|
92
|
+
columns: list[str]
|
|
93
|
+
total_count: int
|
|
94
|
+
truncated: bool
|
|
95
|
+
execution_time_ms: int
|
|
96
|
+
metadata: dict[str, Any] | None = None
|
|
97
|
+
switched_kg: str | None = None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass(frozen=True)
|
|
101
|
+
class ResultChunkResponse:
|
|
102
|
+
rows: list[list[Any]]
|
|
103
|
+
chunk_index: int
|
|
104
|
+
row_provenance: list[str] | None = None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass(frozen=True)
|
|
108
|
+
class ResultEndResponse:
|
|
109
|
+
row_count: int
|
|
110
|
+
chunk_count: int
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass(frozen=True)
|
|
114
|
+
class PongResponse:
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass(frozen=True)
|
|
119
|
+
class NotificationResponse:
|
|
120
|
+
type: str # persistent_update, rule_change, kg_change, schema_change
|
|
121
|
+
seq: int
|
|
122
|
+
timestamp_ms: int
|
|
123
|
+
session_id: str | None = None
|
|
124
|
+
knowledge_graph: str | None = None
|
|
125
|
+
# persistent_update fields
|
|
126
|
+
relation: str | None = None
|
|
127
|
+
operation: str | None = None
|
|
128
|
+
count: int | None = None
|
|
129
|
+
# rule_change fields
|
|
130
|
+
rule_name: str | None = None
|
|
131
|
+
# schema_change fields
|
|
132
|
+
entity: str | None = None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# ── Type alias ────────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
ServerMessage = (
|
|
138
|
+
AuthenticatedResponse
|
|
139
|
+
| AuthErrorResponse
|
|
140
|
+
| ResultResponse
|
|
141
|
+
| ErrorResponse
|
|
142
|
+
| ResultStartResponse
|
|
143
|
+
| ResultChunkResponse
|
|
144
|
+
| ResultEndResponse
|
|
145
|
+
| PongResponse
|
|
146
|
+
| NotificationResponse
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# ── Serialization / Deserialization ───────────────────────────────────
|
|
151
|
+
|
|
152
|
+
def serialize_message(msg: LoginMessage | AuthenticateMessage | ExecuteMessage | PingMessage) -> str:
|
|
153
|
+
"""Serialize a client message to JSON."""
|
|
154
|
+
return msg.to_json()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def deserialize_message(data: str | bytes) -> ServerMessage:
|
|
158
|
+
"""Deserialize a server JSON message into a typed response object."""
|
|
159
|
+
if isinstance(data, bytes):
|
|
160
|
+
data = data.decode("utf-8")
|
|
161
|
+
obj = json.loads(data)
|
|
162
|
+
msg_type = obj.get("type")
|
|
163
|
+
|
|
164
|
+
if msg_type == "authenticated":
|
|
165
|
+
return AuthenticatedResponse(
|
|
166
|
+
session_id=obj["session_id"],
|
|
167
|
+
knowledge_graph=obj["knowledge_graph"],
|
|
168
|
+
version=obj["version"],
|
|
169
|
+
role=obj["role"],
|
|
170
|
+
)
|
|
171
|
+
if msg_type == "auth_error":
|
|
172
|
+
return AuthErrorResponse(message=obj["message"])
|
|
173
|
+
if msg_type == "result":
|
|
174
|
+
return ResultResponse(
|
|
175
|
+
columns=obj["columns"],
|
|
176
|
+
rows=obj["rows"],
|
|
177
|
+
row_count=obj["row_count"],
|
|
178
|
+
total_count=obj["total_count"],
|
|
179
|
+
truncated=obj["truncated"],
|
|
180
|
+
execution_time_ms=obj["execution_time_ms"],
|
|
181
|
+
row_provenance=obj.get("row_provenance"),
|
|
182
|
+
metadata=obj.get("metadata"),
|
|
183
|
+
switched_kg=obj.get("switched_kg"),
|
|
184
|
+
)
|
|
185
|
+
if msg_type == "error":
|
|
186
|
+
return ErrorResponse(
|
|
187
|
+
message=obj["message"],
|
|
188
|
+
validation_errors=obj.get("validation_errors"),
|
|
189
|
+
)
|
|
190
|
+
if msg_type == "result_start":
|
|
191
|
+
return ResultStartResponse(
|
|
192
|
+
columns=obj["columns"],
|
|
193
|
+
total_count=obj["total_count"],
|
|
194
|
+
truncated=obj["truncated"],
|
|
195
|
+
execution_time_ms=obj["execution_time_ms"],
|
|
196
|
+
metadata=obj.get("metadata"),
|
|
197
|
+
switched_kg=obj.get("switched_kg"),
|
|
198
|
+
)
|
|
199
|
+
if msg_type == "result_chunk":
|
|
200
|
+
return ResultChunkResponse(
|
|
201
|
+
rows=obj["rows"],
|
|
202
|
+
chunk_index=obj["chunk_index"],
|
|
203
|
+
row_provenance=obj.get("row_provenance"),
|
|
204
|
+
)
|
|
205
|
+
if msg_type == "result_end":
|
|
206
|
+
return ResultEndResponse(
|
|
207
|
+
row_count=obj["row_count"],
|
|
208
|
+
chunk_count=obj["chunk_count"],
|
|
209
|
+
)
|
|
210
|
+
if msg_type == "pong":
|
|
211
|
+
return PongResponse()
|
|
212
|
+
if msg_type in ("persistent_update", "rule_change", "kg_change", "schema_change"):
|
|
213
|
+
return NotificationResponse(
|
|
214
|
+
type=msg_type,
|
|
215
|
+
seq=obj["seq"],
|
|
216
|
+
timestamp_ms=obj["timestamp_ms"],
|
|
217
|
+
session_id=obj.get("session_id"),
|
|
218
|
+
knowledge_graph=obj.get("knowledge_graph"),
|
|
219
|
+
relation=obj.get("relation"),
|
|
220
|
+
operation=obj.get("operation"),
|
|
221
|
+
count=obj.get("count"),
|
|
222
|
+
rule_name=obj.get("rule_name"),
|
|
223
|
+
entity=obj.get("entity"),
|
|
224
|
+
)
|
|
225
|
+
raise ValueError(f"Unknown message type: {msg_type!r}")
|