nl2sql-engine 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.
- nl2sql/__init__.py +38 -0
- nl2sql/adapters/__init__.py +0 -0
- nl2sql/adapters/duckdb/__init__.py +0 -0
- nl2sql/adapters/duckdb/adapter.py +71 -0
- nl2sql/adapters/mssql/__init__.py +0 -0
- nl2sql/adapters/mssql/adapter.py +122 -0
- nl2sql/adapters/mysql/__init__.py +0 -0
- nl2sql/adapters/mysql/adapter.py +123 -0
- nl2sql/adapters/postgres/__init__.py +0 -0
- nl2sql/adapters/postgres/adapter.py +115 -0
- nl2sql/adapters/sqlalchemy_base/__init__.py +17 -0
- nl2sql/adapters/sqlalchemy_base/adapter.py +476 -0
- nl2sql/adapters/sqlalchemy_base/models.py +36 -0
- nl2sql/adapters/sqlite/__init__.py +0 -0
- nl2sql/adapters/sqlite/adapter.py +88 -0
- nl2sql/aggregation/__init__.py +3 -0
- nl2sql/aggregation/aggregator.py +98 -0
- nl2sql/aggregation/engines/__init__.py +3 -0
- nl2sql/aggregation/engines/polars_duckdb.py +125 -0
- nl2sql/api/__init__.py +0 -0
- nl2sql/api/auth_api.py +60 -0
- nl2sql/api/benchmark_api.py +114 -0
- nl2sql/api/datasource_api.py +132 -0
- nl2sql/api/indexing_api.py +59 -0
- nl2sql/api/llm_api.py +82 -0
- nl2sql/api/policy_api.py +135 -0
- nl2sql/api/query_api.py +138 -0
- nl2sql/api/result_api.py +24 -0
- nl2sql/api/settings_api.py +65 -0
- nl2sql/auth/__init__.py +8 -0
- nl2sql/auth/models.py +36 -0
- nl2sql/auth/rbac.py +25 -0
- nl2sql/cli/__init__.py +0 -0
- nl2sql/cli/checks.py +53 -0
- nl2sql/cli/commands/__init__.py +0 -0
- nl2sql/cli/commands/benchmark.py +34 -0
- nl2sql/cli/commands/doctor.py +49 -0
- nl2sql/cli/commands/indexing.py +126 -0
- nl2sql/cli/commands/info.py +25 -0
- nl2sql/cli/commands/install.py +27 -0
- nl2sql/cli/commands/policy.py +57 -0
- nl2sql/cli/commands/run.py +166 -0
- nl2sql/cli/commands/setup.py +415 -0
- nl2sql/cli/commands/visualize.py +34 -0
- nl2sql/cli/common/decorators.py +34 -0
- nl2sql/cli/config.py +24 -0
- nl2sql/cli/console.py +52 -0
- nl2sql/cli/demo/__init__.py +1 -0
- nl2sql/cli/demo/data.py +87 -0
- nl2sql/cli/demo/defaults.py +122 -0
- nl2sql/cli/demo/factory.py +289 -0
- nl2sql/cli/demo/manager.py +230 -0
- nl2sql/cli/demo/schemas.py +336 -0
- nl2sql/cli/demo/writers/__init__.py +0 -0
- nl2sql/cli/demo/writers/docker.py +182 -0
- nl2sql/cli/demo/writers/sqlite.py +88 -0
- nl2sql/cli/generators/datasources/__init__.py +3 -0
- nl2sql/cli/generators/datasources/generator.py +24 -0
- nl2sql/cli/generators/datasources/templates.py +7 -0
- nl2sql/cli/generators/env/__init__.py +3 -0
- nl2sql/cli/generators/env/generator.py +46 -0
- nl2sql/cli/generators/env/templates.py +25 -0
- nl2sql/cli/generators/llm/__init__.py +3 -0
- nl2sql/cli/generators/llm/generator.py +24 -0
- nl2sql/cli/generators/llm/templates.py +4 -0
- nl2sql/cli/generators/policies/__init__.py +3 -0
- nl2sql/cli/generators/policies/generator.py +20 -0
- nl2sql/cli/generators/policies/templates.py +2 -0
- nl2sql/cli/main.py +195 -0
- nl2sql/cli/reporting.py +878 -0
- nl2sql/cli/types.py +13 -0
- nl2sql/common/__init__.py +1 -0
- nl2sql/common/cancellation.py +25 -0
- nl2sql/common/context.py +5 -0
- nl2sql/common/errors.py +109 -0
- nl2sql/common/event_logger.py +88 -0
- nl2sql/common/exceptions.py +3 -0
- nl2sql/common/logger.py +119 -0
- nl2sql/common/metrics.py +50 -0
- nl2sql/common/resilience.py +59 -0
- nl2sql/common/settings.py +195 -0
- nl2sql/configs/__init__.py +6 -0
- nl2sql/configs/datasources.py +10 -0
- nl2sql/configs/llm.py +36 -0
- nl2sql/configs/manager.py +176 -0
- nl2sql/configs/policies.py +14 -0
- nl2sql/configs/sample_questions.py +11 -0
- nl2sql/configs/secrets.py +11 -0
- nl2sql/context.py +106 -0
- nl2sql/datasources/__init__.py +21 -0
- nl2sql/datasources/discovery.py +28 -0
- nl2sql/datasources/models.py +21 -0
- nl2sql/datasources/protocols.py +3 -0
- nl2sql/datasources/registry.py +172 -0
- nl2sql/evaluation/__init__.py +6 -0
- nl2sql/evaluation/benchmark_runner.py +320 -0
- nl2sql/evaluation/evaluator.py +134 -0
- nl2sql/evaluation/types.py +22 -0
- nl2sql/execution/__init__.py +4 -0
- nl2sql/execution/artifacts/__init__.py +3 -0
- nl2sql/execution/artifacts/parquet.py +41 -0
- nl2sql/execution/artifacts/store.py +165 -0
- nl2sql/execution/contracts.py +57 -0
- nl2sql/execution/execution_store.py +25 -0
- nl2sql/execution/executor/__init__.py +3 -0
- nl2sql/execution/executor/sql_executor.py +116 -0
- nl2sql/indexing/__init__.py +7 -0
- nl2sql/indexing/chunk_builder.py +227 -0
- nl2sql/indexing/embeddings.py +180 -0
- nl2sql/indexing/enrichment_service.py +316 -0
- nl2sql/indexing/models.py +209 -0
- nl2sql/indexing/orchestrator.py +90 -0
- nl2sql/indexing/vector_store.py +422 -0
- nl2sql/llm/__init__.py +8 -0
- nl2sql/llm/models.py +10 -0
- nl2sql/llm/registry.py +214 -0
- nl2sql/pipeline/__init__.py +1 -0
- nl2sql/pipeline/graph.py +73 -0
- nl2sql/pipeline/graph_utils.py +141 -0
- nl2sql/pipeline/nodes/__init__.py +25 -0
- nl2sql/pipeline/nodes/aggregator/__init__.py +4 -0
- nl2sql/pipeline/nodes/aggregator/node.py +55 -0
- nl2sql/pipeline/nodes/aggregator/prompts.py +20 -0
- nl2sql/pipeline/nodes/aggregator/schemas.py +28 -0
- nl2sql/pipeline/nodes/answer_synthesizer/__init__.py +4 -0
- nl2sql/pipeline/nodes/answer_synthesizer/node.py +98 -0
- nl2sql/pipeline/nodes/answer_synthesizer/prompts.py +19 -0
- nl2sql/pipeline/nodes/answer_synthesizer/schemas.py +24 -0
- nl2sql/pipeline/nodes/ast_planner/__init__.py +4 -0
- nl2sql/pipeline/nodes/ast_planner/node.py +104 -0
- nl2sql/pipeline/nodes/ast_planner/prompts.py +138 -0
- nl2sql/pipeline/nodes/ast_planner/schemas.py +236 -0
- nl2sql/pipeline/nodes/datasource_resolver/__init__.py +4 -0
- nl2sql/pipeline/nodes/datasource_resolver/node.py +253 -0
- nl2sql/pipeline/nodes/datasource_resolver/schemas.py +21 -0
- nl2sql/pipeline/nodes/decomposer/__init__.py +3 -0
- nl2sql/pipeline/nodes/decomposer/node.py +219 -0
- nl2sql/pipeline/nodes/decomposer/prompts.py +96 -0
- nl2sql/pipeline/nodes/decomposer/schemas.py +143 -0
- nl2sql/pipeline/nodes/executor/__init__.py +3 -0
- nl2sql/pipeline/nodes/executor/node.py +107 -0
- nl2sql/pipeline/nodes/generator/__init__.py +4 -0
- nl2sql/pipeline/nodes/generator/node.py +267 -0
- nl2sql/pipeline/nodes/generator/schemas.py +13 -0
- nl2sql/pipeline/nodes/global_planner/__init__.py +4 -0
- nl2sql/pipeline/nodes/global_planner/node.py +186 -0
- nl2sql/pipeline/nodes/global_planner/schemas.py +101 -0
- nl2sql/pipeline/nodes/refiner/__init__.py +4 -0
- nl2sql/pipeline/nodes/refiner/node.py +132 -0
- nl2sql/pipeline/nodes/refiner/prompts.py +28 -0
- nl2sql/pipeline/nodes/refiner/schemas.py +13 -0
- nl2sql/pipeline/nodes/schema_retriever/__init__.py +3 -0
- nl2sql/pipeline/nodes/schema_retriever/node.py +252 -0
- nl2sql/pipeline/nodes/schema_retriever/schema.py +27 -0
- nl2sql/pipeline/nodes/validator/__init__.py +7 -0
- nl2sql/pipeline/nodes/validator/node.py +839 -0
- nl2sql/pipeline/nodes/validator/schemas.py +12 -0
- nl2sql/pipeline/pipeline_runner.py +72 -0
- nl2sql/pipeline/routes.py +72 -0
- nl2sql/pipeline/runtime.py +153 -0
- nl2sql/pipeline/state.py +92 -0
- nl2sql/pipeline/subgraphs/__init__.py +5 -0
- nl2sql/pipeline/subgraphs/schemas.py +23 -0
- nl2sql/pipeline/subgraphs/sql_agent.py +167 -0
- nl2sql/public_api.py +199 -0
- nl2sql/schema/__init__.py +37 -0
- nl2sql/schema/in_memory_store.py +173 -0
- nl2sql/schema/protocol.py +88 -0
- nl2sql/schema/sqlite_store.py +233 -0
- nl2sql/schema/store.py +29 -0
- nl2sql/secrets/__init__.py +14 -0
- nl2sql/secrets/factory.py +85 -0
- nl2sql/secrets/interfaces.py +16 -0
- nl2sql/secrets/manager.py +139 -0
- nl2sql/secrets/models.py +56 -0
- nl2sql/secrets/providers/aws.py +30 -0
- nl2sql/secrets/providers/azure.py +49 -0
- nl2sql/secrets/providers/env.py +8 -0
- nl2sql/secrets/providers/hashi.py +46 -0
- nl2sql/services/__init__.py +0 -0
- nl2sql/services/callbacks/__init__.py +0 -0
- nl2sql/services/callbacks/monitor.py +84 -0
- nl2sql/services/callbacks/node_context.py +7 -0
- nl2sql/services/callbacks/node_handlers.py +187 -0
- nl2sql/services/callbacks/node_metrics.py +14 -0
- nl2sql/services/callbacks/presenter.py +12 -0
- nl2sql/services/callbacks/token_handler.py +56 -0
- nl2sql_engine-0.1.0.dist-info/METADATA +295 -0
- nl2sql_engine-0.1.0.dist-info/RECORD +192 -0
- nl2sql_engine-0.1.0.dist-info/WHEEL +5 -0
- nl2sql_engine-0.1.0.dist-info/entry_points.txt +9 -0
- nl2sql_engine-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
from nl2sql.cli.reporting import ConsolePresenter
|
|
5
|
+
from nl2sql.indexing.vector_store import VectorStore
|
|
6
|
+
from nl2sql.datasources import DatasourceRegistry
|
|
7
|
+
from nl2sql.context import NL2SQLContext
|
|
8
|
+
from nl2sql.cli.common.decorators import handle_cli_errors
|
|
9
|
+
from nl2sql.indexing.orchestrator import IndexingOrchestrator
|
|
10
|
+
|
|
11
|
+
@handle_cli_errors
|
|
12
|
+
def run_indexing(
|
|
13
|
+
ctx: NL2SQLContext,
|
|
14
|
+
) -> None:
|
|
15
|
+
"""
|
|
16
|
+
Runs schema indexing for all registered datasources.
|
|
17
|
+
|
|
18
|
+
This command clears the existing vector store and indexes
|
|
19
|
+
schema chunks for each configured datasource using the
|
|
20
|
+
indexing orchestrator.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
ctx: The initialized NL2SQLContext.
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
SystemExit: With code 1 if the store could not be cleared or if any
|
|
27
|
+
datasource failed to index.
|
|
28
|
+
"""
|
|
29
|
+
presenter = ConsolePresenter()
|
|
30
|
+
presenter.print_info(f"Indexing schema to: {ctx.vector_store.persist_directory}")
|
|
31
|
+
|
|
32
|
+
adapters = ctx.ds_registry.list_adapters()
|
|
33
|
+
orchestrator = IndexingOrchestrator(ctx)
|
|
34
|
+
stats = []
|
|
35
|
+
errors = []
|
|
36
|
+
empty_stats = []
|
|
37
|
+
|
|
38
|
+
presenter.start_interactive_status("Clearing existing data...")
|
|
39
|
+
try:
|
|
40
|
+
orchestrator.clear_store()
|
|
41
|
+
except Exception as e:
|
|
42
|
+
presenter.stop_interactive_status()
|
|
43
|
+
presenter.print_error(f"Failed to clear existing data: {e}")
|
|
44
|
+
sys.exit(1)
|
|
45
|
+
presenter.stop_interactive_status()
|
|
46
|
+
presenter.print_success("Cleared existing data.")
|
|
47
|
+
|
|
48
|
+
for adapter in adapters:
|
|
49
|
+
ds_id = adapter.datasource_id
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
task = presenter.start_task_line(f"Indexing {ds_id}...")
|
|
53
|
+
schema_stats = orchestrator.index_datasource(adapter)
|
|
54
|
+
if schema_stats:
|
|
55
|
+
stats.append(schema_stats)
|
|
56
|
+
else:
|
|
57
|
+
empty_stats.append(ds_id)
|
|
58
|
+
presenter.finish_task_line(task, f"{ds_id} indexed", success=True)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
if "task" in locals():
|
|
61
|
+
presenter.finish_task_line(task, f"{ds_id} failed", success=False)
|
|
62
|
+
presenter.print_error(f"Failed to index {ds_id}: {e}")
|
|
63
|
+
errors.append(
|
|
64
|
+
{"datasource_id": ds_id, "error": str(e)}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if errors:
|
|
68
|
+
presenter.print_table(errors, "Indexing Errors", columns=["datasource_id", "error"])
|
|
69
|
+
|
|
70
|
+
if stats:
|
|
71
|
+
summary_rows = []
|
|
72
|
+
total_chunks = 0
|
|
73
|
+
totals_by_type: Dict[str, int] = {}
|
|
74
|
+
|
|
75
|
+
for s in stats:
|
|
76
|
+
ds_id = s.get("datasource_id", "unknown")
|
|
77
|
+
schema_version = s.get("schema_version", "-")
|
|
78
|
+
chunk_stats = {
|
|
79
|
+
k: v for k, v in s.items()
|
|
80
|
+
if k not in ("datasource_id", "schema_version")
|
|
81
|
+
}
|
|
82
|
+
ds_total = sum(v for v in chunk_stats.values() if isinstance(v, int))
|
|
83
|
+
total_chunks += ds_total
|
|
84
|
+
for key, val in chunk_stats.items():
|
|
85
|
+
if isinstance(val, int):
|
|
86
|
+
totals_by_type[key] = totals_by_type.get(key, 0) + val
|
|
87
|
+
|
|
88
|
+
summary_rows.append(
|
|
89
|
+
{
|
|
90
|
+
"datasource_id": ds_id,
|
|
91
|
+
"schema_version": schema_version,
|
|
92
|
+
"total_chunks": ds_total,
|
|
93
|
+
"chunks": json.dumps(chunk_stats, separators=(",", ":")),
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
presenter.print_table(
|
|
98
|
+
summary_rows,
|
|
99
|
+
"Indexing Summary",
|
|
100
|
+
columns=["datasource_id", "schema_version", "total_chunks", "chunks"],
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if totals_by_type:
|
|
104
|
+
totals_str = ", ".join([f"{k}={v}" for k, v in sorted(totals_by_type.items())])
|
|
105
|
+
presenter.print_info(f"Total chunks indexed: {total_chunks} ({totals_str})")
|
|
106
|
+
|
|
107
|
+
total_adapters = len(adapters)
|
|
108
|
+
succeeded = len(stats)
|
|
109
|
+
failed = len(errors)
|
|
110
|
+
skipped = len(empty_stats)
|
|
111
|
+
presenter.print_info(
|
|
112
|
+
f"Datasources: total={total_adapters}, succeeded={succeeded}, failed={failed}, empty={skipped}"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if empty_stats:
|
|
116
|
+
presenter.print_warning(f"Datasources with empty stats: {', '.join(empty_stats)}")
|
|
117
|
+
|
|
118
|
+
if errors:
|
|
119
|
+
# Any failure is fatal, not just a total one: a partially populated
|
|
120
|
+
# index answers later queries from an incomplete schema, which is worse
|
|
121
|
+
# than a loud stop because it looks like it worked. Scripts chaining
|
|
122
|
+
# `nl2sql index && nl2sql run ...` need that signal.
|
|
123
|
+
presenter.print_warning("Indexing completed with errors.")
|
|
124
|
+
sys.exit(1)
|
|
125
|
+
|
|
126
|
+
presenter.print_success("Indexing complete.")
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from rich.console import Console
|
|
2
|
+
from rich.table import Table
|
|
3
|
+
from nl2sql.datasources.discovery import discover_adapters
|
|
4
|
+
|
|
5
|
+
from nl2sql.cli.common.decorators import handle_cli_errors
|
|
6
|
+
|
|
7
|
+
@handle_cli_errors
|
|
8
|
+
def list_available_adapters() -> None:
|
|
9
|
+
"""Discovers and displays all installed Datasource Adapters."""
|
|
10
|
+
console = Console()
|
|
11
|
+
adapters = discover_adapters()
|
|
12
|
+
|
|
13
|
+
if not adapters:
|
|
14
|
+
console.print("[yellow]No adapters found. Please install a driver extra (e.g., nl2sql\[postgres]).[/yellow]")
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
table = Table(title="Installed Datasource Adapters")
|
|
18
|
+
table.add_column("Adapter ID", style="cyan", no_wrap=True)
|
|
19
|
+
table.add_column("Class", style="magenta")
|
|
20
|
+
table.add_column("Status", style="green")
|
|
21
|
+
|
|
22
|
+
for name, cls in adapters.items():
|
|
23
|
+
table.add_row(name, f"{cls.__module__}.{cls.__name__}", "Active")
|
|
24
|
+
|
|
25
|
+
console.print(table)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import subprocess
|
|
3
|
+
from rich.markup import escape
|
|
4
|
+
from rich.prompt import Confirm
|
|
5
|
+
from nl2sql.cli.config import KNOWN_ADAPTERS
|
|
6
|
+
from nl2sql.cli.console import console, print_success, print_error, print_step
|
|
7
|
+
|
|
8
|
+
def install_package(package_name: str) -> bool:
|
|
9
|
+
print_step(f"Installing {package_name}...")
|
|
10
|
+
try:
|
|
11
|
+
subprocess.check_call([sys.executable, "-m", "pip", "install", package_name])
|
|
12
|
+
print_success(f"Installed {package_name}")
|
|
13
|
+
return True
|
|
14
|
+
except subprocess.CalledProcessError:
|
|
15
|
+
print_error(f"Failed to install {package_name}")
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
from nl2sql.cli.common.decorators import handle_cli_errors
|
|
19
|
+
|
|
20
|
+
@handle_cli_errors
|
|
21
|
+
def install_command(adapter_name: str):
|
|
22
|
+
target_pkg = adapter_name
|
|
23
|
+
if adapter_name in KNOWN_ADAPTERS:
|
|
24
|
+
target_pkg = KNOWN_ADAPTERS[adapter_name]
|
|
25
|
+
|
|
26
|
+
if Confirm.ask(f"Install [cyan]{escape(str(target_pkg))}[/cyan]?"):
|
|
27
|
+
install_package(target_pkg)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import pathlib
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from typing_extensions import Annotated
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.markup import escape
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from nl2sql import PolicyAPI
|
|
12
|
+
|
|
13
|
+
app = typer.Typer(help="Manage RBAC policies and security.")
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
from nl2sql.cli.common.decorators import handle_cli_errors
|
|
17
|
+
|
|
18
|
+
@app.command("validate")
|
|
19
|
+
@handle_cli_errors
|
|
20
|
+
def validate(
|
|
21
|
+
config: Annotated[Optional[pathlib.Path], typer.Option("--config", help="Path to datasource config")] = None,
|
|
22
|
+
policies: Annotated[Optional[pathlib.Path], typer.Option("--policies", help="Path to policies.json")] = None,
|
|
23
|
+
secrets: Annotated[Optional[pathlib.Path], typer.Option("--secrets", help="Path to secrets config")] = None,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Validate policy syntax and integrity against defined datasources.
|
|
27
|
+
"""
|
|
28
|
+
console.print(f"[bold blue]Validating Policies from:[/bold blue] {escape(str(policies or 'default'))}")
|
|
29
|
+
|
|
30
|
+
api = PolicyAPI()
|
|
31
|
+
report = api.validate_policies(policies_path=policies, datasources_path=config, secrets_path=secrets)
|
|
32
|
+
|
|
33
|
+
if report.errors:
|
|
34
|
+
console.print(f"[bold red]Schema Validation Failed:[/bold red]\\n{escape(str(report.errors[0]))}")
|
|
35
|
+
sys.exit(1)
|
|
36
|
+
|
|
37
|
+
console.print(f"[bold blue]Checking Integrity against Datasources:[/bold blue] {escape(str(config or 'default'))}")
|
|
38
|
+
if report.available_datasources:
|
|
39
|
+
console.print(f"[dim]Available Datasources: {report.available_datasources}[/dim]")
|
|
40
|
+
|
|
41
|
+
table = Table(title="Policy Integrity Report")
|
|
42
|
+
table.add_column("Role", style="cyan")
|
|
43
|
+
table.add_column("Target", style="magenta")
|
|
44
|
+
table.add_column("Status", style="green")
|
|
45
|
+
table.add_column("Details", style="white")
|
|
46
|
+
|
|
47
|
+
for entry in report.entries:
|
|
48
|
+
status = "[green]OK[/green]" if entry.status == "OK" else f"[red]{entry.status}[/red]"
|
|
49
|
+
table.add_row(Text(str(entry.role)), Text(str(entry.target)), status, Text(str(entry.details)))
|
|
50
|
+
|
|
51
|
+
console.print(table)
|
|
52
|
+
|
|
53
|
+
if not report.ok:
|
|
54
|
+
console.print("\n[bold red]Integrity Check Failed: Policies reference missing resources.[/bold red]")
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
else:
|
|
57
|
+
console.print("\n[bold green]✓ Policy Integrity Verified[/bold green]")
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from rich.text import Text
|
|
5
|
+
|
|
6
|
+
from nl2sql.datasources import DatasourceRegistry
|
|
7
|
+
from nl2sql.llm import LLMRegistry
|
|
8
|
+
from nl2sql.indexing.vector_store import VectorStore
|
|
9
|
+
from nl2sql.cli.reporting import ConsolePresenter
|
|
10
|
+
from nl2sql.pipeline.pipeline_runner import PipelineRunner
|
|
11
|
+
from nl2sql.common.settings import settings
|
|
12
|
+
from nl2sql.cli.types import RunConfig
|
|
13
|
+
from nl2sql.cli.common.decorators import handle_cli_errors
|
|
14
|
+
from nl2sql.context import NL2SQLContext
|
|
15
|
+
|
|
16
|
+
@handle_cli_errors
|
|
17
|
+
def run_pipeline(
|
|
18
|
+
config: RunConfig,
|
|
19
|
+
ctx: NL2SQLContext
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Executes the NL2SQL pipeline."""
|
|
22
|
+
if not config.query:
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
presenter = ConsolePresenter()
|
|
26
|
+
presenter.print_info(f"Query: {config.query}")
|
|
27
|
+
if config.no_exec:
|
|
28
|
+
presenter.print_warning("Execution disabled (no_exec). Only SQL/plan output will be shown.")
|
|
29
|
+
|
|
30
|
+
# Instantiate Runner
|
|
31
|
+
runner = PipelineRunner(ctx)
|
|
32
|
+
|
|
33
|
+
# Setup Monitoring
|
|
34
|
+
from nl2sql.services.callbacks.monitor import PipelineMonitorCallback
|
|
35
|
+
monitor = PipelineMonitorCallback(presenter)
|
|
36
|
+
|
|
37
|
+
presenter.start_interactive_status("Thinking...")
|
|
38
|
+
|
|
39
|
+
# Execution
|
|
40
|
+
result = runner.run(
|
|
41
|
+
query=config.query,
|
|
42
|
+
role=config.role,
|
|
43
|
+
datasource_id=config.ds_id,
|
|
44
|
+
execute=not config.no_exec,
|
|
45
|
+
callbacks=[monitor]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
presenter.stop_interactive_status()
|
|
49
|
+
|
|
50
|
+
# Handle Result
|
|
51
|
+
if not result.success:
|
|
52
|
+
if result.traceback:
|
|
53
|
+
presenter.print_error(result.traceback)
|
|
54
|
+
presenter.print_error(result.error or "Unknown Pipeline Error")
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
|
|
57
|
+
final_state = result.final_state
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
query_history = []
|
|
61
|
+
subgraph_outputs = final_state.get("subgraph_outputs") or {}
|
|
62
|
+
for _subgraph_id, output in subgraph_outputs.items():
|
|
63
|
+
if isinstance(output, dict):
|
|
64
|
+
sub_query = output.get("sub_query")
|
|
65
|
+
sql_draft = output.get("sql_draft")
|
|
66
|
+
else:
|
|
67
|
+
sub_query = getattr(output, "sub_query", None)
|
|
68
|
+
sql_draft = getattr(output, "sql_draft", None)
|
|
69
|
+
|
|
70
|
+
if not sub_query:
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
if isinstance(sub_query, dict):
|
|
74
|
+
ds_id = sub_query.get("datasource_id")
|
|
75
|
+
intent = sub_query.get("intent")
|
|
76
|
+
else:
|
|
77
|
+
ds_id = getattr(sub_query, "datasource_id", None)
|
|
78
|
+
intent = getattr(sub_query, "intent", None)
|
|
79
|
+
|
|
80
|
+
query_history.append({
|
|
81
|
+
"sub_query": intent,
|
|
82
|
+
"datasource_id": ds_id,
|
|
83
|
+
"sql": sql_draft,
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
if config.verbose:
|
|
87
|
+
reasoning = final_state.get("reasoning", [])
|
|
88
|
+
presenter.print_execution_tree(config.query, query_history, top_level_reasoning=reasoning)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
with open("last_reasoning.json", "w") as f:
|
|
92
|
+
dump_data = {
|
|
93
|
+
"global_reasoning": reasoning,
|
|
94
|
+
"execution_history": query_history
|
|
95
|
+
}
|
|
96
|
+
json.dump(dump_data, f, indent=2, default=str)
|
|
97
|
+
presenter.print_info("Detailed reasoning trace saved to last_reasoning.json")
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
if query_history:
|
|
102
|
+
datasources_used = sorted({item.get("datasource_id") for item in query_history if item.get("datasource_id")})
|
|
103
|
+
if datasources_used:
|
|
104
|
+
presenter.print_info(f"Datasources used: {', '.join(datasources_used)}")
|
|
105
|
+
|
|
106
|
+
for item in query_history:
|
|
107
|
+
ds = item.get("datasource_id", "Unknown")
|
|
108
|
+
sub_query = item.get("sub_query")
|
|
109
|
+
sql = item.get("sql")
|
|
110
|
+
|
|
111
|
+
if sql:
|
|
112
|
+
body = Text()
|
|
113
|
+
if sub_query:
|
|
114
|
+
body.append(f"Sub-Query: {sub_query}\n", style="bold")
|
|
115
|
+
body.append(f"Datasource: {ds}\n\n", style="bold")
|
|
116
|
+
body.append(str(sql))
|
|
117
|
+
presenter.print_sql(body)
|
|
118
|
+
|
|
119
|
+
elif final_state.get("sql_draft"):
|
|
120
|
+
sql_draft_data = final_state.get("sql_draft")
|
|
121
|
+
sql_draft = sql_draft_data.get("sql") if isinstance(sql_draft_data, dict) else getattr(sql_draft_data, "sql", None)
|
|
122
|
+
if sql_draft:
|
|
123
|
+
presenter.print_sql(str(sql_draft))
|
|
124
|
+
|
|
125
|
+
errors = final_state.get("errors")
|
|
126
|
+
if errors:
|
|
127
|
+
presenter.print_pipeline_errors(errors)
|
|
128
|
+
|
|
129
|
+
warnings = final_state.get("warnings") or []
|
|
130
|
+
for warning in warnings:
|
|
131
|
+
if isinstance(warning, dict):
|
|
132
|
+
presenter.print_warning(json.dumps(warning, default=str))
|
|
133
|
+
else:
|
|
134
|
+
presenter.print_warning(str(warning))
|
|
135
|
+
|
|
136
|
+
answer_payload = None
|
|
137
|
+
answer_synth = final_state.get("answer_synthesizer_response")
|
|
138
|
+
if answer_synth:
|
|
139
|
+
if isinstance(answer_synth, dict):
|
|
140
|
+
answer_payload = answer_synth.get("final_answer")
|
|
141
|
+
else:
|
|
142
|
+
answer_payload = getattr(answer_synth, "final_answer", None)
|
|
143
|
+
if isinstance(answer_payload, dict) and answer_payload:
|
|
144
|
+
presenter.print_answer_synthesizer_output(answer_payload)
|
|
145
|
+
|
|
146
|
+
final_answer = final_state.get("final_answer")
|
|
147
|
+
if isinstance(final_answer, dict) and final_answer:
|
|
148
|
+
presenter.print_answer_synthesizer_output(final_answer)
|
|
149
|
+
elif type(final_answer) == str and final_answer:
|
|
150
|
+
presenter.print_final_answer(final_answer)
|
|
151
|
+
elif type(final_answer) == list and final_answer:
|
|
152
|
+
presenter.print_execution_result(final_answer)
|
|
153
|
+
|
|
154
|
+
execution = final_state.get("execution")
|
|
155
|
+
if execution:
|
|
156
|
+
row_count = execution.get("row_count", 0) if isinstance(execution, dict) else getattr(execution, "row_count", 0)
|
|
157
|
+
presenter.print_rows_returned(row_count)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if config.show_perf:
|
|
161
|
+
tree, metrics, node_map = monitor.get_performance_tree()
|
|
162
|
+
presenter.print_performance_tree(tree, metrics, node_map)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
from nl2sql.common.metrics import TOKEN_LOG
|
|
166
|
+
presenter.print_cost_summary(result.duration, TOKEN_LOG)
|