nl2sql-api 0.1.2__py3-none-any.whl → 0.2.1__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/auth.py ADDED
@@ -0,0 +1,92 @@
1
+ """Where the caller's RBAC role comes from.
2
+
3
+ The engine enforces RBAC; this module only decides which role a request carries.
4
+ It is a seam, not an auth provider: put a proxy that authenticates the caller in
5
+ front of the API and have it set the role header, or pin one static role.
6
+
7
+ Sources, first match wins:
8
+
9
+ 1. ``NL2SQL_API_ROLE_HEADER``: the name of a header a trusted proxy sets, for
10
+ example ``X-NL2SQL-Role``. Comma-separated values are several roles. Only
11
+ configure it when the proxy overwrites that header on every request, or any
12
+ client can pick its own role.
13
+ 2. ``NL2SQL_API_ROLE``: one static role for every request.
14
+ 3. ``user_context`` in the request body, only with the dev flag
15
+ ``NL2SQL_API_TRUST_BODY_ROLE=true``. For local testing: it lets any client
16
+ choose any role.
17
+
18
+ No role is a 401. The settings are read on each request.
19
+ """
20
+
21
+ import logging
22
+ import os
23
+ from typing import Any, List
24
+
25
+ from fastapi import HTTPException, Request, status
26
+ from pydantic import ValidationError
27
+
28
+ from nl2sql import UserContext
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ ROLE_HEADER_ENV = "NL2SQL_API_ROLE_HEADER"
33
+ STATIC_ROLE_ENV = "NL2SQL_API_ROLE"
34
+ TRUST_BODY_ROLE_ENV = "NL2SQL_API_TRUST_BODY_ROLE"
35
+
36
+
37
+ def _body_role_trusted() -> bool:
38
+ return os.getenv(TRUST_BODY_ROLE_ENV, "").strip().lower() in {"1", "true", "yes", "on"}
39
+
40
+
41
+ def _roles(value: str) -> List[str]:
42
+ return [role.strip() for role in value.split(",") if role.strip()]
43
+
44
+
45
+ def warn_if_body_role_trusted() -> None:
46
+ """Log a warning when the dev flag lets clients choose their own role."""
47
+ if _body_role_trusted():
48
+ logger.warning(
49
+ "%s is on: the RBAC role is taken from the request body, so any client "
50
+ "can choose any role. Use it for local testing only.",
51
+ TRUST_BODY_ROLE_ENV,
52
+ )
53
+
54
+
55
+ async def _body_user_context(request: Request) -> Any:
56
+ try:
57
+ body = await request.json()
58
+ except Exception:
59
+ return None
60
+ return body.get("user_context") if isinstance(body, dict) else None
61
+
62
+
63
+ async def get_user_context(request: Request) -> UserContext:
64
+ """Return the caller's ``UserContext``, or raise 401 when no role is configured."""
65
+ header = os.getenv(ROLE_HEADER_ENV, "").strip()
66
+ if header:
67
+ roles = _roles(request.headers.get(header, ""))
68
+ if roles:
69
+ return UserContext(roles=roles)
70
+
71
+ static_role = os.getenv(STATIC_ROLE_ENV, "").strip()
72
+ if static_role:
73
+ return UserContext(roles=_roles(static_role))
74
+
75
+ if _body_role_trusted():
76
+ payload = await _body_user_context(request)
77
+ if isinstance(payload, dict):
78
+ try:
79
+ user_context = UserContext(**payload)
80
+ except ValidationError:
81
+ user_context = None
82
+ if user_context and user_context.roles:
83
+ return user_context
84
+
85
+ raise HTTPException(
86
+ status_code=status.HTTP_401_UNAUTHORIZED,
87
+ detail=(
88
+ "No role for this request. Configure a trusted proxy header "
89
+ f"({ROLE_HEADER_ENV}) or a static role ({STATIC_ROLE_ENV}). The body's "
90
+ f"user_context is ignored unless {TRUST_BODY_ROLE_ENV}=true (local testing only)."
91
+ ),
92
+ )
nl2sql_api/main.py CHANGED
@@ -6,20 +6,22 @@ from importlib.metadata import PackageNotFoundError, version
6
6
 
