knobik/sql-agent
Self-learning text-to-SQL agent for Laravel that turns natural language into accurate, safe SQL. Uses schema introspection, a curated knowledge base, query patterns, and conversation memory; recovers from errors and saves learnings. Includes built-in chat UI.
Use the SqlAgent facade to ask questions and receive structured responses:
use Knobik\SqlAgent\Facades\SqlAgent;
$response = SqlAgent::run('How many users registered last week?');
$response->answer; // "There are 42 users who registered last week."
$response->sql; // "SELECT COUNT(*) as count FROM users WHERE ..."
$response->results; // [['count' => 42]]
$response->toolCalls; // All tool calls made during execution
$response->iterations; // Detailed iteration data
$response->error; // Error message if failed, null otherwise
$response->usage; // Token usage data (see Token Usage Tracking below)
$response->isSuccess(); // true if no error occurred
$response->hasResults(); // true if results is not empty
For real-time output, use the stream method which returns a generator of chunks:
use Knobik\SqlAgent\Facades\SqlAgent;
foreach (SqlAgent::stream('Show me the top 5 customers') as $chunk) {
echo $chunk->content;
if ($chunk->isComplete()) {
// Stream finished
}
}
The stream method accepts the same parameters as run, plus conversation history:
SqlAgent::stream(
string $question,
?string $connection = null,
array $history = [],
): Generator
Query a specific database connection by passing it as the second argument:
$response = SqlAgent::run('How many orders today?', 'analytics');
For multi-turn conversations, pass previous messages as history:
$history = [
['role' => 'user', 'content' => 'Show me all products'],
['role' => 'assistant', 'content' => 'Here are the products...'],
];
foreach (SqlAgent::stream('Now filter by price > 100', null, $history) as $chunk) {
echo $chunk->content;
}
You may also resolve the agent via dependency injection using the Agent contract:
use Knobik\SqlAgent\Contracts\Agent;
class ReportController extends Controller
{
public function __construct(
private Agent $agent,
) {}
public function generate(Request $request)
{
$response = $this->agent->run($request->input('question'));
return [
'answer' => $response->answer,
'sql' => $response->sql,
'data' => $response->results,
];
}
}
Every response from the agent includes token usage data from the LLM provider. This is useful for monitoring costs, debugging prompt sizes, and observability.
The usage property on AgentResponse contains an array of token counts:
$response = SqlAgent::run('How many users are there?');
$response->usage;
// [
// 'prompt_tokens' => 1234,
// 'completion_tokens' => 567,
// 'cache_write_input_tokens' => null,
// 'cache_read_input_tokens' => null,
// 'thought_tokens' => null,
// ]
| Field | Description |
|---|---|
prompt_tokens |
Number of tokens in the input prompt |
completion_tokens |
Number of tokens generated by the LLM |
cache_write_input_tokens |
Tokens written to prompt cache (provider-dependent) |
cache_read_input_tokens |
Tokens read from prompt cache (provider-dependent) |
thought_tokens |
Tokens used for chain-of-thought reasoning (provider-dependent) |
Cache and thought token fields are null when the provider does not support them.
When using the web interface or the SSE streaming endpoint, usage data is included in the done event:
{"event": "done", "data": {"queryCount": 2, "usage": {"prompt_tokens": 1234, "completion_tokens": 567, ...}}}
Usage data is automatically persisted in the message metadata column for every assistant response. You can access it via the usage accessor on the Message model:
$message = Message::find(1);
$message->usage; // ['prompt_tokens' => 1234, 'completion_tokens' => 567, ...]
How can I help you explore Laravel packages today?