nl2sql-api 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_api/__init__.py +0 -0
- nl2sql_api/dependencies.py +44 -0
- nl2sql_api/main.py +47 -0
- nl2sql_api/models/__init__.py +0 -0
- nl2sql_api/models/datasource.py +11 -0
- nl2sql_api/models/llm.py +11 -0
- nl2sql_api/models/query.py +33 -0
- nl2sql_api/models/response.py +13 -0
- nl2sql_api/models/schema.py +8 -0
- nl2sql_api/routes/__init__.py +0 -0
- nl2sql_api/routes/datasource.py +57 -0
- nl2sql_api/routes/health.py +22 -0
- nl2sql_api/routes/indexing.py +73 -0
- nl2sql_api/routes/llm.py +41 -0
- nl2sql_api/routes/query.py +29 -0
- nl2sql_api/server.py +30 -0
- nl2sql_api/services/__init__.py +15 -0
- nl2sql_api/services/datasource.py +43 -0
- nl2sql_api/services/health.py +22 -0
- nl2sql_api/services/indexing.py +30 -0
- nl2sql_api/services/llm.py +25 -0
- nl2sql_api/services/query.py +34 -0
- nl2sql_api-0.1.0.dist-info/METADATA +11 -0
- nl2sql_api-0.1.0.dist-info/RECORD +27 -0
- nl2sql_api-0.1.0.dist-info/WHEEL +5 -0
- nl2sql_api-0.1.0.dist-info/entry_points.txt +2 -0
- nl2sql_api-0.1.0.dist-info/top_level.txt +1 -0
nl2sql_api/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from fastapi import Depends, Request
|
|
2
|
+
|
|
3
|
+
from nl2sql import NL2SQL
|
|
4
|
+
from nl2sql_api.services import (
|
|
5
|
+
DatasourceService,
|
|
6
|
+
QueryService,
|
|
7
|
+
LLMService,
|
|
8
|
+
IndexingService,
|
|
9
|
+
HealthService,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_engine(request: Request) -> NL2SQL:
|
|
14
|
+
return request.app.state.engine
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_datasource_service(
|
|
18
|
+
engine: NL2SQL = Depends(get_engine),
|
|
19
|
+
) -> DatasourceService:
|
|
20
|
+
return DatasourceService(engine)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_query_service(
|
|
24
|
+
engine: NL2SQL = Depends(get_engine),
|
|
25
|
+
) -> QueryService:
|
|
26
|
+
return QueryService(engine)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_llm_service(
|
|
30
|
+
engine: NL2SQL = Depends(get_engine),
|
|
31
|
+
) -> LLMService:
|
|
32
|
+
return LLMService(engine)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_indexing_service(
|
|
36
|
+
engine: NL2SQL = Depends(get_engine),
|
|
37
|
+
) -> IndexingService:
|
|
38
|
+
return IndexingService(engine)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_health_service(
|
|
42
|
+
engine: NL2SQL = Depends(get_engine),
|
|
43
|
+
) -> HealthService:
|
|
44
|
+
return HealthService(engine)
|
nl2sql_api/main.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
|
|
6
|
+
from .routes import query, health, datasource, llm, indexing
|
|
7
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
8
|
+
from nl2sql import NL2SQL
|
|
9
|
+
from nl2sql.common.logger import configure_logging
|
|
10
|
+
from nl2sql.common.settings import settings
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@asynccontextmanager
|
|
14
|
+
async def lifespan(app: FastAPI):
|
|
15
|
+
# The library no longer configures logging on import, so the application
|
|
16
|
+
# entry point owns it - before anything that logs is constructed.
|
|
17
|
+
configure_logging(
|
|
18
|
+
level="INFO",
|
|
19
|
+
json_format=(settings.observability_exporter == "otlp"),
|
|
20
|
+
)
|
|
21
|
+
app.state.engine = NL2SQL()
|
|
22
|
+
yield
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
app = FastAPI(
|
|
26
|
+
title="NL2SQL API",
|
|
27
|
+
version="0.1.0",
|
|
28
|
+
lifespan=lifespan,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
app.include_router(query.router, prefix="/api/v1")
|
|
32
|
+
app.include_router(health.router, prefix="/api/v1")
|
|
33
|
+
app.include_router(datasource.router, prefix="/api/v1")
|
|
34
|
+
app.include_router(llm.router, prefix="/api/v1")
|
|
35
|
+
app.include_router(indexing.router, prefix="/api/v1")
|
|
36
|
+
|
|
37
|
+
_origins = [o.strip() for o in os.getenv("NL2SQL_API_CORS_ORIGINS", "").split(",") if o.strip()]
|
|
38
|
+
|
|
39
|
+
app.add_middleware(
|
|
40
|
+
CORSMiddleware,
|
|
41
|
+
allow_origins=_origins,
|
|
42
|
+
allow_credentials=bool(_origins),
|
|
43
|
+
allow_methods=["*"],
|
|
44
|
+
allow_headers=["*"],
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
File without changes
|
nl2sql_api/models/llm.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
from typing import Optional, Dict, Any, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class QueryRequest(BaseModel):
|
|
6
|
+
natural_language: str
|
|
7
|
+
datasource_id: Optional[str] = None
|
|
8
|
+
execute: bool = True
|
|
9
|
+
user_context: Optional[Dict[str, Any]] = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SubQueryResponse(BaseModel):
|
|
13
|
+
"""One decomposed sub-query and the SQL generated for it."""
|
|
14
|
+
id: str = ""
|
|
15
|
+
intent: str = ""
|
|
16
|
+
sql: str = ""
|
|
17
|
+
datasource_id: str = ""
|
|
18
|
+
schema_version: str = ""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class QueryResponse(BaseModel):
|
|
22
|
+
"""Mirrors ``nl2sql.api.query_api.QueryResult``.
|
|
23
|
+
|
|
24
|
+
Result rows are not inlined; they live in artifact storage and are addressable
|
|
25
|
+
through ``artifact_refs``.
|
|
26
|
+
"""
|
|
27
|
+
sub_queries: List[SubQueryResponse] = Field(default_factory=list)
|
|
28
|
+
final_answer: Optional[Dict[str, Any]] = None
|
|
29
|
+
errors: List[Dict[str, Any]] = Field(default_factory=list)
|
|
30
|
+
trace_id: Optional[str] = None
|
|
31
|
+
reasoning: List[Dict[str, Any]] = Field(default_factory=list)
|
|
32
|
+
warnings: List[Dict[str, Any]] = Field(default_factory=list)
|
|
33
|
+
artifact_refs: Dict[str, Dict[str, Any]] = Field(default_factory=dict)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from typing import Optional, List, Dict, Any
|
|
3
|
+
|
|
4
|
+
class ErrorResponse(BaseModel):
|
|
5
|
+
error_code: str
|
|
6
|
+
message: str
|
|
7
|
+
details: Optional[Dict[str, Any]] = None
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SuccessResponse(BaseModel):
|
|
11
|
+
success: bool
|
|
12
|
+
data: Optional[Dict[str, Any]] = None
|
|
13
|
+
message: Optional[str] = None
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from fastapi import APIRouter, HTTPException, Depends
|
|
2
|
+
from typing import Dict, Any, Annotated
|
|
3
|
+
|
|
4
|
+
from nl2sql_api.models.datasource import DatasourceRequest, DatasourceResponse
|
|
5
|
+
from nl2sql_api.dependencies import get_datasource_service
|
|
6
|
+
from nl2sql_api.services import DatasourceService
|
|
7
|
+
|
|
8
|
+
router = APIRouter()
|
|
9
|
+
|
|
10
|
+
DatasourceSvc = Annotated[DatasourceService, Depends(get_datasource_service)]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@router.post("/datasource", response_model=DatasourceResponse)
|
|
14
|
+
def add_datasource(
|
|
15
|
+
payload: DatasourceRequest,
|
|
16
|
+
service: DatasourceSvc,
|
|
17
|
+
):
|
|
18
|
+
try:
|
|
19
|
+
return service.add_datasource(payload)
|
|
20
|
+
except Exception as e:
|
|
21
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@router.get("/datasource", response_model=Dict[str, Any])
|
|
25
|
+
def list_datasources(
|
|
26
|
+
service: DatasourceSvc,
|
|
27
|
+
):
|
|
28
|
+
try:
|
|
29
|
+
return {"datasources": service.list_datasources()}
|
|
30
|
+
except Exception as e:
|
|
31
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@router.get("/datasource/{datasource_id}", response_model=Dict[str, Any])
|
|
35
|
+
def get_datasource(
|
|
36
|
+
datasource_id: str,
|
|
37
|
+
service: DatasourceSvc,
|
|
38
|
+
):
|
|
39
|
+
try:
|
|
40
|
+
return service.get_datasource(datasource_id)
|
|
41
|
+
except ValueError as e:
|
|
42
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
43
|
+
except Exception as e:
|
|
44
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@router.delete("/datasource/{datasource_id}", response_model=Dict[str, Any])
|
|
48
|
+
def remove_datasource(
|
|
49
|
+
datasource_id: str,
|
|
50
|
+
service: DatasourceSvc,
|
|
51
|
+
):
|
|
52
|
+
try:
|
|
53
|
+
return service.remove_datasource(datasource_id)
|
|
54
|
+
except ValueError as e:
|
|
55
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
56
|
+
except Exception as e:
|
|
57
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from fastapi import APIRouter, Depends
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
from nl2sql_api.models.response import SuccessResponse
|
|
4
|
+
from nl2sql_api.dependencies import get_health_service
|
|
5
|
+
from nl2sql_api.services import HealthService
|
|
6
|
+
|
|
7
|
+
router = APIRouter()
|
|
8
|
+
|
|
9
|
+
HealthSvc = Annotated[HealthService, Depends(get_health_service)]
|
|
10
|
+
|
|
11
|
+
@router.get("/health", response_model=SuccessResponse)
|
|
12
|
+
async def health_check(
|
|
13
|
+
service: HealthSvc
|
|
14
|
+
):
|
|
15
|
+
return service.health_check()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@router.get("/ready", response_model=SuccessResponse)
|
|
19
|
+
async def readiness_check(
|
|
20
|
+
service: HealthSvc,
|
|
21
|
+
):
|
|
22
|
+
return service.readiness_check()
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from fastapi import APIRouter, HTTPException, Depends
|
|
2
|
+
from typing import Dict, Any, Annotated
|
|
3
|
+
|
|
4
|
+
from nl2sql_api.dependencies import get_indexing_service
|
|
5
|
+
from nl2sql_api.services import IndexingService
|
|
6
|
+
router = APIRouter()
|
|
7
|
+
|
|
8
|
+
IndexingSvc = Annotated[IndexingService, Depends(get_indexing_service)]
|
|
9
|
+
|
|
10
|
+
@router.post("/index/{datasource_id}", response_model=Dict[str, Any])
|
|
11
|
+
def index_datasource(
|
|
12
|
+
datasource_id: str,
|
|
13
|
+
service: IndexingSvc
|
|
14
|
+
):
|
|
15
|
+
try:
|
|
16
|
+
result = service.index_datasource(datasource_id)
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
"success": True,
|
|
20
|
+
"datasource_id": datasource_id,
|
|
21
|
+
"indexing_stats": result,
|
|
22
|
+
"message": f"Successfully indexed datasource '{datasource_id}'",
|
|
23
|
+
}
|
|
24
|
+
except Exception as e:
|
|
25
|
+
raise HTTPException(
|
|
26
|
+
status_code=500,
|
|
27
|
+
detail=f"Failed to index datasource '{datasource_id}': {str(e)}",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@router.post("/index-all", response_model=Dict[str, Any])
|
|
32
|
+
def index_all_datasources(
|
|
33
|
+
service: IndexingSvc
|
|
34
|
+
):
|
|
35
|
+
try:
|
|
36
|
+
results = service.index_all_datasources()
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
"success": True,
|
|
40
|
+
"indexing_results": results,
|
|
41
|
+
"message": "Successfully initiated indexing for all datasources",
|
|
42
|
+
}
|
|
43
|
+
except Exception as e:
|
|
44
|
+
raise HTTPException(
|
|
45
|
+
status_code=500,
|
|
46
|
+
detail=f"Failed to index all datasources: {str(e)}",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@router.delete("/index", response_model=Dict[str, Any])
|
|
51
|
+
def clear_index(
|
|
52
|
+
service: IndexingSvc
|
|
53
|
+
):
|
|
54
|
+
try:
|
|
55
|
+
return service.clear_index()
|
|
56
|
+
except Exception as e:
|
|
57
|
+
raise HTTPException(
|
|
58
|
+
status_code=500,
|
|
59
|
+
detail=f"Failed to clear index: {str(e)}",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@router.get("/index/status", response_model=Dict[str, Any])
|
|
64
|
+
def get_index_status(
|
|
65
|
+
service: IndexingSvc
|
|
66
|
+
):
|
|
67
|
+
try:
|
|
68
|
+
return service.get_index_status()
|
|
69
|
+
except Exception as e:
|
|
70
|
+
raise HTTPException(
|
|
71
|
+
status_code=500,
|
|
72
|
+
detail=f"Failed to get index status: {str(e)}",
|
|
73
|
+
)
|
nl2sql_api/routes/llm.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from fastapi import APIRouter, HTTPException, Depends
|
|
2
|
+
from typing import Dict, Any, Annotated
|
|
3
|
+
|
|
4
|
+
from nl2sql_api.models.llm import LLMRequest, LLMResponse
|
|
5
|
+
from nl2sql_api.dependencies import get_llm_service
|
|
6
|
+
from nl2sql_api.services import LLMService
|
|
7
|
+
|
|
8
|
+
router = APIRouter()
|
|
9
|
+
|
|
10
|
+
LLMSvc = Annotated[LLMService, Depends(get_llm_service)]
|
|
11
|
+
|
|
12
|
+
@router.post("/llm", response_model=LLMResponse)
|
|
13
|
+
def configure_llm(
|
|
14
|
+
payload: LLMRequest,
|
|
15
|
+
service: LLMSvc,
|
|
16
|
+
):
|
|
17
|
+
try:
|
|
18
|
+
return service.configure_llm(payload)
|
|
19
|
+
except Exception as e:
|
|
20
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@router.get("/llm", response_model=Dict[str, Any])
|
|
24
|
+
def list_llms(
|
|
25
|
+
service: LLMSvc,
|
|
26
|
+
):
|
|
27
|
+
try:
|
|
28
|
+
return {"llms": service.list_llms()}
|
|
29
|
+
except Exception as e:
|
|
30
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@router.get("/llm/{llm_name}", response_model=Dict[str, Any])
|
|
34
|
+
def get_llm(
|
|
35
|
+
llm_name: str,
|
|
36
|
+
service: LLMSvc,
|
|
37
|
+
):
|
|
38
|
+
try:
|
|
39
|
+
return service.get_llm(llm_name)
|
|
40
|
+
except Exception as e:
|
|
41
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, HTTPException, Depends
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
from nl2sql_api.models.query import QueryRequest, QueryResponse
|
|
6
|
+
from nl2sql_api.dependencies import get_query_service
|
|
7
|
+
from nl2sql_api.services import QueryService
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
router = APIRouter()
|
|
12
|
+
|
|
13
|
+
QuerySvc = Annotated[QueryService, Depends(get_query_service)]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Synchronous on purpose: the pipeline performs blocking LLM and database calls,
|
|
17
|
+
# so Starlette runs this handler in its threadpool instead of on the event loop.
|
|
18
|
+
@router.post("/query", response_model=QueryResponse)
|
|
19
|
+
def execute_query(
|
|
20
|
+
payload: QueryRequest,
|
|
21
|
+
service: QuerySvc,
|
|
22
|
+
):
|
|
23
|
+
try:
|
|
24
|
+
return service.execute_query(payload)
|
|
25
|
+
except Exception:
|
|
26
|
+
# Pipeline failures are reported in QueryResponse.errors with a 200; reaching
|
|
27
|
+
# here means something genuinely unexpected broke.
|
|
28
|
+
logger.exception("Unexpected failure while executing query")
|
|
29
|
+
raise HTTPException(status_code=500, detail="Failed to execute query.")
|
nl2sql_api/server.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
NL2SQL API Startup Script
|
|
4
|
+
|
|
5
|
+
This script starts the NL2SQL API server.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import sys
|
|
10
|
+
from nl2sql_api.main import app
|
|
11
|
+
import uvicorn
|
|
12
|
+
|
|
13
|
+
def main():
|
|
14
|
+
parser = argparse.ArgumentParser(description='NL2SQL API Server')
|
|
15
|
+
parser.add_argument('--host', default='127.0.0.1', help='Host to bind to (default: 127.0.0.1)')
|
|
16
|
+
parser.add_argument('--port', type=int, default=8000, help='Port to bind to (default: 8000)')
|
|
17
|
+
parser.add_argument('--reload', action='store_true', help='Enable auto-reload (development)')
|
|
18
|
+
|
|
19
|
+
args = parser.parse_args()
|
|
20
|
+
|
|
21
|
+
uvicorn.run(
|
|
22
|
+
'nl2sql_api.main:app',
|
|
23
|
+
host=args.host,
|
|
24
|
+
port=args.port,
|
|
25
|
+
reload=args.reload,
|
|
26
|
+
log_level="info"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if __name__ == '__main__':
|
|
30
|
+
main()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
from .datasource import DatasourceService
|
|
3
|
+
from .llm import LLMService
|
|
4
|
+
from .health import HealthService
|
|
5
|
+
from .query import QueryService
|
|
6
|
+
from .indexing import IndexingService
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"DatasourceService",
|
|
11
|
+
"LLMService",
|
|
12
|
+
"HealthService",
|
|
13
|
+
"QueryService",
|
|
14
|
+
"IndexingService"
|
|
15
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional
|
|
2
|
+
|
|
3
|
+
from nl2sql import NL2SQL
|
|
4
|
+
from nl2sql_api.models.datasource import DatasourceRequest, DatasourceResponse
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DatasourceService:
|
|
8
|
+
def __init__(self, engine: NL2SQL):
|
|
9
|
+
self.engine = engine
|
|
10
|
+
|
|
11
|
+
def add_datasource(self, request: DatasourceRequest) -> DatasourceResponse:
|
|
12
|
+
"""Add a new datasource programmatically."""
|
|
13
|
+
self.engine.add_datasource(request.config)
|
|
14
|
+
return DatasourceResponse(
|
|
15
|
+
success=True,
|
|
16
|
+
message=f"Datasource '{request.config.get('id')}' added successfully",
|
|
17
|
+
datasource_id=request.config.get('id')
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
def list_datasources(self) -> list:
|
|
21
|
+
"""List all registered datasources."""
|
|
22
|
+
return self.engine.list_datasources()
|
|
23
|
+
|
|
24
|
+
def get_datasource(self, datasource_id: str) -> dict:
|
|
25
|
+
"""Get details of a specific datasource."""
|
|
26
|
+
datasource_ids = self.engine.list_datasources()
|
|
27
|
+
if datasource_id not in datasource_ids:
|
|
28
|
+
raise ValueError(f"Datasource '{datasource_id}' not found")
|
|
29
|
+
|
|
30
|
+
# In a real implementation, we would return detailed information about the datasource
|
|
31
|
+
return {"datasource_id": datasource_id, "exists": True}
|
|
32
|
+
|
|
33
|
+
def remove_datasource(self, datasource_id: str) -> dict:
|
|
34
|
+
"""Remove a datasource (not directly supported by the engine, but could be implemented)."""
|
|
35
|
+
datasource_ids = self.engine.list_datasources()
|
|
36
|
+
if datasource_id not in datasource_ids:
|
|
37
|
+
raise ValueError(f"Datasource '{datasource_id}' not found")
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
"success": False,
|
|
41
|
+
"message": "Removing datasources is not currently supported by the engine",
|
|
42
|
+
"datasource_id": datasource_id
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional
|
|
2
|
+
from nl2sql import NL2SQL
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class HealthService:
|
|
6
|
+
def __init__(self, engine: NL2SQL):
|
|
7
|
+
self.engine = engine
|
|
8
|
+
|
|
9
|
+
def health_check(self) -> dict:
|
|
10
|
+
"""Perform health check."""
|
|
11
|
+
return {
|
|
12
|
+
"success": True,
|
|
13
|
+
"message": "NL2SQL API is running"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def readiness_check(self) -> dict:
|
|
17
|
+
"""Perform readiness check."""
|
|
18
|
+
# Add actual readiness checks (database connections, etc.)
|
|
19
|
+
return {
|
|
20
|
+
"success": True,
|
|
21
|
+
"message": "NL2SQL API is ready"
|
|
22
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional
|
|
2
|
+
from nl2sql import NL2SQL
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class IndexingService:
|
|
6
|
+
def __init__(self, engine: NL2SQL):
|
|
7
|
+
self.engine = engine
|
|
8
|
+
|
|
9
|
+
def index_datasource(self, datasource_id: str) -> Dict[str, int]:
|
|
10
|
+
"""Index schema for a specific datasource."""
|
|
11
|
+
return self.engine.indexing.index_datasource(datasource_id)
|
|
12
|
+
|
|
13
|
+
def index_all_datasources(self) -> Dict[str, Dict[str, int]]:
|
|
14
|
+
"""Index schema for all registered datasources."""
|
|
15
|
+
return self.engine.indexing.index_all_datasources()
|
|
16
|
+
|
|
17
|
+
def clear_index(self) -> None:
|
|
18
|
+
"""Clear the vector store index."""
|
|
19
|
+
self.engine.indexing.clear_index()
|
|
20
|
+
return {"success": True, "message": "Index cleared successfully"}
|
|
21
|
+
|
|
22
|
+
def get_index_status(self) -> Dict[str, Any]:
|
|
23
|
+
"""Get the status of the index."""
|
|
24
|
+
# This would typically return information about the vector store
|
|
25
|
+
# For now, we'll return a placeholder
|
|
26
|
+
return {
|
|
27
|
+
"status": "operational",
|
|
28
|
+
"indexed_datasources": self.engine.list_datasources(),
|
|
29
|
+
"total_indexes": len(self.engine.list_datasources()) # Placeholder
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional
|
|
2
|
+
from nl2sql import NL2SQL
|
|
3
|
+
from nl2sql_api.models.llm import LLMRequest, LLMResponse
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LLMService:
|
|
7
|
+
def __init__(self, engine: NL2SQL):
|
|
8
|
+
self.engine = engine
|
|
9
|
+
|
|
10
|
+
def configure_llm(self, request: LLMRequest) -> LLMResponse:
|
|
11
|
+
"""Configure an LLM programmatically."""
|
|
12
|
+
self.engine.configure_llm(request.config)
|
|
13
|
+
return LLMResponse(
|
|
14
|
+
success=True,
|
|
15
|
+
message=f"LLM '{request.config.get('name', 'default')}' configured successfully",
|
|
16
|
+
llm_name=request.config.get('name', 'default')
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def list_llms(self) -> list:
|
|
20
|
+
"""List all configured LLMs."""
|
|
21
|
+
return self.engine.list_llms()
|
|
22
|
+
|
|
23
|
+
def get_llm(self, llm_name: str) -> dict:
|
|
24
|
+
"""Get details of a specific LLM."""
|
|
25
|
+
return self.engine.get_llm(llm_name)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from nl2sql import NL2SQL
|
|
2
|
+
from nl2sql_api.models.query import QueryRequest, QueryResponse
|
|
3
|
+
from nl2sql.auth.models import UserContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class QueryService:
|
|
7
|
+
def __init__(self, engine: NL2SQL):
|
|
8
|
+
self.engine = engine
|
|
9
|
+
|
|
10
|
+
def execute_query(self, request: QueryRequest) -> QueryResponse:
|
|
11
|
+
# Convert user_context if provided
|
|
12
|
+
user_context = None
|
|
13
|
+
if request.user_context:
|
|
14
|
+
user_context = UserContext(**request.user_context)
|
|
15
|
+
|
|
16
|
+
result = self.engine.run_query(
|
|
17
|
+
request.natural_language,
|
|
18
|
+
datasource_id=request.datasource_id,
|
|
19
|
+
execute=request.execute,
|
|
20
|
+
user_context=user_context,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
return QueryResponse(
|
|
24
|
+
sub_queries=[sub_query.model_dump() for sub_query in result.sub_queries],
|
|
25
|
+
final_answer=result.final_answer,
|
|
26
|
+
errors=result.errors,
|
|
27
|
+
trace_id=result.trace_id,
|
|
28
|
+
reasoning=result.reasoning,
|
|
29
|
+
warnings=result.warnings,
|
|
30
|
+
artifact_refs={
|
|
31
|
+
node_id: ref.model_dump(mode="json")
|
|
32
|
+
for node_id, ref in result.artifact_refs.items()
|
|
33
|
+
},
|
|
34
|
+
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nl2sql-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: API layer for NL2SQL engine
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Requires-Dist: nl2sql-engine~=0.1
|
|
7
|
+
Requires-Dist: fastapi>=0.100.0
|
|
8
|
+
Requires-Dist: uvicorn>=0.20.0
|
|
9
|
+
Requires-Dist: pydantic>=2.0
|
|
10
|
+
Requires-Dist: python-multipart>=0.0.6
|
|
11
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
nl2sql_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
nl2sql_api/dependencies.py,sha256=Vob6_ylRqX7A9mtLAk6P6C-dYaVj-sD6x_bgT1g7650,890
|
|
3
|
+
nl2sql_api/main.py,sha256=zqD6eVoBxuhLpb-KIaW8czwyu24EXLaIrMWrcQwXRkY,1287
|
|
4
|
+
nl2sql_api/server.py,sha256=spmORkrbdnRT5F0ayW5JqE60xaAEGItS-f_GYGvjzjc,787
|
|
5
|
+
nl2sql_api/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
nl2sql_api/models/datasource.py,sha256=05fWM3Oo3m86NsjwCU1fCyIVDRA6xU-WT2ayGwRO0us,247
|
|
7
|
+
nl2sql_api/models/llm.py,sha256=8WEtUg0vkW7v3fzsuMhhcUvHjrCrc0tH0Hy36devAhk,228
|
|
8
|
+
nl2sql_api/models/query.py,sha256=k2lC35rYLBJgz66QELc5kRW2N8LazQb89qIVbmqKNQA,1106
|
|
9
|
+
nl2sql_api/models/response.py,sha256=unon5gOq3FeLKULMNjgP5dmy3b_eF9b9EybuhbL4MBU,320
|
|
10
|
+
nl2sql_api/models/schema.py,sha256=9npDcphba7ZZ19d11wQKv6iNcwBwuedrBNleNi5aejE,251
|
|
11
|
+
nl2sql_api/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
nl2sql_api/routes/datasource.py,sha256=MCOwfA9xAr2CIoVJ5QFu3gdRr6TghLcFYWoHFEGHcq4,1728
|
|
13
|
+
nl2sql_api/routes/health.py,sha256=KzcYxGZvvi-Bt_Ltl2Z8vmQ9ycrbiy3_AAxflm6tkqk,600
|
|
14
|
+
nl2sql_api/routes/indexing.py,sha256=_UwxBZrGh66yMrWVtq2qQvfS3gAOgXm_S8y11mxu6-8,2021
|
|
15
|
+
nl2sql_api/routes/llm.py,sha256=t4sKlQpkHE1JRH_J1NFyEwBUbXbRFoNWxkPdGA0Cyis,1078
|
|
16
|
+
nl2sql_api/routes/query.py,sha256=lkeX8vlOTc7ULsqdm7K0TAG-AVSAINf6hUz9I0MOGk4,1046
|
|
17
|
+
nl2sql_api/services/__init__.py,sha256=wai2Xe8XI6FIbEqrfwQD2Ghm19YuGM0l1xzzgAjT9IM,296
|
|
18
|
+
nl2sql_api/services/datasource.py,sha256=XHioCMbZw0YDnFzYYxSSXd7R-T6ObQf6t0SZP510qx8,1753
|
|
19
|
+
nl2sql_api/services/health.py,sha256=hmv4IN4HqXqJXRHbdYuQdPnwUYUJ84Shdsa6-4CsTqY,579
|
|
20
|
+
nl2sql_api/services/indexing.py,sha256=TCqJFNqtO4RNsyFTfcmgVoH6FODsOer6bQTWdD1zVKs,1158
|
|
21
|
+
nl2sql_api/services/llm.py,sha256=U4GmHI4Np2kuELEqYS1xI0lnApYRY2x8Nw8C9-mo-W8,843
|
|
22
|
+
nl2sql_api/services/query.py,sha256=tk-RowcxnOKQxd6JAVuvaiuyAc_PkgLBaOpbBxjqnGU,1159
|
|
23
|
+
nl2sql_api-0.1.0.dist-info/METADATA,sha256=8hV-DfQ_fV4p0dxtIqceb4JDzh6AGKafe2cvx1xZywI,315
|
|
24
|
+
nl2sql_api-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
25
|
+
nl2sql_api-0.1.0.dist-info/entry_points.txt,sha256=p5MLHkru6-8adKBwwpySsJgAsEvKHF8LDedWdw9w23M,54
|
|
26
|
+
nl2sql_api-0.1.0.dist-info/top_level.txt,sha256=c_OEDdzXMoZTtOaZS8l4qKaNF0Cz5AesYzLye2pOSvA,11
|
|
27
|
+
nl2sql_api-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nl2sql_api
|