7
7
  from .routes import query, health, datasource, llm, indexing
8
8
  from fastapi.middleware.cors import CORSMiddleware
9
- from nl2sql import NL2SQL
10
- from nl2sql.common.logger import configure_logging
11
- from nl2sql.common.settings import settings
9
+ from nl2sql import NL2SQL, configure_logging
10
+ from .auth import warn_if_body_role_trusted
12
11
 
13
12
 
14
13
  @asynccontextmanager
15
14
  async def lifespan(app: FastAPI):
16
15
  # The library no longer configures logging on import, so the application
17
- # entry point owns it - before anything that logs is constructed.
18
- configure_logging(
19
- level="INFO",
20
- json_format=(settings.observability_exporter == "otlp"),
21
- )
22
- app.state.engine = NL2SQL()
16
+ # entry point owns it - before anything that logs is constructed. The
17
+ # exporter is read through the facade once the engine has loaded settings;
18
+ # an OTLP deployment then switches to JSON logs.
19
+ configure_logging(level="INFO")
20
+ engine = NL2SQL()
21
+ if engine.get_setting("observability_exporter") == "otlp":
22
+ configure_logging(level="INFO", json_format=True)
23
+ warn_if_body_role_trusted()
24
+ app.state.engine = engine
23
25
  yield
24
26
 
25
27
 
@@ -1,33 +1,34 @@
1
+ from typing import Any, Dict, List, Optional
2
+
1
3
  from pydantic import BaseModel, Field
2
- from typing import Optional, Dict, Any, List
4
+
5
+ from nl2sql import QueryResult, SubQueryResult
3
6
 
4
7
 
5
8
  class QueryRequest(BaseModel):
6
9
  natural_language: str
7
10
  datasource_id: Optional[str] = None
8
11
  execute: bool = True
9
- user_context: Optional[Dict[str, Any]] = None
12
+ user_context: Optional[Dict[str, Any]] = Field(
13
+ default=None,
14
+ description=(
15
+ "Ignored unless NL2SQL_API_TRUST_BODY_ROLE=true (local testing only); "
16
+ "the role otherwise comes from the API's auth settings."
17
+ ),
18
+ )
10
19
 
11
20
 
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 = ""
21
+ class SubQueryResponse(SubQueryResult):
22
+ """One decomposed sub-query: its plan, validation checks, SQL and row sample."""
19
23
 
20
24
 
21
- class QueryResponse(BaseModel):
22
- """Mirrors ``nl2sql.api.query_api.QueryResult``.
25
+ class QueryResponse(QueryResult):
26
+ """The engine's ``QueryResult``, served as is.
23
27
 
