mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-07-16 22:05:20 +00:00
Adds three new read-only tools the chat LLM can call to answer "who/what did the most X" questions that previously fell through the cracks: - rank_top_customers — ranks customers by invoiced_total, paid_total, invoice_count, or outstanding_balance over a named time period - rank_top_items — ranks catalog items by quantity_sold or revenue - rank_expense_categories — ranks expense categories by total spend All three share a new ResolvesPeriod trait that centralizes the period-name → [start, end] logic. GetCompanyStatsTool is refactored onto the same trait (identical public schema — the 'all_time' option is only exposed on the new ranking tools, where an unbounded window makes sense; stats over "all time" collapses every record into one giant bucket and is rarely useful). Each tool follows the existing pattern: snake_case name, one-sentence description tuned for LLM tool selection, JSON-schema parameters with injected company scoping (never trusting LLM-supplied company IDs), and JSON-encodable output. outstanding_balance on the customer tool explicitly ignores the period param since it's a current-state snapshot. Multi-company scoping tests lock down the session-authoritative boundary on every new tool. Per-metric ordering tests verify the aggregate queries actually rank correctly, and an ad-hoc-item exclusion test verifies rank_top_items skips invoice lines where item_id is null (free-typed entries that have no catalog row to rank by id). 15 new tests added (tests/Feature/Ai/Tools/); test suite grows from 398 to 413 passing. LLM tool count goes from 9 to 12 — the model will discover the new tools automatically via the function-calling schema with no prompt changes required.
57 lines
2.2 KiB
PHP
57 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Providers;
|
|
|
|
use App\Services\Ai\AiToolRegistry;
|
|
use App\Services\Ai\Tools\GetCompanyStatsTool;
|
|
use App\Services\Ai\Tools\GetCustomerTool;
|
|
use App\Services\Ai\Tools\GetInvoiceTool;
|
|
use App\Services\Ai\Tools\ListExpenseCategoriesTool;
|
|
use App\Services\Ai\Tools\ListOverdueInvoicesTool;
|
|
use App\Services\Ai\Tools\ListRecentPaymentsTool;
|
|
use App\Services\Ai\Tools\RankExpenseCategoriesTool;
|
|
use App\Services\Ai\Tools\RankTopCustomersTool;
|
|
use App\Services\Ai\Tools\RankTopItemsTool;
|
|
use App\Services\Ai\Tools\SearchCustomersTool;
|
|
use App\Services\Ai\Tools\SearchInvoicesTool;
|
|
use App\Services\Ai\Tools\SearchItemsTool;
|
|
use Illuminate\Contracts\Foundation\Application;
|
|
use Illuminate\Support\ServiceProvider;
|
|
|
|
/**
|
|
* Binds the AI tool registry as a singleton and registers every built-in
|
|
* read-only tool the chat assistant can call.
|
|
*
|
|
* Modules that ship additional tools should extend the registry from their
|
|
* own ServiceProvider::boot() by resolving AiToolRegistry from the container
|
|
* and calling register() on it.
|
|
*/
|
|
class AiServiceProvider extends ServiceProvider
|
|
{
|
|
public function register(): void
|
|
{
|
|
$this->app->singleton(AiToolRegistry::class, function (Application $app): AiToolRegistry {
|
|
$registry = new AiToolRegistry;
|
|
|
|
// Built-in read-only tools (order is presentation-only; the LLM picks).
|
|
$registry->register(new SearchInvoicesTool);
|
|
$registry->register(new GetInvoiceTool);
|
|
$registry->register(new ListOverdueInvoicesTool);
|
|
$registry->register(new SearchCustomersTool);
|
|
$registry->register(new GetCustomerTool);
|
|
$registry->register(new ListRecentPaymentsTool);
|
|
$registry->register(new SearchItemsTool);
|
|
$registry->register(new ListExpenseCategoriesTool);
|
|
$registry->register(new GetCompanyStatsTool);
|
|
|
|
// Ranking tools — group-by aggregates the individual-record
|
|
// tools above can't express.
|
|
$registry->register(new RankTopCustomersTool);
|
|
$registry->register(new RankTopItemsTool);
|
|
$registry->register(new RankExpenseCategoriesTool);
|
|
|
|
return $registry;
|
|
});
|
|
}
|
|
}
|