Compare commits

...
1 Commits
Author SHA1 Message Date
Wojtek 37d8c6040c Show remaining API credits in AI settings 2026-10-16 15:12:33 +02:00
6 changed files with 111 additions and 10 deletions
+1 -1
View File
@@ -30,6 +30,6 @@ Szkielet komponentu Joomla 6 do gromadzenia pytań testowych.
## Instalacja ## Instalacja
W Joomla wybierz **System → Install → Install from Folder** i wskaż ten katalog. Manifestem instalacyjnym jest `com_testquestions.xml` (wersja 1.43.6). W Joomla wybierz **System → Install → Install from Folder** i wskaż ten katalog. Manifestem instalacyjnym jest `com_testquestions.xml` (wersja 1.43.7).
Po instalacji komponent jest dostępny w panelu administratora jako **Pytania testowe**. Po instalacji komponent jest dostępny w panelu administratora jako **Pytania testowe**.
@@ -70,6 +70,18 @@ COM_TESTQUESTIONS_AI_GROK_KEY="Klucz API Grok"
COM_TESTQUESTIONS_AI_OPENAI_MODEL="Model OpenAI" COM_TESTQUESTIONS_AI_OPENAI_MODEL="Model OpenAI"
COM_TESTQUESTIONS_AI_GROK_MODEL="Model Grok" COM_TESTQUESTIONS_AI_GROK_MODEL="Model Grok"
COM_TESTQUESTIONS_AI_MODEL_INVALID="Wybierz prawidłowy model dla każdego dostawcy AI." COM_TESTQUESTIONS_AI_MODEL_INVALID="Wybierz prawidłowy model dla każdego dostawcy AI."
COM_TESTQUESTIONS_AI_BALANCE="Saldo kont API"
COM_TESTQUESTIONS_AI_OPENAI_BALANCE_INFO="OpenAI nie udostępnia salda przedpłaconych środków przez zwykłe API. Saldo sprawdzisz w panelu rozliczeń: "
COM_TESTQUESTIONS_AI_OPENAI_BILLING_LINK="Otwórz rozliczenia OpenAI"
COM_TESTQUESTIONS_AI_GROK_TEAM_ID="ID zespołu xAI"
COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY="Klucz zarządzający xAI"
COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY_SAVED="Klucz zarządzający jest zapisany. Pozostaw pole bez zmian, aby go zachować."
COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY_INFO="Wymagany jest osobny Management API key z xAI Console → Settings → Management Keys."
COM_TESTQUESTIONS_AI_CHECK_GROK_BALANCE="Sprawdź saldo xAI"
COM_TESTQUESTIONS_AI_GROK_BALANCE_INFO="Odczyt dotyczy dostępnych kredytów przedpłaconych zespołu xAI. Nie obejmuje limitu rozliczeń postpaid."
COM_TESTQUESTIONS_AI_GROK_BALANCE_CONFIG_REQUIRED="Aby sprawdzić saldo xAI, wpisz ID zespołu i klucz zarządzający, a następnie ponów próbę."
COM_TESTQUESTIONS_AI_GROK_BALANCE_FAILED="Nie udało się odczytać salda xAI. Sprawdź ID zespołu, klucz zarządzający i jego uprawnienia."
COM_TESTQUESTIONS_AI_GROK_BALANCE_RESULT="Dostępne kredyty przedpłacone xAI: $%s USD"
COM_TESTQUESTIONS_AI_SETTINGS_MENU="Konfiguracja AI" COM_TESTQUESTIONS_AI_SETTINGS_MENU="Konfiguracja AI"
COM_TESTQUESTIONS_AI_SETTINGS_SAVED="Ustawienia AI zapisano." COM_TESTQUESTIONS_AI_SETTINGS_SAVED="Ustawienia AI zapisano."
COM_TESTQUESTIONS_AI_SAVE_FAILED="Nie udało się zapisać ustawień AI." COM_TESTQUESTIONS_AI_SAVE_FAILED="Nie udało się zapisać ustawień AI."
@@ -0,0 +1 @@
-- No database changes required for version 1.43.7.
@@ -5,6 +5,7 @@ namespace Komponent\Component\Testquestions\Administrator\Controller;
defined('_JEXEC') or die; defined('_JEXEC') or die;
use Joomla\CMS\Factory; use Joomla\CMS\Factory;
use Joomla\CMS\Http\HttpFactory;
use Joomla\CMS\Language\Text; use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController; use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Router\Route; use Joomla\CMS\Router\Route;
@@ -14,6 +15,65 @@ use Komponent\Component\Testquestions\Administrator\Service\AiService;
class AiController extends BaseController class AiController extends BaseController
{ {
public function save(): void public function save(): void
{
$this->saveSettings();
$this->setRedirect(
Route::_('index.php?option=com_testquestions&view=ai', false),
Text::_('COM_TESTQUESTIONS_AI_SETTINGS_SAVED')
);
}
public function checkbalance(): void
{
$settings = $this->saveSettings();
$teamId = trim((string) ($settings['ai_grok_team_id'] ?? ''));
$managementKey = trim((string) ($settings['ai_grok_management_key'] ?? ''));
if ($teamId === '' || $managementKey === '') {
$this->setRedirect(
Route::_('index.php?option=com_testquestions&view=ai', false),
Text::_('COM_TESTQUESTIONS_AI_GROK_BALANCE_CONFIG_REQUIRED'),
'warning'
);
return;
}
try {
$response = HttpFactory::getHttp()->get(
'https://management-api.x.ai/v1/billing/teams/' . rawurlencode($teamId) . '/prepaid/balance',
['Authorization' => 'Bearer ' . $managementKey, 'Accept' => 'application/json'],
30
);
} catch (\Throwable $error) {
$this->setRedirect(
Route::_('index.php?option=com_testquestions&view=ai', false),
Text::_('COM_TESTQUESTIONS_AI_GROK_BALANCE_FAILED'),
'error'
);
return;
}
$body = json_decode((string) $response->body, true);
if ((int) $response->code < 200 || (int) $response->code >= 300 || !isset($body['total']['val']) || !is_numeric($body['total']['val'])) {
$this->setRedirect(
Route::_('index.php?option=com_testquestions&view=ai', false),
Text::_('COM_TESTQUESTIONS_AI_GROK_BALANCE_FAILED'),
'error'
);
return;
}
$balance = abs((float) $body['total']['val']) / 100;
$message = Text::sprintf('COM_TESTQUESTIONS_AI_GROK_BALANCE_RESULT', number_format($balance, 2, ',', ' '));
$this->setRedirect(
Route::_('index.php?option=com_testquestions&view=ai', false),
$message,
'message'
);
}
private function saveSettings(): array
{ {
if (!Session::checkToken()) { if (!Session::checkToken()) {
throw new \RuntimeException(Text::_('JINVALID_TOKEN'), 403); throw new \RuntimeException(Text::_('JINVALID_TOKEN'), 403);
@@ -56,38 +116,41 @@ class AiController extends BaseController
$settings = json_decode((string) $row->params, true); $settings = json_decode((string) $row->params, true);
$settings = is_array($settings) ? $settings : []; $settings = is_array($settings) ? $settings : [];
$legacyKey = trim((string) ($settings['ai_api_key'] ?? '')); $legacyKey = trim((string) ($settings['ai_api_key'] ?? ''));
$settings['ai_provider'] = $provider; $settings['ai_provider'] = $provider;
foreach ($selectedModels as $modelProvider => $model) { foreach ($selectedModels as $modelProvider => $model) {
$settings['ai_' . $modelProvider . '_model'] = $model; $settings['ai_' . $modelProvider . '_model'] = $model;
} }
foreach (['openai', 'grok'] as $keyProvider) { foreach (['openai', 'grok'] as $keyProvider) {
$fieldName = 'ai_' . $keyProvider . '_api_key'; $fieldName = 'ai_' . $keyProvider . '_api_key';
$apiKey = trim((string) $input->post->get($fieldName, '', 'RAW')); $apiKey = trim((string) $input->post->get($fieldName, '', 'RAW'));
if ($apiKey !== '') { if ($apiKey !== '') {
$settings[$fieldName] = $apiKey; $settings[$fieldName] = $apiKey;
} }
if ($keyProvider === $provider && empty($settings[$fieldName]) && $legacyKey !== '') { if ($keyProvider === $provider && empty($settings[$fieldName]) && $legacyKey !== '') {
$settings[$fieldName] = $legacyKey; $settings[$fieldName] = $legacyKey;
} }
} }
$teamId = trim((string) $input->post->get('ai_grok_team_id', '', 'STRING'));
if ($teamId !== '') {
$settings['ai_grok_team_id'] = $teamId;
}
$managementKey = trim((string) $input->post->get('ai_grok_management_key', '', 'RAW'));
if ($managementKey !== '') {
$settings['ai_grok_management_key'] = $managementKey;
}
unset($settings['ai_api_key']); unset($settings['ai_api_key']);
$extension = (object) [ $extension = (object) [
'extension_id' => (int) $row->extension_id, 'extension_id' => (int) $row->extension_id,
'params' => json_encode($settings, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), 'params' => json_encode($settings, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
]; ];
if (!$db->updateObject('#__extensions', $extension, 'extension_id')) { if (!$db->updateObject('#__extensions', $extension, 'extension_id')) {
throw new \RuntimeException(Text::_('COM_TESTQUESTIONS_AI_SAVE_FAILED')); throw new \RuntimeException(Text::_('COM_TESTQUESTIONS_AI_SAVE_FAILED'));
} }
$this->setRedirect( return $settings;
Route::_('index.php?option=com_testquestions&view=ai', false),
Text::_('COM_TESTQUESTIONS_AI_SETTINGS_SAVED')
);
} }
public function cancel(): void public function cancel(): void
@@ -54,6 +54,31 @@
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<hr>
<h3><?php echo \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_BALANCE'); ?></h3>
<div class="alert alert-info">
<?php echo \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_OPENAI_BALANCE_INFO'); ?>
<a href="https://platform.openai.com/settings/organization/billing/overview" target="_blank" rel="noopener noreferrer"><?php echo \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_OPENAI_BILLING_LINK'); ?></a>
</div>
<div class="mb-3">
<label for="ai-grok-team-id" class="form-label"><?php echo \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_GROK_TEAM_ID'); ?></label>
<input type="text" id="ai-grok-team-id" name="ai_grok_team_id" class="form-control" maxlength="255" value="<?php echo htmlspecialchars((string) ($this->settings['ai_grok_team_id'] ?? ''), ENT_QUOTES, 'UTF-8'); ?>">
</div>
<div class="mb-3">
<label for="ai-grok-management-key" class="form-label"><?php echo \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY'); ?></label>
<div class="input-group">
<input type="password" id="ai-grok-management-key" name="ai_grok_management_key" value="<?php echo htmlspecialchars((string) ($this->settings['ai_grok_management_key'] ?? ''), ENT_QUOTES, 'UTF-8'); ?>" class="form-control" maxlength="4096" autocomplete="new-password">
<button type="button" class="btn btn-outline-secondary" data-ai-key-toggle="ai-grok-management-key">Pokaż</button>
</div>
<small class="form-text text-muted">
<?php echo !empty($this->settings['ai_grok_management_key']) ? \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY_SAVED') : \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY_INFO'); ?>
</small>
</div>
<button type="submit" class="btn btn-outline-primary" onclick="this.form.task.value='ai.checkbalance';">
<?php echo \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_CHECK_GROK_BALANCE'); ?>
</button>
<div class="form-text mt-2"><?php echo \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_GROK_BALANCE_INFO'); ?></div>
<hr>
<button type="submit" class="btn btn-primary" onclick="this.form.task.value='ai.save';"> <button type="submit" class="btn btn-primary" onclick="this.form.task.value='ai.save';">
<?php echo \Joomla\CMS\Language\Text::_('JTOOLBAR_SAVE'); ?> <?php echo \Joomla\CMS\Language\Text::_('JTOOLBAR_SAVE'); ?>
</button> </button>
+1 -1
View File
@@ -4,7 +4,7 @@
<creationDate>2026-10-14</creationDate> <creationDate>2026-10-14</creationDate>
<author>Komponent</author> <author>Komponent</author>
<license>GNU General Public License version 2 or later</license> <license>GNU General Public License version 2 or later</license>
<version>1.43.6</version> <version>1.43.7</version>
<description>COM_TESTQUESTIONS_DESCRIPTION</description> <description>COM_TESTQUESTIONS_DESCRIPTION</description>
<element>com_testquestions</element> <element>com_testquestions</element>
<namespace path="src">Komponent\Component\Testquestions</namespace> <namespace path="src">Komponent\Component\Testquestions</namespace>