24
- Result rows are not inlined; they live in artifact storage and are addressable
25
- through ``artifact_refs``.
28
+ It derives from ``nl2sql.QueryResult``, so the HTTP response cannot drift
29
+ from what the engine returns. ``sub_queries[].rows`` carries a capped
30
+ sample only; the full result set lives in artifact storage, addressable
31
+ through ``artifact_refs``. ``trace_path`` is where the run's trace was
32
+ written on the server, or None.
26
33
  """
27
34
  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)
@@ -5,37 +5,43 @@ from nl2sql_api.models.datasource import DatasourceRequest, DatasourceResponse
5
5
  from nl2sql_api.dependencies import get_datasource_service
6
6
  from nl2sql_api.services import DatasourceService
7
7
 
8
- router = APIRouter()
8
+ router = APIRouter(tags=["datasources"])
9
9
 
10
10
  DatasourceSvc = Annotated[DatasourceService, Depends(get_datasource_service)]
11
11
 
12
12
 
13
- @router.post("/datasource", response_model=DatasourceResponse)
13
+ @router.post("/datasource", response_model=DatasourceResponse, summary="Add a datasource")
14
14
  def add_datasource(
15
15
  payload: DatasourceRequest,
16
16
  service: DatasourceSvc,
17
17
  ):
18
+ """Register a datasource in this process from `config`, shaped like one entry of
19
+ `configs/datasources.yaml`. It is not written to the config file and not indexed:
20
+ call `POST /api/v1/index/{datasource_id}` next.
21
+ """
18
22
  try:
19
23
  return service.add_datasource(payload)
20
24
  except Exception as e:
21
25
  raise HTTPException(status_code=500, detail=str(e))
22
26
 
23
27
 
24
- @router.get("/datasource", response_model=Dict[str, Any])
28
+ @router.get("/datasource", response_model=Dict[str, Any], summary="List datasources")
25
29
  def list_datasources(
26
30
  service: DatasourceSvc,
27
31
  ):
32
+ """The ids of the registered datasources, as `{"datasources": [...]}`."""
28
33
  try:
29
34
  return {"datasources": service.list_datasources()}
30
35
  except Exception as e:
31
36
  raise HTTPException(status_code=500, detail=str(e))
32
37
 
33
38
 
34
- @router.get("/datasource/{datasource_id}", response_model=Dict[str, Any])
39
+ @router.get("/datasource/{datasource_id}", response_model=Dict[str, Any], summary="Check a datasource exists")
35
40
  def get_datasource(
36
41
  datasource_id: str,
37
42
  service: DatasourceSvc,
38
43
  ):
44
+ """`{"datasource_id": ..., "exists": true}`, or 404 when it is not registered. No other details yet."""
39
45
  try:
40
46
  return service.get_datasource(datasource_id)
41
47
  except ValueError as e:
@@ -44,11 +50,12 @@ def get_datasource(
44
50
  raise HTTPException(status_code=500, detail=str(e))
45
51
 
46
52
 
47
- @router.delete("/datasource/{datasource_id}", response_model=Dict[str, Any])
53
+ @router.delete("/datasource/{datasource_id}", response_model=Dict[str, Any], summary="Remove a datasource (not supported)")
48
54
  def remove_datasource(
49
55
  datasource_id: str,
50
56
  service: DatasourceSvc,
51
57
  ):
58
+ """Not supported by the engine yet: answers `success: false` (404 for an unknown id)."""
52
59
  try:
53
60
  return service.remove_datasource(datasource_id)
54
61
  except ValueError as e:
@@ -4,19 +4,21 @@ from nl2sql_api.models.response import SuccessResponse
4
4
  from nl2sql_api.dependencies import get_health_service
5
5
  from nl2sql_api.services import HealthService
6
6
 
7
- router = APIRouter()
7
+ router = APIRouter(tags=["health"])
8
8
 
9
9
  HealthSvc = Annotated[HealthService, Depends(get_health_service)]
10
10
 
11
- @router.get("/health", response_model=SuccessResponse)
11
+ @router.get("/health", response_model=SuccessResponse, summary="Liveness check")
12
12
  async def health_check(
13
13
  service: HealthSvc
14
14
  ):
15
+ """Returns `success: true` while the process is serving. Checks nothing else."""
15
16
  return service.health_check()
16
17
 
17
18
 
18
- @router.get("/ready", response_model=SuccessResponse)
19
+ @router.get("/ready", response_model=SuccessResponse, summary="Readiness check")
19
20
  async def readiness_check(
20
21
  service: HealthSvc,
21
22
  ):
23
+ """Returns `success: true`. It does not yet check datasources, the LLM or the index."""
22
24
  return service.readiness_check()
@@ -3,15 +3,16 @@ from typing import Dict, Any, Annotated
3
3
 
4
4
  from nl2sql_api.dependencies import get_indexing_service
5
5
  from nl2sql_api.services import IndexingService
6
- router = APIRouter()
6
+ router = APIRouter(tags=["indexing"])
7
7
 
8
8
  IndexingSvc = Annotated[IndexingService, Depends(get_indexing_service)]
9
9
 
10
- @router.post("/index/{datasource_id}", response_model=Dict[str, Any])
10
+ @router.post("/index/{datasource_id}", response_model=Dict[str, Any], summary="Index one datasource")
11
11
  def index_datasource(
12
12
  datasource_id: str,
13
13
  service: IndexingSvc
14
14
  ):
15
+ """Index one datasource's schema into the vector store; returns the indexing stats."""
15
16
  try:
