diff --git a/app/Services/RagSearchService.php b/app/Services/RagSearchService.php index f27dfefc..e25d49a9 100644 --- a/app/Services/RagSearchService.php +++ b/app/Services/RagSearchService.php @@ -4,6 +4,7 @@ use App\Helpers\AiTokenHelper; use App\Models\System\AiConfig; +use App\Models\System\AiPricingConfig; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; @@ -282,7 +283,21 @@ private function callGemini(AiConfig $config, string $query, string $context, ar $result = $response->json(); $answer = $result['candidates'][0]['content']['parts'][0]['text'] ?? ''; - // 토큰 사용량 저장 + // 토큰 사용량 계산 + $usage = $result['usageMetadata'] ?? []; + $promptTokens = $usage['promptTokenCount'] ?? 0; + $completionTokens = $usage['candidatesTokenCount'] ?? 0; + $totalTokens = $usage['totalTokenCount'] ?? 0; + + // 비용 계산 + $pricing = AiPricingConfig::getActivePricing('gemini'); + $inputPrice = $pricing ? (float) $pricing->input_price_per_million / 1_000_000 : 0.10 / 1_000_000; + $outputPrice = $pricing ? (float) $pricing->output_price_per_million / 1_000_000 : 0.40 / 1_000_000; + $costUsd = ($promptTokens * $inputPrice) + ($completionTokens * $outputPrice); + $exchangeRate = AiPricingConfig::getExchangeRate(); + $costKrw = $costUsd * $exchangeRate; + + // 토큰 사용량 DB 저장 AiTokenHelper::saveGeminiUsage($result, $result['modelVersion'] ?? $config->model, 'RAG검색'); // 참조 문서 목록 추출 @@ -297,6 +312,13 @@ private function callGemini(AiConfig $config, string $query, string $context, ar 'references' => $references, 'searched_count' => count($documents), 'model' => $config->model, + 'token_usage' => [ + 'prompt_tokens' => $promptTokens, + 'completion_tokens' => $completionTokens, + 'total_tokens' => $totalTokens, + 'cost_usd' => round($costUsd, 6), + 'cost_krw' => round($costKrw, 2), + ], ]; } catch (\Exception $e) { diff --git a/resources/views/additional/rag/index.blade.php b/resources/views/additional/rag/index.blade.php index c4e90065..dbe88ef6 100644 --- a/resources/views/additional/rag/index.blade.php +++ b/resources/views/additional/rag/index.blade.php @@ -65,6 +65,17 @@ .rag-answer-body blockquote { border-left: 3px solid #3b82f6; padding-left: 14px; margin: 12px 0; color: #64748b; } .rag-answer-body strong { font-weight: 600; color: #1e293b; } + /* 토큰 비용 바 */ + .rag-token-bar { display: flex; align-items: center; gap: 14px; background: #fffbeb; border: 1px solid #fde68a; border-radius: 8px; padding: 10px 16px; margin-bottom: 16px; flex-wrap: wrap; } + .rag-token-item { display: flex; align-items: center; gap: 5px; font-size: 0.75rem; color: #92400e; } + .rag-token-item .label { color: #a16207; } + .rag-token-item .value { font-weight: 600; } + .rag-token-sep { width: 1px; height: 14px; background: #fde68a; } + + /* 비용 안내 */ + .rag-cost-notice { background: #f0f9ff; border: 1px solid #bae6fd; border-radius: 8px; padding: 10px 16px; margin-bottom: 24px; display: flex; align-items: flex-start; gap: 8px; font-size: 0.78rem; color: #0369a1; line-height: 1.5; } + .rag-cost-notice svg { width: 16px; height: 16px; flex-shrink: 0; margin-top: 1px; } + /* 참조 문서 */ .rag-refs { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 10px; padding: 16px 20px; } .rag-refs-title { font-size: 0.78rem; font-weight: 600; color: #64748b; margin-bottom: 10px; display: flex; align-items: center; gap: 6px; } @@ -127,6 +138,12 @@ + {{-- 비용 안내 --}} +