diff --git a/README.md b/README.md index dce16fe..8011a23 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,6 @@ Szkielet komponentu Joomla 6 do gromadzenia pytań testowych. ## Instalacja -W Joomla wybierz **System → Install → Install from Folder** i wskaż ten katalog. Manifestem instalacyjnym jest `com_testquestions.xml` (wersja 1.43.3). +W Joomla wybierz **System → Install → Install from Folder** i wskaż ten katalog. Manifestem instalacyjnym jest `com_testquestions.xml` (wersja 1.43.4). Po instalacji komponent jest dostępny w panelu administratora jako **Pytania testowe**. diff --git a/administrator/components/com_testquestions/sql/updates/mysql/1.43.4.sql b/administrator/components/com_testquestions/sql/updates/mysql/1.43.4.sql new file mode 100644 index 0000000..9d56018 --- /dev/null +++ b/administrator/components/com_testquestions/sql/updates/mysql/1.43.4.sql @@ -0,0 +1 @@ +-- No database changes required for version 1.43.4. diff --git a/administrator/components/com_testquestions/src/Model/ResultModel.php b/administrator/components/com_testquestions/src/Model/ResultModel.php index ba4abbe..0989532 100644 --- a/administrator/components/com_testquestions/src/Model/ResultModel.php +++ b/administrator/components/com_testquestions/src/Model/ResultModel.php @@ -151,6 +151,9 @@ class ResultModel extends BaseDatabaseModel ); $item->answers[$questionId]['ai_feedback'] = $aiResult['feedback']; $item->answers[$questionId]['awarded_points'] = $aiResult['awarded_points']; + if ($aiResult['usage'] !== null) { + $item->answers[$questionId]['ai_usage'] = $aiResult['usage']; + } $correctionPoints = 0; foreach ($item->answers as $savedAnswer) { diff --git a/administrator/components/com_testquestions/src/Service/AiService.php b/administrator/components/com_testquestions/src/Service/AiService.php index b5ff1ec..0be68c1 100644 --- a/administrator/components/com_testquestions/src/Service/AiService.php +++ b/administrator/components/com_testquestions/src/Service/AiService.php @@ -73,9 +73,46 @@ class AiService throw new \RuntimeException('AI zwróciło nieprawidłowy format oceny i punktów.'); } + $usage = $this->getUsage($body, $provider, $config['model']); + return [ 'awarded_points' => min(max(0, $maxPoints), max(0, (int) $result['awarded_points'])), 'feedback' => trim((string) ($result['feedback'] ?? '')), + 'usage' => $usage, + ]; + } + + private function getUsage(array $body, string $provider, string $model): ?array + { + $usage = $body['usage'] ?? null; + if (!is_array($usage) || !isset($usage['input_tokens'], $usage['output_tokens'])) { + return null; + } + + $inputTokens = max(0, (int) $usage['input_tokens']); + $outputTokens = max(0, (int) $usage['output_tokens']); + $cachedTokens = min($inputTokens, max(0, (int) ($usage['input_tokens_details']['cached_tokens'] ?? 0))); + $cost = null; + $costSource = ''; + + if ($provider === 'grok' && isset($usage['cost_in_usd_ticks']) && is_numeric($usage['cost_in_usd_ticks'])) { + $cost = (float) $usage['cost_in_usd_ticks'] / 10000000000; + $costSource = 'provider'; + } elseif ($provider === 'openai' && $model === 'gpt-4.1-mini') { + $uncachedTokens = $inputTokens - $cachedTokens; + $cost = ($uncachedTokens * 0.40 + $cachedTokens * 0.10 + $outputTokens * 1.60) / 1000000; + $costSource = 'token_rates'; + } + + return [ + 'provider' => $provider, + 'model' => $model, + 'input_tokens' => $inputTokens, + 'output_tokens' => $outputTokens, + 'total_tokens' => (int) ($usage['total_tokens'] ?? ($inputTokens + $outputTokens)), + 'cached_input_tokens' => $cachedTokens, + 'cost_usd' => $cost, + 'cost_source' => $costSource, ]; } diff --git a/administrator/components/com_testquestions/tmpl/result/default.php b/administrator/components/com_testquestions/tmpl/result/default.php index dad1245..b7a061e 100644 --- a/administrator/components/com_testquestions/tmpl/result/default.php +++ b/administrator/components/com_testquestions/tmpl/result/default.php @@ -35,6 +35,25 @@ + 0, 'output_tokens' => 0, 'total_tokens' => 0, 'cost_usd' => 0.0, 'providers' => []]; + foreach ($this->item->answers as $savedAnswer) { + $usage = $savedAnswer['ai_usage'] ?? null; + if (!is_array($usage)) continue; + $usageTotals['input_tokens'] += (int) ($usage['input_tokens'] ?? 0); + $usageTotals['output_tokens'] += (int) ($usage['output_tokens'] ?? 0); + $usageTotals['total_tokens'] += (int) ($usage['total_tokens'] ?? 0); + if (is_numeric($usage['cost_usd'] ?? null)) $usageTotals['cost_usd'] += (float) $usage['cost_usd']; + if (!empty($usage['provider'])) $usageTotals['providers'][(string) $usage['provider']] = true; + } + ?> + 0) : ?> + + ✦ : + tokenów, + USD + +
@@ -55,6 +74,17 @@ + + + + + · + in / + out · + + + +