16
17
  result = service.index_datasource(datasource_id)
17
18
 
@@ -28,10 +29,11 @@ def index_datasource(
28
29
  )
29
30
 
30
31
 
31
- @router.post("/index-all", response_model=Dict[str, Any])
32
+ @router.post("/index-all", response_model=Dict[str, Any], summary="Index every datasource")
32
33
  def index_all_datasources(
33
34
  service: IndexingSvc
34
35
  ):
36
+ """Index every registered datasource; returns the stats per datasource."""
35
37
  try:
36
38
  results = service.index_all_datasources()
37
39
 
@@ -47,10 +49,11 @@ def index_all_datasources(
47
49
  )
48
50
 
49
51
 
50
- @router.delete("/index", response_model=Dict[str, Any])
52
+ @router.delete("/index", response_model=Dict[str, Any], summary="Clear the index")
51
53
  def clear_index(
52
54
  service: IndexingSvc
53
55
  ):
56
+ """Remove every entry from the vector store. Questions need a re-index before they can be answered."""
54
57
  try:
55
58
  return service.clear_index()
56
59
  except Exception as e:
@@ -60,10 +63,13 @@ def clear_index(
60
63
  )
61
64
 
62
65
 
63
- @router.get("/index/status", response_model=Dict[str, Any])
66
+ @router.get("/index/status", response_model=Dict[str, Any], summary="Index status")
64
67
  def get_index_status(
65
68
  service: IndexingSvc
66
69
  ):
70
+ """The vector index's health: `status` (`ok`, `empty`, `stale` or `missing`), entry counts by
71
+ type, when it was built, the embedding model, one entry per registered datasource and any
72
+ problems found. Read from the live index; the same report the playground shows."""
67
73
  try:
68
74
  return service.get_index_status()
69
75
  except Exception as e:
nl2sql_api/routes/llm.py CHANGED
@@ -5,36 +5,41 @@ from nl2sql_api.models.llm import LLMRequest, LLMResponse
5
5
  from nl2sql_api.dependencies import get_llm_service
6
6
  from nl2sql_api.services import LLMService
7
7
 
8
- router = APIRouter()
8
+ router = APIRouter(tags=["llm"])
9
9
 
10
10
  LLMSvc = Annotated[LLMService, Depends(get_llm_service)]
11
11
 
12
- @router.post("/llm", response_model=LLMResponse)
12
+ @router.post("/llm", response_model=LLMResponse, summary="Configure an LLM")
13
13
  def configure_llm(
14
14
  payload: LLMRequest,
15
15
  service: LLMSvc,
16
16
  ):
17
+ """Register an LLM in this process from `config` (`name` defaults to `default`).
18
+ It is not written to `configs/llm.yaml`.
19
+ """
17
20
  try:
18
21
  return service.configure_llm(payload)
19
22
  except Exception as e:
20
23
  raise HTTPException(status_code=500, detail=str(e))
21
24
 
22
25
 
23
- @router.get("/llm", response_model=Dict[str, Any])
26
+ @router.get("/llm", response_model=Dict[str, Any], summary="List LLMs")
24
27
  def list_llms(
25
28
  service: LLMSvc,
26
29
  ):
30
+ """The configured LLMs, as `{"llms": ...}`."""
27
31
  try:
28
32
  return {"llms": service.list_llms()}
29
33
  except Exception as e:
30
34
  raise HTTPException(status_code=500, detail=str(e))
31
35
 
32
36
 
33
- @router.get("/llm/{llm_name}", response_model=Dict[str, Any])
37
+ @router.get("/llm/{llm_name}", response_model=Dict[str, Any], summary="Get an LLM")
34
38
  def get_llm(
35
39
  llm_name: str,
36
40
  service: LLMSvc,
37
41
  ):
42
+ """One configured LLM by name."""
38
43
  try:
39
44
  return service.get_llm(llm_name)
40
45
  except Exception as e:
@@ -2,26 +2,45 @@ import logging
2
2
 
3
3
  from fastapi import APIRouter, HTTPException, Depends
4
4
  from typing import Annotated
