Replace AI balance with usage counters

This commit is contained in:
Wojtek
2026-10-16 19:36:42 +02:00
parent 37d8c6040c
commit 8693720b78
8 changed files with 101 additions and 94 deletions
@@ -5,7 +5,6 @@ namespace Komponent\Component\Testquestions\Administrator\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Http\HttpFactory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Router\Route;
@@ -24,55 +23,6 @@ class AiController extends BaseController
);
}
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()) {
@@ -132,15 +82,8 @@ class AiController extends BaseController
}
}
$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_grok_team_id'], $settings['ai_grok_management_key']);
$extension = (object) [
'extension_id' => (int) $row->extension_id,
@@ -161,6 +161,14 @@ class ResultModel extends BaseDatabaseModel
$item->answers[$questionId]['ai_feedback'] = $aiResult['feedback'];
$item->answers[$questionId]['awarded_points'] = $aiResult['awarded_points'];
if ($aiResult['usage'] !== null) {
$history = $item->answers[$questionId]['ai_usage_history'] ?? null;
if (!is_array($history)) {
$history = !empty($item->answers[$questionId]['ai_usage']) && is_array($item->answers[$questionId]['ai_usage'])
? [$item->answers[$questionId]['ai_usage']]
: [];
}
$history[] = $aiResult['usage'];
$item->answers[$questionId]['ai_usage_history'] = $history;
$item->answers[$questionId]['ai_usage'] = $aiResult['usage'];
}
@@ -12,6 +12,7 @@ use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
protected array $settings = [];
protected array $usageTotals = [];
public function display($tpl = null)
{
@@ -24,6 +25,54 @@ class HtmlView extends BaseHtmlView
->where($db->quoteName('element') . ' = ' . $db->quote('com_testquestions'))
)->loadResult();
$this->settings = json_decode($raw, true) ?: [];
$this->usageTotals = [
'openai' => ['calls' => 0, 'input_tokens' => 0, 'output_tokens' => 0, 'total_tokens' => 0, 'cost_usd' => 0.0, 'cost_calls' => 0],
'grok' => ['calls' => 0, 'input_tokens' => 0, 'output_tokens' => 0, 'total_tokens' => 0, 'cost_usd' => 0.0, 'cost_calls' => 0],
];
$usageRows = $db->setQuery(
$db->getQuery(true)
->select($db->quoteName('answers'))
->from($db->quoteName('#__testquestions_results'))
->where($db->quoteName('answers') . ' LIKE ' . $db->quote('%ai_usage%'))
)->loadColumn();
foreach ($usageRows as $answersJson) {
$answers = json_decode((string) $answersJson, true);
if (!is_array($answers)) {
continue;
}
foreach ($answers as $answer) {
if (!is_array($answer)) {
continue;
}
$usages = $answer['ai_usage_history'] ?? (isset($answer['ai_usage']) ? [$answer['ai_usage']] : []);
if (!is_array($usages)) {
continue;
}
foreach ($usages as $usage) {
if (!is_array($usage)) {
continue;
}
$provider = (string) ($usage['provider'] ?? '');
if (!isset($this->usageTotals[$provider])) {
continue;
}
$this->usageTotals[$provider]['calls']++;
$this->usageTotals[$provider]['input_tokens'] += (int) ($usage['input_tokens'] ?? 0);
$this->usageTotals[$provider]['output_tokens'] += (int) ($usage['output_tokens'] ?? 0);
$this->usageTotals[$provider]['total_tokens'] += (int) ($usage['total_tokens'] ?? 0);
if (is_numeric($usage['cost_usd'] ?? null)) {
$this->usageTotals[$provider]['cost_usd'] += (float) $usage['cost_usd'];
$this->usageTotals[$provider]['cost_calls']++;
}
}
}
}
ToolbarHelper::title(Text::_('COM_TESTQUESTIONS_AI_SETTINGS'));
ToolbarHelper::link('index.php?option=com_testquestions&view=questions', 'COM_TESTQUESTIONS_QUESTIONS', 'arrow-left');