nl2sql-adapter-sdk 0.1.0__tar.gz
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.
- nl2sql_adapter_sdk-0.1.0/PKG-INFO +6 -0
- nl2sql_adapter_sdk-0.1.0/pyproject.toml +19 -0
- nl2sql_adapter_sdk-0.1.0/setup.cfg +4 -0
- nl2sql_adapter_sdk-0.1.0/src/nl2sql_adapter_sdk/__init__.py +37 -0
- nl2sql_adapter_sdk-0.1.0/src/nl2sql_adapter_sdk/capabilities.py +13 -0
- nl2sql_adapter_sdk-0.1.0/src/nl2sql_adapter_sdk/contracts.py +87 -0
- nl2sql_adapter_sdk-0.1.0/src/nl2sql_adapter_sdk/protocols.py +41 -0
- nl2sql_adapter_sdk-0.1.0/src/nl2sql_adapter_sdk/schema.py +117 -0
- nl2sql_adapter_sdk-0.1.0/src/nl2sql_adapter_sdk.egg-info/PKG-INFO +6 -0
- nl2sql_adapter_sdk-0.1.0/src/nl2sql_adapter_sdk.egg-info/SOURCES.txt +11 -0
- nl2sql_adapter_sdk-0.1.0/src/nl2sql_adapter_sdk.egg-info/dependency_links.txt +1 -0
- nl2sql_adapter_sdk-0.1.0/src/nl2sql_adapter_sdk.egg-info/requires.txt +1 -0
- nl2sql_adapter_sdk-0.1.0/src/nl2sql_adapter_sdk.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nl2sql-adapter-sdk"
|
|
7
|
+
version = "0.1.0" # x-release-please-version
|
|
8
|
+
description = "Shared adapter contracts for NL2SQL"
|
|
9
|
+
requires-python = ">=3.9"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"pydantic>=1.10",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[tool.setuptools]
|
|
15
|
+
package-dir = {"" = "src"}
|
|
16
|
+
|
|
17
|
+
[tool.setuptools.packages.find]
|
|
18
|
+
where = ["src"]
|
|
19
|
+
include = ["nl2sql_adapter_sdk*"]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Adapter SDK: shared contracts for core and adapters."""
|
|
2
|
+
|
|
3
|
+
from .capabilities import DatasourceCapability
|
|
4
|
+
from .contracts import AdapterRequest, ResultError, ResultFrame
|
|
5
|
+
from .protocols import DatasourceAdapterProtocol
|
|
6
|
+
from .schema import (
|
|
7
|
+
TableRef,
|
|
8
|
+
ColumnStatistics,
|
|
9
|
+
ColumnMetadata,
|
|
10
|
+
ColumnContract,
|
|
11
|
+
ForeignKeyContract,
|
|
12
|
+
TableContract,
|
|
13
|
+
TableMetadata,
|
|
14
|
+
SchemaContract,
|
|
15
|
+
SchemaMetadata,
|
|
16
|
+
SchemaSnapshot,
|
|
17
|
+
ColumnRef
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"DatasourceCapability",
|
|
22
|
+
"AdapterRequest",
|
|
23
|
+
"ResultError",
|
|
24
|
+
"ResultFrame",
|
|
25
|
+
"DatasourceAdapterProtocol",
|
|
26
|
+
"TableRef",
|
|
27
|
+
"ColumnStatistics",
|
|
28
|
+
"ColumnMetadata",
|
|
29
|
+
"ColumnContract",
|
|
30
|
+
"ForeignKeyContract",
|
|
31
|
+
"TableContract",
|
|
32
|
+
"TableMetadata",
|
|
33
|
+
"SchemaContract",
|
|
34
|
+
"SchemaMetadata",
|
|
35
|
+
"SchemaSnapshot",
|
|
36
|
+
"ColumnRef",
|
|
37
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DatasourceCapability(str, Enum):
|
|
5
|
+
"""Capability flags for datasource adapters."""
|
|
6
|
+
|
|
7
|
+
SUPPORTS_SQL = "supports_sql"
|
|
8
|
+
SUPPORTS_REST = "supports_rest"
|
|
9
|
+
SUPPORTS_GRAPHQL = "supports_graphql"
|
|
10
|
+
SUPPORTS_LAKE = "supports_lake"
|
|
11
|
+
SUPPORTS_SCHEMA_INTROSPECTION = "supports_schema_introspection"
|
|
12
|
+
SUPPORTS_DRY_RUN = "supports_dry_run"
|
|
13
|
+
SUPPORTS_COST_ESTIMATE = "supports_cost_estimate"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AdapterRequest(BaseModel):
|
|
7
|
+
"""Generic adapter request for subgraph execution."""
|
|
8
|
+
|
|
9
|
+
plan_type: str = Field(
|
|
10
|
+
..., description="Execution plan type (e.g., 'sql', 'rest', 'nosql')."
|
|
11
|
+
)
|
|
12
|
+
payload: Dict[str, Any] = Field(
|
|
13
|
+
default_factory=dict, description="Plan-specific request payload."
|
|
14
|
+
)
|
|
15
|
+
parameters: Dict[str, Any] = Field(
|
|
16
|
+
default_factory=dict, description="Optional parameter bindings."
|
|
17
|
+
)
|
|
18
|
+
limits: Dict[str, int] = Field(
|
|
19
|
+
default_factory=dict,
|
|
20
|
+
description="Execution limits (row_limit, timeout_ms, max_bytes).",
|
|
21
|
+
)
|
|
22
|
+
trace_id: Optional[str] = Field(
|
|
23
|
+
default=None, description="Optional trace id for observability."
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
model_config = ConfigDict(extra="ignore")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ResultError(BaseModel):
|
|
31
|
+
"""Standardized error envelope for adapter results."""
|
|
32
|
+
|
|
33
|
+
error_code: str
|
|
34
|
+
safe_message: str
|
|
35
|
+
severity: str = Field(default="ERROR")
|
|
36
|
+
retryable: bool = Field(default=False)
|
|
37
|
+
stage: Optional[str] = None
|
|
38
|
+
datasource_id: Optional[str] = None
|
|
39
|
+
error_id: Optional[str] = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ResultFrame(BaseModel):
|
|
43
|
+
"""Adapter-agnostic, DataFrame-like result contract."""
|
|
44
|
+
|
|
45
|
+
success: bool = Field(default=True)
|
|
46
|
+
columns: List[str] = Field(default_factory=list)
|
|
47
|
+
rows: List[List[Any]] = Field(default_factory=list)
|
|
48
|
+
row_count: int = Field(default=0)
|
|
49
|
+
truncated: bool = Field(default=False)
|
|
50
|
+
bytes: Optional[int] = Field(default=None)
|
|
51
|
+
datasource_id: Optional[str] = Field(default=None)
|
|
52
|
+
tenant_id: Optional[str] = Field(default=None)
|
|
53
|
+
execution_stats: Dict[str, Any] = Field(default_factory=dict)
|
|
54
|
+
error: Optional[ResultError] = Field(default=None)
|
|
55
|
+
|
|
56
|
+
model_config = ConfigDict(extra="ignore")
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_row_dicts(
|
|
60
|
+
cls,
|
|
61
|
+
rows: List[Dict[str, Any]],
|
|
62
|
+
columns: Optional[List[str]] = None,
|
|
63
|
+
*,
|
|
64
|
+
row_count: Optional[int] = None,
|
|
65
|
+
**kwargs: Any,
|
|
66
|
+
) -> "ResultFrame":
|
|
67
|
+
"""Create a ResultFrame from list-of-dict rows."""
|
|
68
|
+
|
|
69
|
+
if columns is None:
|
|
70
|
+
columns = list(rows[0].keys()) if rows else []
|
|
71
|
+
|
|
72
|
+
row_values = [[row.get(col) for col in columns] for row in rows]
|
|
73
|
+
|
|
74
|
+
return cls(
|
|
75
|
+
columns=columns,
|
|
76
|
+
rows=row_values,
|
|
77
|
+
row_count=row_count if row_count is not None else len(row_values),
|
|
78
|
+
**kwargs,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def to_row_dicts(self) -> List[Dict[str, Any]]:
|
|
82
|
+
"""Convert row values to list-of-dict rows using column order."""
|
|
83
|
+
|
|
84
|
+
if not self.rows or not self.columns:
|
|
85
|
+
return []
|
|
86
|
+
names = self.columns
|
|
87
|
+
return [dict(zip(names, row)) for row in self.rows]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional, Protocol, Set, runtime_checkable
|
|
2
|
+
|
|
3
|
+
from .capabilities import DatasourceCapability
|
|
4
|
+
from .contracts import AdapterRequest, ResultFrame
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@runtime_checkable
|
|
8
|
+
class DatasourceAdapterProtocol(Protocol):
|
|
9
|
+
"""Contract for adapter implementations."""
|
|
10
|
+
|
|
11
|
+
datasource_id: str
|
|
12
|
+
datasource_engine_type: str
|
|
13
|
+
connection_args: Dict[str, Any]
|
|
14
|
+
statement_timeout_ms: Optional[int]
|
|
15
|
+
row_limit: Optional[int]
|
|
16
|
+
max_bytes: Optional[int]
|
|
17
|
+
|
|
18
|
+
def capabilities(self) -> Set[DatasourceCapability]:
|
|
19
|
+
"""Returns supported capabilities for this adapter."""
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
def connect(self) -> None:
|
|
23
|
+
"""Initialize connections / clients based on config."""
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
def fetch_schema_snapshot(self) -> Any:
|
|
27
|
+
"""Return a structured schema snapshot if supported."""
|
|
28
|
+
...
|
|
29
|
+
|
|
30
|
+
def execute(self, request: AdapterRequest) -> ResultFrame:
|
|
31
|
+
"""Execute a plan-specific request and return a ResultFrame."""
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
def get_dialect(self) -> str:
|
|
35
|
+
"""Return the normalized dialect string (SQL adapters)."""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_connection(self) -> bool:
|
|
40
|
+
"""Test if the connection to the datasource can be established."""
|
|
41
|
+
...
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List, Optional, Union, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
Scalar = Union[str, int, float, bool, None]
|
|
8
|
+
JsonValue = Union[Scalar, List[Scalar], Dict[str, Scalar]]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ColumnRef(BaseModel):
|
|
12
|
+
table: TableRef
|
|
13
|
+
column_name: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TableRef(BaseModel):
|
|
18
|
+
schema_name: str
|
|
19
|
+
table_name: str
|
|
20
|
+
|
|
21
|
+
model_config = ConfigDict(extra="ignore", frozen=True)
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def full_name(self) -> str:
|
|
25
|
+
return f"[{self.schema_name}].[{self.table_name}]"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ColumnStatistics(BaseModel):
|
|
29
|
+
null_percentage: float
|
|
30
|
+
distinct_count: int
|
|
31
|
+
min_value: Optional[Scalar] = None
|
|
32
|
+
max_value: Optional[Scalar] = None
|
|
33
|
+
sample_values: List[JsonValue] = Field(default_factory=list)
|
|
34
|
+
|
|
35
|
+
def __str__(self) -> str:
|
|
36
|
+
return (
|
|
37
|
+
"null_percentage: "
|
|
38
|
+
f"{self.null_percentage}, distinct_count: {self.distinct_count}, "
|
|
39
|
+
f"min_value: {self.min_value}, max_value: {self.max_value}, "
|
|
40
|
+
f"sample_values: {self.sample_values}"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ColumnMetadata(BaseModel):
|
|
45
|
+
description: Optional[str] = None
|
|
46
|
+
statistics: Optional[ColumnStatistics] = None
|
|
47
|
+
synonyms: Optional[List[str]] = None
|
|
48
|
+
pii: bool = False
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ColumnContract(BaseModel):
|
|
52
|
+
name: str
|
|
53
|
+
data_type: str
|
|
54
|
+
is_nullable: bool = True
|
|
55
|
+
is_primary_key: bool = False
|
|
56
|
+
|
|
57
|
+
model_config = ConfigDict(extra="ignore", frozen=True)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ForeignKeyContract(BaseModel):
|
|
61
|
+
constrained_columns: List[str]
|
|
62
|
+
referred_table: TableRef
|
|
63
|
+
referred_columns: List[str]
|
|
64
|
+
cardinality: Literal[
|
|
65
|
+
"one-to-one",
|
|
66
|
+
"one-to-many",
|
|
67
|
+
"many-to-one",
|
|
68
|
+
"many-to-many",
|
|
69
|
+
"unknown",
|
|
70
|
+
] = "unknown"
|
|
71
|
+
business_meaning: Optional[str] = None
|
|
72
|
+
|
|
73
|
+
model_config = ConfigDict(extra="ignore", frozen=True)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class TableContract(BaseModel):
|
|
77
|
+
table: TableRef
|
|
78
|
+
columns: Dict[str, ColumnContract] = Field(default_factory=dict)
|
|
79
|
+
foreign_keys: List[ForeignKeyContract] = Field(default_factory=list)
|
|
80
|
+
|
|
81
|
+
model_config = ConfigDict(extra="ignore", frozen=True)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def full_name(self) -> str:
|
|
85
|
+
return f"[{self.table.schema_name}].[{self.table.table_name}]"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class TableMetadata(BaseModel):
|
|
89
|
+
table: TableRef
|
|
90
|
+
columns: Dict[str, ColumnMetadata] = Field(default_factory=dict)
|
|
91
|
+
row_count: Optional[int] = None
|
|
92
|
+
description: Optional[str] = None
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def full_name(self) -> str:
|
|
96
|
+
return f"[{self.table.schema_name}].[{self.table.table_name}]"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class SchemaContract(BaseModel):
|
|
100
|
+
datasource_id: str
|
|
101
|
+
engine_type: str
|
|
102
|
+
tables: Dict[str, TableContract] = Field(default_factory=dict)
|
|
103
|
+
|
|
104
|
+
model_config = ConfigDict(extra="ignore", frozen=True)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class SchemaMetadata(BaseModel):
|
|
108
|
+
datasource_id: str
|
|
109
|
+
engine_type: str
|
|
110
|
+
description: Optional[str] = None
|
|
111
|
+
domains: Optional[List[str]] = None
|
|
112
|
+
tables: Dict[str, TableMetadata] = Field(default_factory=dict)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class SchemaSnapshot(BaseModel):
|
|
116
|
+
contract: SchemaContract
|
|
117
|
+
metadata: SchemaMetadata
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
src/nl2sql_adapter_sdk/__init__.py
|
|
3
|
+
src/nl2sql_adapter_sdk/capabilities.py
|
|
4
|
+
src/nl2sql_adapter_sdk/contracts.py
|
|
5
|
+
src/nl2sql_adapter_sdk/protocols.py
|
|
6
|
+
src/nl2sql_adapter_sdk/schema.py
|
|
7
|
+
src/nl2sql_adapter_sdk.egg-info/PKG-INFO
|
|
8
|
+
src/nl2sql_adapter_sdk.egg-info/SOURCES.txt
|
|
9
|
+
src/nl2sql_adapter_sdk.egg-info/dependency_links.txt
|
|
10
|
+
src/nl2sql_adapter_sdk.egg-info/requires.txt
|
|
11
|
+
src/nl2sql_adapter_sdk.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pydantic>=1.10
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nl2sql_adapter_sdk
|