5
+ from nl2sql import UserContext
6
+ from nl2sql_api import auth
5
7
  from nl2sql_api.models.query import QueryRequest, QueryResponse
6
8
  from nl2sql_api.dependencies import get_query_service
7
9
  from nl2sql_api.services import QueryService
8
10
 
9
11
  logger = logging.getLogger(__name__)
10
12
 
11
- router = APIRouter()
13
+ router = APIRouter(tags=["query"])
12
14
 
13
15
  QuerySvc = Annotated[QueryService, Depends(get_query_service)]
16
+ Caller = Annotated[UserContext, Depends(auth.get_user_context)]
14
17
 
15
18
 
16
19
  # Synchronous on purpose: the pipeline performs blocking LLM and database calls,
17
20
  # so Starlette runs this handler in its threadpool instead of on the event loop.
18
- @router.post("/query", response_model=QueryResponse)
21
+ @router.post(
22
+ "/query",
23
+ response_model=QueryResponse,
24
+ summary="Ask a question",
25
+ responses={401: {"description": "No role for this request (see the auth settings)."}},
26
+ )
19
27
  def execute_query(
20
28
  payload: QueryRequest,
21
29
  service: QuerySvc,
30
+ user_context: Caller,
22
31
  ):
32
+ """Run a natural-language question through the pipeline.
33
+
34
+ Returns, per sub-query, the plan, the validation checks, the SQL and a capped row
35
+ sample, plus the written answer, errors, timings and token usage. A pipeline
36
+ failure (a refusal included) comes back with status 200 and `status: "error"`.
37
+ `execute: false` plans and validates without running SQL. The RBAC role comes
38
+ from a trusted proxy header (`NL2SQL_API_ROLE_HEADER`) or a static role
39
+ (`NL2SQL_API_ROLE`); the body's `user_context` counts only with the dev flag
40
+ `NL2SQL_API_TRUST_BODY_ROLE=true`. No role is a 401.
41
+ """
23
42
  try:
24
- return service.execute_query(payload)
43
+ return service.execute_query(payload, user_context)
25
44
  except Exception:
26
45
  # Pipeline failures are reported in QueryResponse.errors with a 200; reaching
27
46
  # here means something genuinely unexpected broke.
@@ -20,11 +20,5 @@ class IndexingService:
20
20
  return {"success": True, "message": "Index cleared successfully"}
21
21
 
22
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
- }
23
+ """The vector index's health, as ``NL2SQL.index_health`` reports it."""
24
+ return self.engine.index_health()
@@ -1,34 +1,17 @@
1
- from nl2sql import NL2SQL
1
+ from nl2sql import NL2SQL, UserContext
2
2
  from nl2sql_api.models.query import QueryRequest, QueryResponse
3
- from nl2sql.auth.models import UserContext
4
3
 
5
4
 
6
5
  class QueryService:
7
6
  def __init__(self, engine: NL2SQL):
8
7
  self.engine = engine
