Adding a New Adapter
This guide describes how to add a new datasource adapter and make it available to NL2SQL.
1. Implement the adapter contract
All adapters implement DatasourceAdapter, version 2, and declare a connection_model. See ../adapters/sdk.md for the authoritative contract and required fields/methods.
If you are building a SQL adapter, extend SQLAlchemyAdapter from nl2sql_sdk.sqlalchemy (pip install "nl2sql-sdk[sqlalchemy]"): it connects, executes, reads the schema the datasource's index scope wants with catalog row estimates and type-aware statistics, and reports what it could not read as issues. You implement url and get_dialect, and override system_schemas, row_estimates or column_profile where your database differs. See SQLAlchemy adapters and the complete example in Building adapters.
2. Register it
The engine finds an adapter by its type name in the
registry. There are two
ways to put a name there.
From a package, declare an entry point in the nl2sql.adapters group of
your pyproject.toml:
Registry.default() lists every installed entry point by name and imports none
of them. An adapter is imported when a datasource uses its type.
In your own code, register the class on a registry you build:
from nl2sql.registry import Registry
registry = Registry.default()
@registry.adapter("snowflake")
class SnowflakeAdapter: ...
# or: registry.add_adapter("snowflake", SnowflakeAdapter)
A registration belongs to that registry only, so two engines or two tests never
see each other's. Adding a name that is already there, including a built-in,
raises unless you pass replace=True.
When the adapter is loaded, the registry checks three things and stops with a message that says how to fix each:
| Check | Fails with |
|---|---|
interface_version is the version this engine needs (2) |
nl2sql-snowflake implements adapter interface 1; this engine needs 2. |
every module in driver_modules is installed (importlib.util.find_spec, without importing it) |
the missing modules and your install_hint |
| only one installed package declares the name | both package names |
An unknown type suggests the closest registered name. nl2sql list-adapters
shows every registered adapter and whether it can be loaded.
3. Configure the datasource
Add a datasource to the datasources section of nl2sql.yaml, for example as
its own file in datasources/:
# datasources/sales_db.yaml
type: postgres
description: "Sales warehouse"
connection:
host: "${env:PG_HOST}"
database: sales
user: "${env:PG_USER}"
password: "${env:PG_PASSWORD}"
References under connection are resolved by the engine's SecretManager
when the adapter is built, and the block is then validated against the
adapter's connection_model: an unknown key is an error.
4. Validate capabilities
Ensure capabilities() advertises the correct set for routing and execution:
supports_sqlif SQL execution is supported.supports_bound_parametersifexecute(andexecute_batches) bindAdapterRequest.parametersby name to:nameplaceholders through the driver. Without it, a verified query with parameters is never sent to the adapter. Adapters built onSQLAlchemyAdapterinherit both the binding and the capability.enforces_read_onlyandenforces_timeoutonly when the session cannot write, and a statement pastlimits.timeout_msis stopped, on a real server.
Source references
- Adapter protocol:
packages/sdk/src/nl2sql_sdk/protocols.py - Registry:
packages/nl2sql/src/nl2sql/registry.py - Datasource registry:
packages/nl2sql/src/nl2sql/datasources/registry.py - SQLAlchemy base adapter:
packages/sdk/src/nl2sql_sdk/sqlalchemy/adapter.py