Files
autocrm/tests/Feature/AiModelSelectorTest.php
Vasyka 0e3f9e8bca feat: AI model selector + i18n nav labels (RU/EN) on new modules
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>
2026-06-03 06:23:21 +00:00

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;
}
}