0e3f9e8bca
AI model selector:
- AiAssistantService::MODEL_DEFAULTS and MODEL_OPTIONS const tables (3 picks per
provider: Claude Opus 4.7 / Sonnet 4.6 / Haiku 4.5, OpenAI 4o / 4o-mini,
Gemini 1.5 Pro / Flash). Default upgraded from Sonnet 4.5 → Sonnet 4.6.
- modelFor(provider, company?) resolves tenant override > global default.
- All 8 hardcoded model strings replaced with modelFor() across callClaude
(chat with tool-use), callOpenAI, callGemini (chat), postClaude/postOpenAI/
postGemini (single-shot), and OcrInvoiceService.
- Settings page adds 3 model selectors per provider with persistence at
settings.ai.models.{claude,gpt,gemini}.
i18n nav labels:
- TireSet / Bodyshop / Subcontractor / SubcontractJob / PricingCoefficient /
ShopCustomer resources: getNavigationLabel / getNavigationGroup /
getModelLabel / getPluralModelLabel return __()-wrapped strings.
- 20 keys added to lang/ru.json and lang/en.json.
Tests (4 new): default model, tenant override wins, unknown provider falls
back to claude default, options dictionary contains each default key.
Full suite: 134 passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
74 lines
2.5 KiB
PHP
74 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Central\Company;
|
|
use App\Models\Central\Plan;
|
|
use App\Services\Ai\AiAssistantService;
|
|
use App\Tenancy\TenantManager;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class AiModelSelectorTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_default_model_when_no_override(): void
|
|
{
|
|
$svc = app(AiAssistantService::class);
|
|
$company = $this->makeCompany(['ai' => ['claude_key' => 'sk']]);
|
|
|
|
$this->assertEquals('claude-sonnet-4-6', $svc->modelFor('claude', $company));
|
|
$this->assertEquals('gpt-4o-mini', $svc->modelFor('gpt', $company));
|
|
$this->assertEquals('gemini-1.5-flash', $svc->modelFor('gemini', $company));
|
|
}
|
|
|
|
public function test_tenant_override_wins(): void
|
|
{
|
|
$svc = app(AiAssistantService::class);
|
|
$company = $this->makeCompany([
|
|
'ai' => [
|
|
'claude_key' => 'sk',
|
|
'models' => [
|
|
'claude' => 'claude-opus-4-7',
|
|
'gpt' => 'gpt-4o',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$this->assertEquals('claude-opus-4-7', $svc->modelFor('claude', $company));
|
|
$this->assertEquals('gpt-4o', $svc->modelFor('gpt', $company));
|
|
// Provider without override → default.
|
|
$this->assertEquals('gemini-1.5-flash', $svc->modelFor('gemini', $company));
|
|
}
|
|
|
|
public function test_unknown_provider_falls_back(): void
|
|
{
|
|
$svc = app(AiAssistantService::class);
|
|
$company = $this->makeCompany([]);
|
|
$this->assertEquals('claude-sonnet-4-6', $svc->modelFor('xyz', $company));
|
|
}
|
|
|
|
public function test_options_dictionary_keys_match_defaults(): void
|
|
{
|
|
foreach (AiAssistantService::MODEL_DEFAULTS as $provider => $default) {
|
|
$this->assertArrayHasKey(
|
|
$default,
|
|
AiAssistantService::MODEL_OPTIONS[$provider],
|
|
"default '$default' for $provider is not in MODEL_OPTIONS"
|
|
);
|
|
}
|
|
}
|
|
|
|
private function makeCompany(array $settings): Company
|
|
{
|
|
$plan = Plan::firstOrCreate(['slug' => 'test'], ['name' => 'T', 'price' => 0, 'features' => []]);
|
|
$company = Company::create([
|
|
'plan_id' => $plan->id, 'slug' => 'mdl-' . uniqid(),
|
|
'name' => 'Model Co', 'status' => 'active', 'settings' => $settings,
|
|
]);
|
|
app(TenantManager::class)->setCurrent($company);
|
|
return $company;
|
|
}
|
|
}
|