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>
This commit is contained in:
@@ -63,6 +63,9 @@ class Settings extends Page
|
||||
'ai_claude_key' => $settings['ai']['claude_key'] ?? null,
|
||||
'ai_gpt_key' => $settings['ai']['gpt_key'] ?? null,
|
||||
'ai_gemini_key' => $settings['ai']['gemini_key'] ?? null,
|
||||
'ai_model_claude' => data_get($settings, 'ai.models.claude', \App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['claude']),
|
||||
'ai_model_gpt' => data_get($settings, 'ai.models.gpt', \App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['gpt']),
|
||||
'ai_model_gemini' => data_get($settings, 'ai.models.gemini', \App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['gemini']),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -193,8 +196,20 @@ class Settings extends Page
|
||||
->options(['claude' => 'Claude (Anthropic)', 'gpt' => 'ChatGPT (OpenAI)', 'gemini' => 'Gemini (Google)'])
|
||||
->default('claude'),
|
||||
Forms\Components\TextInput::make('ai_claude_key')->label('Claude API Key')->password()->revealable()->placeholder('sk-ant-...'),
|
||||
Forms\Components\Select::make('ai_model_claude')
|
||||
->label('Model Claude')
|
||||
->options(\App\Services\Ai\AiAssistantService::MODEL_OPTIONS['claude'])
|
||||
->default(\App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['claude']),
|
||||
Forms\Components\TextInput::make('ai_gpt_key')->label('OpenAI API Key')->password()->revealable()->placeholder('sk-proj-...'),
|
||||
Forms\Components\Select::make('ai_model_gpt')
|
||||
->label('Model OpenAI')
|
||||
->options(\App\Services\Ai\AiAssistantService::MODEL_OPTIONS['gpt'])
|
||||
->default(\App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['gpt']),
|
||||
Forms\Components\TextInput::make('ai_gemini_key')->label('Gemini API Key')->password()->revealable(),
|
||||
Forms\Components\Select::make('ai_model_gemini')
|
||||
->label('Model Gemini')
|
||||
->options(\App\Services\Ai\AiAssistantService::MODEL_OPTIONS['gemini'])
|
||||
->default(\App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['gemini']),
|
||||
]),
|
||||
])
|
||||
->statePath('data');
|
||||
@@ -246,6 +261,11 @@ class Settings extends Page
|
||||
'claude_key' => $data['ai_claude_key'] ?? null,
|
||||
'gpt_key' => $data['ai_gpt_key'] ?? null,
|
||||
'gemini_key' => $data['ai_gemini_key'] ?? null,
|
||||
'models' => [
|
||||
'claude' => $data['ai_model_claude'] ?? \App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['claude'],
|
||||
'gpt' => $data['ai_model_gpt'] ?? \App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['gpt'],
|
||||
'gemini' => $data['ai_model_gemini'] ?? \App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['gemini'],
|
||||
],
|
||||
],
|
||||
]),
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user