laravel-sql maintained by dbmind
Why the SQL-only edition?
Install the package, paste an API key, and ask your database questions in plain
language. For every question an AI model writes a single read-only SELECT
(it may JOIN your approved tables), the query is validated by SqlGuard,
run on a read-only connection with a statement timeout, and the real
result rows are handed back to the model to phrase a grounded answer.
There is no Qdrant, no embeddings, no vector retrieval — and no per-table configuration: during install you only choose which tables the AI must NOT answer from. Everything else is approved and auto-configured from the schema (column types, foreign keys, indexes).
It also adds a clarifying-question loop: when a question is genuinely ambiguous the model asks short questions, the CLI collects the answers, and it re-asks with them in context.
⚡ Quickstart
composer require dbmind/laravel-sql
Cloud mode needs nothing but an API key from your DBMind dashboard. Local mode needs Ollama with a SQL-capable chat model:
ollama pull qwen2.5-coder:7b
Then run the wizard:
php artisan dbmind1:install # 1) cloud API key or local Ollama 2) exclude tables → done
php artisan dbmind1:doctor # health: DB read access + the AI gateway
php artisan dbmind1:discover # read-only: list connections, tables, columns
php artisan dbmind1:sync # after migrations: auto-approve new tables, refresh metadata
php artisan dbmind1:test "which users spent the most? top 3 spenders" --show-sql
[!NOTE] The install wizard asks "Which tables should the AI NOT answer from?" — framework tables (migrations, jobs, sessions, …) are pre-selected. Every other table is approved and auto-configured: primary key, readable/filterable/numeric columns, foreign-key join paths and indexed columns are inferred from the schema. Fine-tune by editing
storage/app/dbmind1/catalog.jsonanytime.
🧭 How it works
- Write the SQL — the model sees a sanitised schema (approved tables, real
foreign keys, indexed columns) and writes exactly one
SELECT, JOINing along the foreign keys it was given. - Guard & execute —
SqlGuardrejects anything that isn't a single read-onlySELECTon approved tables with no denied columns and an enforcedLIMIT. The query runs on a read-only connection under a database-side statement timeout; failures feed a bounded repair loop. - Answer from real rows — the result rows (capped and sanitised) go back to
the model, which phrases a grounded answer.
--show-sqlprints the exact query so every answer is auditable.
Two ways to run it
cloud (recommended) |
local |
|
|---|---|---|
| Setup | paste the API key from your DBMind dashboard | run Ollama + pull a model |
| Who reasons | the hosted DBMind gateway (/v1/sql/*) |
your own Ollama server |
| What leaves the server | question + schema metadata + the query's result rows (capped + sanitised) | nothing |
| Who touches your DB | only your server — SQL is guarded and executed locally in both modes | same |
| Billing / quotas | metered per token on your DBMind plan | free |
In both modes every generated query passes the local SqlGuard and runs on
your local read-only connection. The cloud never connects to your database.
How it differs from dbmind/laravel
dbmind/laravel |
dbmind/laravel-sql (this) |
|
|---|---|---|
| Namespace | DBMind\ |
DBMind1\ |
| Config / commands | dbmind, dbmind:* |
dbmind1, dbmind1:* |
| Answering | vector RAG + plan engine + raw SQL | raw SQL only |
| Joins across tables | ✗ (plan engine) | ✓ (follows real foreign keys) |
| Clarifying questions | ✗ | ✓ (interactive) |
| Qdrant / embeddings | required | none |
| Table approval | per-table wizard | exclusion list ("don't answer from X") |
Both packages use different namespaces, config keys, and command prefixes, so they can be installed side by side.
🛠 Commands
| Command | Purpose |
|---|---|
dbmind1:install |
Interactive setup: provider + table exclusions + auto-config |
dbmind1:doctor |
Health check: DB read access + the AI gateway |
dbmind1:discover |
Read-only listing of connections, tables, columns |
dbmind1:sync [--dry-run] |
After migrations: auto-approve new tables, refresh metadata |
dbmind1:test "question" |
Ask from the CLI |
dbmind1:test flags
--show-sql— print the SQL that was executed--think— prefix anApproach:line stating the metric/assumptions--no-clarify— never pause for clarification; answer with best assumptions--rounds=N— max clarifying rounds (default 3)
💻 Programmatic use
use DBMind1\Laravel\Facades\DBMind1;
use DBMind1\Domain\DTO\AskRequest;
$response = DBMind1::ask(new AskRequest('top 3 customers by spend', allowClarify: false));
if ($response->isClarification()) {
// $response->clarifyingQuestions() — collect answers, then:
// DBMind1::ask($request->withClarification($q, $a));
}
$response->answer; // the grounded answer
$response->metadata['sql']; // the read-only SELECT that ran
🛡 Security
- Read-only.
SqlGuardallows exactly oneSELECT— no DML/DDL, no stacked statements, no comments — and always enforces aLIMIT. It runs through the connection's read PDO; for defence in depth pointDBMIND1_SQL_CONNECTIONat a user with onlySELECTgrants. - Approved tables only. Every
FROM/JOINtarget must be an approved source. - Denied columns blocked, three ways.
password,token,secret,api_key, … are stripped from the schema the model sees, rejected bySqlGuardif they appear in a query, and stripped from result rows before they reach the model.SELECT *is rejected outright so unlisted columns can never leak through a star-select. - Statement timeout. Every generated query runs under a database-side
timeout (
DBMIND1_SQL_TIMEOUT_MS, default 5 s) — a heavy query is killed by the server and the error feeds the model's repair loop. - Fast by construction. The model is given the real foreign keys and indexed columns and instructed to join/filter along them only.
[!IMPORTANT] This edition is not tenant-scoped (raw SQL can't be safely tenant-filtered). For multi-tenant isolation use the full
dbmind/laravelplan engine.
⚙️ Configuration
Publish with php artisan vendor:publish --tag=dbmind1-config. Key env vars:
DBMIND1_PROVIDER=cloud # cloud | local
# cloud
DBMIND1_API_URL=https://dbmind.online
DBMIND1_API_KEY=sk_live_… # from your DBMind dashboard
DBMIND1_PROJECT_ID=project_…
# local
DBMIND1_OLLAMA_URL=http://localhost:11434
DBMIND1_CHAT_MODEL=qwen2.5-coder:7b
# both
DBMIND1_SQL_CONNECTION= # optional read-only connection name
DBMIND1_SQL_MAX_ROWS=200
DBMIND1_SQL_MAX_ATTEMPTS=3 # query-repair retries
DBMIND1_SQL_TIMEOUT_MS=5000 # DB-side statement timeout
DBMIND1_CLARIFY=true
DBMIND1_THINK=false
🏛 Architecture
src/
Domain/ Contracts, DTOs, Exceptions (pure PHP, no framework)
Application/ Use cases: Asking, Catalog (auto-config), Discovery
Infrastructure/ Adapters: Ollama gateway, Cloud SQL gateway, read-only executor, HTTP
Security/ SqlGuard, ColumnDenyList, SourcePolicy
Support/ SchemaSerializer, Prompts, TokenEstimator
Laravel/ ServiceProvider, Facade, Console (the only Laravel-coupled layer)
Application depends only on Domain contracts; Infrastructure implements
those contracts; Laravel/DBMind1ServiceProvider is the composition root that
wires them together. Data crosses boundaries as final readonly DTOs — never
arrays or Eloquent models.
🧪 Testing
composer test # Pest unit tests (framework-free: SqlGuard, AskService, auto-config, cloud gateway)
composer lint # Laravel Pint
📄 License
MIT — see LICENSE.