9
8
 
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
-
9
+ def execute_query(self, request: QueryRequest, user_context: UserContext) -> QueryResponse:
10
+ """Run the question as ``user_context``, which the auth dependency supplies."""
16
11
  result = self.engine.run_query(
17
12
  request.natural_language,
18
13
  datasource_id=request.datasource_id,
19
14
  execute=request.execute,
20
15
  user_context=user_context,
21
16
  )
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
- )
17
+ return QueryResponse.model_validate(result.model_dump())
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: nl2sql-api
3
+ Version: 0.2.1
4
+ Summary: FastAPI REST service for the nl2sql engine: ask a database questions in English over HTTP
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/nadeem4/nl2sql
7
+ Project-URL: Documentation, https://nadeem4.github.io/nl2sql/
8
+ Project-URL: Source, https://github.com/nadeem4/nl2sql
9
+ Project-URL: Issues, https://github.com/nadeem4/nl2sql/issues
10
+ Project-URL: Demo, https://nadeem4nk-nl2sql-demo.hf.space
11
+ Keywords: nl2sql,text-to-sql,natural language,sql,llm,fastapi,rest api
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: FastAPI
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Database
20
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
21
+ Requires-Python: >=3.12
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: nl2sql-engine~=0.1
24
+ Requires-Dist: fastapi>=0.100.0
25
+ Requires-Dist: uvicorn>=0.20.0
26
+ Requires-Dist: pydantic>=2.0
27
+ Requires-Dist: python-multipart>=0.0.6
28
+ Requires-Dist: python-dotenv>=1.0.0
29
+
30
+ # NL2SQL API
31
+
32
+ FastAPI REST service for the NL2SQL engine (`nl2sql-engine`). It builds one
33
+ `NL2SQL` engine at startup, from the same env file and configs as the CLI, and
34
+ serves it under `/api/v1`.
35
+
36
+ ## Running the API
37
+
38
+ ```bash
39
+ pip install nl2sql-api
40
+ ENV=demo NL2SQL_API_ROLE=admin nl2sql-api --host 127.0.0.1 --port 8000 [--reload]
41
+ # or: python -m nl2sql_api.server ..., or: uvicorn nl2sql_api.main:app
42
+ ```
43
+
44
+ Start it from the folder holding the env file and configs (the demo's use
45
+ relative paths). Interactive docs: Swagger UI at `/docs`, ReDoc at `/redoc`,
46
+ the schema at `/openapi.json`.
47
+
48
+ The API does not authenticate callers and never takes the RBAC role from the
49
+ request body. Set one of: `NL2SQL_API_ROLE_HEADER` (a header a trusted proxy
50
+ sets), `NL2SQL_API_ROLE` (one static role) or, for local testing only,
51
+ `NL2SQL_API_TRUST_BODY_ROLE=true` (the body's `user_context`; logs a warning).
52
+ With none, `/api/v1/query` answers HTTP 401.
53
+
54
+ ## Endpoints
55
+
56
+ - `POST /api/v1/query` - Ask a question as the configured role
57
+ - `GET /api/v1/health` - Liveness check
58
+ - `GET /api/v1/ready` - Readiness check (does not yet check dependencies)
59
+ - `POST /api/v1/datasource` - Register a datasource in the running process
60
+ - `GET /api/v1/datasource` - List registered datasource ids
61
+ - `GET /api/v1/datasource/{datasource_id}` - Check a datasource is registered
62
+ - `DELETE /api/v1/datasource/{datasource_id}` - Not supported yet (answers `success: false`)
63
+ - `POST /api/v1/llm` - Configure an LLM in the running process
64
+ - `GET /api/v1/llm` - List configured LLMs
65
+ - `GET /api/v1/llm/{llm_name}` - Get one configured LLM
66
+ - `POST /api/v1/index/{datasource_id}` - Index one datasource's schema
67
+ - `POST /api/v1/index-all` - Index every registered datasource
68
+ - `DELETE /api/v1/index` - Clear the vector store
69
+ - `GET /api/v1/index/status` - Index health (status, entries by type, embedding model, problems)
70
+
71
+ Reference: <https://github.com/nadeem4/nl2sql/blob/main/docs/api/rest/index.md>.
@@ -1,27 +1,28 @@
1
1
  nl2sql_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ nl2sql_api/auth.py,sha256=iLiRWaHJQce_8oWQ22JM0lABf-z91Ai9GaEDe-3FIKY,3211
2
3
  nl2sql_api/dependencies.py,sha256=Vob6_ylRqX7A9mtLAk6P6C-dYaVj-sD6x_bgT1g7650,890
3
- nl2sql_api/main.py,sha256=64PxKKk7nTBiTNmi2Ozan4h3_MKUyV11sjTUiWyXIkQ,1806
4
+ nl2sql_api/main.py,sha256=0O_dpCbhqc-sCOQpz25X6mf9HSNiYfVO2_arc8JrXIg,2003
4
5
  nl2sql_api/server.py,sha256=spmORkrbdnRT5F0ayW5JqE60xaAEGItS-f_GYGvjzjc,787
5
6
  nl2sql_api/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
7
  nl2sql_api/models/datasource.py,sha256=05fWM3Oo3m86NsjwCU1fCyIVDRA6xU-WT2ayGwRO0us,247
7
8
  nl2sql_api/models/llm.py,sha256=8WEtUg0vkW7v3fzsuMhhcUvHjrCrc0tH0Hy36devAhk,228
8
- nl2sql_api/models/query.py,sha256=k2lC35rYLBJgz66QELc5kRW2N8LazQb89qIVbmqKNQA,1106
9
+ nl2sql_api/models/query.py,sha256=u_UGLcChK9_lrXV4yS9OVI4QmhWNbtjPUHCdm-nZ7dU,1154
9
10
  nl2sql_api/models/response.py,sha256=unon5gOq3FeLKULMNjgP5dmy3b_eF9b9EybuhbL4MBU,320
10
11
  nl2sql_api/models/schema.py,sha256=9npDcphba7ZZ19d11wQKv6iNcwBwuedrBNleNi5aejE,251
11
12
  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
13
+ nl2sql_api/routes/datasource.py,sha256=OMVde4EO5E51bpjZlexN3x743LJcoRnsxNMNoJCHcLw,2398
14
+ nl2sql_api/routes/health.py,sha256=aisKvQsvRDiZHQ1oP1igIo1xq-70fDR2ABhmBh5uRW8,845
15
+ nl2sql_api/routes/indexing.py,sha256=PJYDjoehrEZLfID7wMWCqbacTv7dHDhq23jJA5E6cx8,2711
16
+ nl2sql_api/routes/llm.py,sha256=rhTf8pkXQxmY4dZ6DREqxqy4oT0vs1f9WO8SgiJVhuU,1388
17
+ nl2sql_api/routes/query.py,sha256=NxNtKMHI5PzCpsjIrvlpeSsg18pZl5wt-ZhRvp8OQhs,1971
17
18
  nl2sql_api/services/__init__.py,sha256=wai2Xe8XI6FIbEqrfwQD2Ghm19YuGM0l1xzzgAjT9IM,296
18
19
  nl2sql_api/services/datasource.py,sha256=XHioCMbZw0YDnFzYYxSSXd7R-T6ObQf6t0SZP510qx8,1753
19
20
  nl2sql_api/services/health.py,sha256=hmv4IN4HqXqJXRHbdYuQdPnwUYUJ84Shdsa6-4CsTqY,579
20
- nl2sql_api/services/indexing.py,sha256=TCqJFNqtO4RNsyFTfcmgVoH6FODsOer6bQTWdD1zVKs,1158
21
+ nl2sql_api/services/indexing.py,sha256=mIyoppv83-MJP0xAQXvDLqKwWZ-2V6l1VHjx15Py6Oc,907
21
22
  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.2.dist-info/METADATA,sha256=WRQDSypFmg5BMZhZcW6WJRkrw9ouXm1w2PhN6cJHuhc,315
24
- nl2sql_api-0.1.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
25
- nl2sql_api-0.1.2.dist-info/entry_points.txt,sha256=p5MLHkru6-8adKBwwpySsJgAsEvKHF8LDedWdw9w23M,54
26
- nl2sql_api-0.1.2.dist-info/top_level.txt,sha256=c_OEDdzXMoZTtOaZS8l4qKaNF0Cz5AesYzLye2pOSvA,11
27
- nl2sql_api-0.1.2.dist-info/RECORD,,
23
+ nl2sql_api/services/query.py,sha256=1-jzT2VN6lQVmVTEsdK5PAAtjFck7lXu0dfetXKd63k,657
24
+ nl2sql_api-0.2.1.dist-info/METADATA,sha256=1NniqpaL9rRId8vOn4S1v4hWLLAhCBvFyNiULro1gqA,3175
25
+ nl2sql_api-0.2.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
26
+ nl2sql_api-0.2.1.dist-info/entry_points.txt,sha256=p5MLHkru6-8adKBwwpySsJgAsEvKHF8LDedWdw9w23M,54
27
+ nl2sql_api-0.2.1.dist-info/top_level.txt,sha256=c_OEDdzXMoZTtOaZS8l4qKaNF0Cz5AesYzLye2pOSvA,11
28
+ nl2sql_api-0.2.1.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: nl2sql-api
3
- Version: 0.1.2
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