refactor: remove Gemini integration and seed leakage paths
- Remove Gemini runtime fallback from OPD AI text generation and complaint drafting - Remove legacy Gemini scan extraction path - Update UI/provider labels to Azure/AI generic naming - Make AI usage guardrail metrics provider-neutral (ai_today) - Add seed export sanitization to exclude sensitive hospital_setting and audit entries - Remove Gemini API audit entry from master seed JSON
This commit is contained in:
parent
4adc6d79d9
commit
47d7db6d8b
|
|
@ -480,11 +480,11 @@ class Opd_prescription extends BaseController
|
|||
|
||||
$prompt = $this->buildClinicalPrompt($mode, $text);
|
||||
$this->lastAiProvider = 'none';
|
||||
$draft = $this->generateClinicalTextWithGemini($prompt);
|
||||
$draft = $this->generateClinicalTextWithAi($prompt);
|
||||
|
||||
if ($draft !== null && trim($draft) !== '') {
|
||||
$provider = $this->lastAiProvider === 'azure' ? 'azure' : 'gemini';
|
||||
$providerLabel = $provider === 'azure' ? 'Azure OpenAI' : 'Gemini';
|
||||
$provider = $this->lastAiProvider === 'azure' ? 'azure' : 'ai';
|
||||
$providerLabel = $provider === 'azure' ? 'Azure OpenAI' : 'AI';
|
||||
return $this->response->setJSON([
|
||||
'update' => 1,
|
||||
'draft_text' => trim($draft),
|
||||
|
|
@ -571,7 +571,7 @@ class Opd_prescription extends BaseController
|
|||
|
||||
$prompt = $this->buildFullClinicalDraftPrompt($input);
|
||||
$this->lastAiProvider = 'none';
|
||||
$draftText = $this->generateClinicalTextWithGemini($prompt);
|
||||
$draftText = $this->generateClinicalTextWithAi($prompt);
|
||||
$drafts = $this->extractFullDraftJson($draftText);
|
||||
|
||||
if ($drafts === null) {
|
||||
|
|
@ -579,8 +579,8 @@ class Opd_prescription extends BaseController
|
|||
$aiMode = 'fallback';
|
||||
$message = 'AI unavailable. Template draft prepared from entered data.';
|
||||
} else {
|
||||
$aiMode = $this->lastAiProvider === 'azure' ? 'azure' : 'gemini';
|
||||
$message = 'Full clinical draft prepared using ' . ($aiMode === 'azure' ? 'Azure OpenAI' : 'Gemini') . '.';
|
||||
$aiMode = $this->lastAiProvider === 'azure' ? 'azure' : 'ai';
|
||||
$message = 'Full clinical draft prepared using ' . ($aiMode === 'azure' ? 'Azure OpenAI' : 'AI') . '.';
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
|
|
@ -2208,7 +2208,7 @@ class Opd_prescription extends BaseController
|
|||
. "Use short plain text values. If unsure for a field, keep empty string.";
|
||||
|
||||
$this->lastAiProvider = 'none';
|
||||
$aiText = $this->generateClinicalTextWithGemini($prompt, 280);
|
||||
$aiText = $this->generateClinicalTextWithAi($prompt, 280);
|
||||
if ($aiText === null || trim($aiText) === '') {
|
||||
$fallback = $this->inferMedicineDetailsFromBrandName($name, $formulation);
|
||||
if (($fallback['genericname'] ?? '') !== '' || ($fallback['salt_name'] ?? '') !== '') {
|
||||
|
|
@ -2229,7 +2229,7 @@ class Opd_prescription extends BaseController
|
|||
]);
|
||||
}
|
||||
|
||||
return $this->response->setJSON(['update' => 0, 'error_text' => 'AI unavailable and no local match found. Check AI settings/API key.']);
|
||||
return $this->response->setJSON(['update' => 0, 'error_text' => 'AI unavailable and no local match found. Check AI settings.']);
|
||||
}
|
||||
|
||||
$parsed = $this->parseJsonFromAiResponse($aiText);
|
||||
|
|
@ -2605,11 +2605,11 @@ class Opd_prescription extends BaseController
|
|||
. "English Advice: {$sourceText}";
|
||||
|
||||
$this->lastAiProvider = 'none';
|
||||
$aiText = $this->generateClinicalTextWithGemini($prompt, 220);
|
||||
$aiText = $this->generateClinicalTextWithAi($prompt, 220);
|
||||
if ($aiText === null || trim($aiText) === '') {
|
||||
return $this->response->setJSON([
|
||||
'update' => 0,
|
||||
'error_text' => 'AI unavailable. Check AI settings/API key.',
|
||||
'error_text' => 'AI unavailable. Check AI settings.',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
|
|
@ -3014,13 +3014,13 @@ class Opd_prescription extends BaseController
|
|||
$finalComplaints = array_values($normalized);
|
||||
|
||||
$this->lastAiProvider = 'none';
|
||||
$geminiDraft = $this->generateComplaintDraftWithGemini($finalComplaints, $currentText);
|
||||
if ($geminiDraft !== null && trim($geminiDraft) !== '') {
|
||||
$provider = $this->lastAiProvider === 'azure' ? 'azure' : 'gemini';
|
||||
$providerLabel = $provider === 'azure' ? 'Azure OpenAI' : 'Gemini';
|
||||
$aiDraft = $this->generateComplaintDraftWithAi($finalComplaints, $currentText);
|
||||
if ($aiDraft !== null && trim($aiDraft) !== '') {
|
||||
$provider = $this->lastAiProvider === 'azure' ? 'azure' : 'ai';
|
||||
$providerLabel = $provider === 'azure' ? 'Azure OpenAI' : 'AI';
|
||||
return $this->response->setJSON([
|
||||
'update' => 1,
|
||||
'draft_text' => trim($geminiDraft),
|
||||
'draft_text' => trim($aiDraft),
|
||||
'error_text' => 'AI draft prepared using ' . $providerLabel,
|
||||
'ai_mode' => $provider,
|
||||
'csrfName' => csrf_token(),
|
||||
|
|
@ -5373,34 +5373,6 @@ class Opd_prescription extends BaseController
|
|||
return array_values($out);
|
||||
}
|
||||
|
||||
private function getGeminiApiKey(): string
|
||||
{
|
||||
foreach (['GEMINI_API_KEY', 'AI_GEMINI_API_KEY', 'APP_GEMINI_API_KEY', 'H_GEMINI_API_KEY'] as $constName) {
|
||||
if (defined($constName)) {
|
||||
$value = trim((string) constant($constName));
|
||||
if ($value !== '') {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->db->tableExists('hospital_setting')) {
|
||||
$row = $this->db->table('hospital_setting')
|
||||
->select('s_value')
|
||||
->whereIn('s_name', ['GEMINI_API_KEY', 'AI_GEMINI_API_KEY', 'APP_GEMINI_API_KEY', 'H_GEMINI_API_KEY'])
|
||||
->where('s_value !=', '')
|
||||
->orderBy('s_name', 'ASC')
|
||||
->get(1)
|
||||
->getRowArray();
|
||||
|
||||
if (! empty($row['s_value'])) {
|
||||
return trim((string) $row['s_value']);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{endpoint:string,api_key:string,deployment:string,api_version:string}
|
||||
*/
|
||||
|
|
@ -5475,7 +5447,7 @@ class Opd_prescription extends BaseController
|
|||
/**
|
||||
* @param array<int, string> $complaints
|
||||
*/
|
||||
private function generateComplaintDraftWithGemini(array $complaints, string $currentText): ?string
|
||||
private function generateComplaintDraftWithAi(array $complaints, string $currentText): ?string
|
||||
{
|
||||
if (empty($complaints)) {
|
||||
return null;
|
||||
|
|
@ -5487,7 +5459,7 @@ Input complaints: " . implode(', ', $complaints) . "
|
|||
Additional raw doctor notes: " . $currentText . "
|
||||
Output only one plain text sentence starting with 'Chief complaints:' and no markdown.";
|
||||
|
||||
return $this->generateClinicalTextWithGemini($prompt, 180);
|
||||
return $this->generateClinicalTextWithAi($prompt, 180);
|
||||
}
|
||||
|
||||
private function generateClinicalTextWithAzureOpenAi(string $prompt, int $maxOutputTokens = 300): ?string
|
||||
|
|
@ -5665,70 +5637,9 @@ Input: " . $text;
|
|||
return implode(' ', $sentences);
|
||||
}
|
||||
|
||||
private function generateClinicalTextWithGemini(string $prompt, int $maxOutputTokens = 300): ?string
|
||||
private function generateClinicalTextWithAi(string $prompt, int $maxOutputTokens = 300): ?string
|
||||
{
|
||||
$azureText = $this->generateClinicalTextWithAzureOpenAi($prompt, $maxOutputTokens);
|
||||
if ($azureText !== null && trim($azureText) !== '') {
|
||||
return $azureText;
|
||||
}
|
||||
|
||||
$apiKey = $this->getGeminiApiKey();
|
||||
if ($apiKey === '' || trim($prompt) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$httpOptions = [
|
||||
'timeout' => 20,
|
||||
'http_errors' => false,
|
||||
];
|
||||
if (defined('ENVIRONMENT') && in_array((string) ENVIRONMENT, ['development', 'testing'], true)) {
|
||||
$httpOptions['verify'] = false;
|
||||
}
|
||||
|
||||
$client = service('curlrequest', $httpOptions);
|
||||
$payload = [
|
||||
'contents' => [[
|
||||
'parts' => [[
|
||||
'text' => $prompt,
|
||||
]],
|
||||
]],
|
||||
'generationConfig' => [
|
||||
'temperature' => 0.2,
|
||||
'topP' => 0.9,
|
||||
'maxOutputTokens' => max(64, $maxOutputTokens),
|
||||
],
|
||||
];
|
||||
|
||||
foreach (['gemini-2.0-flash', 'gemini-1.5-flash', 'gemini-1.5-flash-latest'] as $modelName) {
|
||||
$url = 'https://generativelanguage.googleapis.com/v1beta/models/' . $modelName . ':generateContent?key=' . urlencode($apiKey);
|
||||
$response = $client->post($url, [
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
'json' => $payload,
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$decoded = json_decode((string) $response->getBody(), true);
|
||||
$parts = $decoded['candidates'][0]['content']['parts'] ?? [];
|
||||
$text = '';
|
||||
foreach ($parts as $part) {
|
||||
$text .= (string) ($part['text'] ?? '');
|
||||
}
|
||||
|
||||
$text = trim($text);
|
||||
if ($text !== '') {
|
||||
$this->lastAiProvider = 'gemini';
|
||||
return preg_replace('/\s+/', ' ', $text) ?: $text;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return $this->generateClinicalTextWithAzureOpenAi($prompt, $maxOutputTokens);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -6331,91 +6242,9 @@ OPD SNAPSHOT JSON: " . $payload;
|
|||
|
||||
private function extractClinicalTextFromScan(string $filePath, string $scanType = ''): ?string
|
||||
{
|
||||
$apiKey = $this->getGeminiApiKey();
|
||||
if ($apiKey === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
|
||||
$mimeMap = [
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'png' => 'image/png',
|
||||
'webp' => 'image/webp',
|
||||
'pdf' => 'application/pdf',
|
||||
];
|
||||
$mime = $mimeMap[$ext] ?? '';
|
||||
if ($mime === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$raw = @file_get_contents($filePath);
|
||||
if ($raw === false || $raw === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strlen($raw) > 8 * 1024 * 1024) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$typeHint = $scanType !== '' ? strtoupper($scanType) : 'GENERAL';
|
||||
$prompt = "You are a medical report transcription assistant.\n"
|
||||
. "Extract readable text from this " . $typeHint . " scan.\n"
|
||||
. "Return concise structured plain text with sections: Findings, Impression, Values(if present).\n"
|
||||
. "Do not invent data. If unreadable, mention unreadable fields.";
|
||||
|
||||
try {
|
||||
$client = service('curlrequest', [
|
||||
'timeout' => 30,
|
||||
'http_errors' => false,
|
||||
]);
|
||||
|
||||
$payload = [
|
||||
'contents' => [[
|
||||
'parts' => [
|
||||
['text' => $prompt],
|
||||
[
|
||||
'inlineData' => [
|
||||
'mimeType' => $mime,
|
||||
'data' => base64_encode($raw),
|
||||
],
|
||||
],
|
||||
],
|
||||
]],
|
||||
'generationConfig' => [
|
||||
'temperature' => 0.1,
|
||||
'topP' => 0.8,
|
||||
'maxOutputTokens' => 600,
|
||||
],
|
||||
];
|
||||
|
||||
foreach (['gemini-2.0-flash', 'gemini-1.5-flash'] as $modelName) {
|
||||
$url = 'https://generativelanguage.googleapis.com/v1beta/models/' . $modelName . ':generateContent?key=' . urlencode($apiKey);
|
||||
$response = $client->post($url, [
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
'json' => $payload,
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$decoded = json_decode((string) $response->getBody(), true);
|
||||
$parts = $decoded['candidates'][0]['content']['parts'] ?? [];
|
||||
$text = '';
|
||||
foreach ($parts as $part) {
|
||||
$text .= (string) ($part['text'] ?? '');
|
||||
}
|
||||
|
||||
$text = trim($text);
|
||||
if ($text !== '') {
|
||||
return preg_replace('/\s+/', ' ', $text) ?: $text;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Legacy external scan extraction has been removed from this module.
|
||||
// Keep returning null so existing fallback paths continue to work.
|
||||
unset($filePath, $scanType);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ class AiSettings extends BaseController
|
|||
'error_text' => 'Usage table unavailable',
|
||||
'usage' => [
|
||||
'total_today' => 0,
|
||||
'gemini_today' => 0,
|
||||
'ai_today' => 0,
|
||||
'fallback_today' => 0,
|
||||
'last_hour' => 0,
|
||||
'daily_limit' => 20,
|
||||
|
|
@ -154,18 +154,14 @@ class AiSettings extends BaseController
|
|||
->where('created_at <=', $end)
|
||||
->countAllResults();
|
||||
|
||||
$geminiToday = (int) $this->db->table('lab_ai_extraction_batches')
|
||||
->where('created_at >=', $start)
|
||||
->where('created_at <=', $end)
|
||||
->where("LOWER(ai_provider) = 'gemini'", null, false)
|
||||
->countAllResults();
|
||||
|
||||
$fallbackToday = (int) $this->db->table('lab_ai_extraction_batches')
|
||||
->where('created_at >=', $start)
|
||||
->where('created_at <=', $end)
|
||||
->where("LOWER(ai_provider) IN ('local-xray-fallback','regex-fallback')", null, false)
|
||||
->countAllResults();
|
||||
|
||||
$aiToday = max(0, $totalToday - $fallbackToday);
|
||||
|
||||
$lastHour = (int) $this->db->table('lab_ai_extraction_batches')
|
||||
->where('created_at >=', $hourStart)
|
||||
->where('created_at <=', $end)
|
||||
|
|
@ -192,7 +188,7 @@ class AiSettings extends BaseController
|
|||
];
|
||||
}
|
||||
|
||||
$ratio = (int) round(($geminiToday / max(1, $dailyLimit)) * 100);
|
||||
$ratio = (int) round(($aiToday / max(1, $dailyLimit)) * 100);
|
||||
$level = 'ok';
|
||||
if ($ratio >= 100) {
|
||||
$level = 'critical';
|
||||
|
|
@ -206,7 +202,7 @@ class AiSettings extends BaseController
|
|||
'update' => 1,
|
||||
'usage' => [
|
||||
'total_today' => $totalToday,
|
||||
'gemini_today' => $geminiToday,
|
||||
'ai_today' => $aiToday,
|
||||
'fallback_today' => $fallbackToday,
|
||||
'last_hour' => $lastHour,
|
||||
'daily_limit' => $dailyLimit,
|
||||
|
|
@ -249,7 +245,7 @@ class AiSettings extends BaseController
|
|||
$headers['Authorization'] = 'Bearer ' . $token;
|
||||
}
|
||||
|
||||
$client = service('curlrequest', $this->geminiHttpOptions());
|
||||
$client = service('curlrequest', $this->aiHttpOptions());
|
||||
$response = $client->post(rtrim($endpoint, '/'), [
|
||||
'headers' => $headers,
|
||||
'json' => [
|
||||
|
|
@ -326,7 +322,7 @@ class AiSettings extends BaseController
|
|||
$headers['Authorization'] = 'Bearer ' . $token;
|
||||
}
|
||||
|
||||
$client = service('curlrequest', $this->geminiHttpOptions());
|
||||
$client = service('curlrequest', $this->aiHttpOptions());
|
||||
$url = rtrim($baseUrl, '/') . '/translate?text=test&target_lang=en';
|
||||
$response = $client->get($url, [
|
||||
'headers' => $headers,
|
||||
|
|
@ -364,7 +360,7 @@ class AiSettings extends BaseController
|
|||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function geminiHttpOptions(): array
|
||||
private function aiHttpOptions(): array
|
||||
{
|
||||
$options = [
|
||||
'timeout' => 20,
|
||||
|
|
|
|||
|
|
@ -295,7 +295,8 @@ class Setup extends BaseController
|
|||
|
||||
try {
|
||||
$rows = $this->db->table($resolvedTable)->get()->getResultArray();
|
||||
$seedPayload['tables'][$resolvedTable] = is_array($rows) ? $rows : [];
|
||||
$rows = is_array($rows) ? $rows : [];
|
||||
$seedPayload['tables'][$resolvedTable] = $this->sanitizeSeedRows($resolvedTable, $rows);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'Seed export table read failed for {table}: {message}', ['table' => $resolvedTable, 'message' => $e->getMessage()]);
|
||||
$seedPayload['tables'][$resolvedTable] = [];
|
||||
|
|
@ -1103,6 +1104,59 @@ class Setup extends BaseController
|
|||
return array_values($out);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $rows
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function sanitizeSeedRows(string $tableName, array $rows): array
|
||||
{
|
||||
$table = strtolower(trim($tableName));
|
||||
if ($rows === []) {
|
||||
return $rows;
|
||||
}
|
||||
|
||||
if ($table === 'hospital_setting') {
|
||||
return array_values(array_filter($rows, function (array $row): bool {
|
||||
$name = strtoupper(trim((string) ($row['s_name'] ?? '')));
|
||||
return ! $this->isSensitiveSettingName($name);
|
||||
}));
|
||||
}
|
||||
|
||||
if ($table === 'clinical_audit_trail') {
|
||||
return array_values(array_filter($rows, function (array $row): bool {
|
||||
if (strtolower(trim((string) ($row['module'] ?? ''))) !== 'hospital_setting') {
|
||||
return true;
|
||||
}
|
||||
|
||||
$recordId = strtoupper(trim((string) ($row['record_id'] ?? '')));
|
||||
$fieldName = strtoupper(trim((string) ($row['field_name'] ?? '')));
|
||||
return ! ($this->isSensitiveSettingName($recordId) || $this->isSensitiveSettingName($fieldName));
|
||||
}));
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
private function isSensitiveSettingName(string $name): bool
|
||||
{
|
||||
if ($name === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (str_contains($name, 'API_KEY') || str_contains($name, 'TOKEN') || str_contains($name, 'SECRET')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (['GEMINI_', 'AZURE_', 'OPENAI_'] as $prefix) {
|
||||
if (str_starts_with($name, $prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1569,19 +1569,7 @@
|
|||
"created_at": "2026-02-21 13:36:59",
|
||||
"updated_at": "2026-02-21 13:36:59"
|
||||
},
|
||||
{
|
||||
"id": "7",
|
||||
"module": "hospital_setting",
|
||||
"record_id": "GEMINI_API_KEY",
|
||||
"field_name": "GEMINI_API_KEY",
|
||||
"old_value": "\"\"",
|
||||
"new_value": "\"REDACTED_GEMINI_API_KEY\"",
|
||||
"user_id": "1",
|
||||
"action_at": "2026-02-21 19:34:24",
|
||||
"hash": "dfc6370231d5fec4c562914546b5aea6fa79e1a8fe86d84ba9ba31c121cdcfcb",
|
||||
"created_at": "2026-02-21 14:04:24",
|
||||
"updated_at": "2026-02-21 14:04:24"
|
||||
},
|
||||
|
||||
{
|
||||
"id": "8",
|
||||
"module": "hospital_setting",
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Gemini Daily Limit (guardrail)</label>
|
||||
<label class="form-label">AI Daily Limit (guardrail)</label>
|
||||
<input type="number" min="1" max="10000" class="form-control" id="diagnosis_ai_daily_limit" value="<?= esc($diagnosis_ai_daily_limit ?? '20') ?>">
|
||||
<div class="form-text">Used for warning levels at 70% and 90%.</div>
|
||||
</div>
|
||||
|
|
@ -75,7 +75,7 @@
|
|||
<span class="badge bg-secondary" id="ai_usage_level">Unknown</span>
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col-md-3"><div class="small text-muted">Gemini Calls</div><div id="ai_usage_gemini" class="fw-bold">-</div></div>
|
||||
<div class="col-md-3"><div class="small text-muted">AI Calls</div><div id="ai_usage_ai" class="fw-bold">-</div></div>
|
||||
<div class="col-md-3"><div class="small text-muted">Fallback Calls</div><div id="ai_usage_fallback" class="fw-bold">-</div></div>
|
||||
<div class="col-md-3"><div class="small text-muted">Total Calls</div><div id="ai_usage_total" class="fw-bold">-</div></div>
|
||||
<div class="col-md-3"><div class="small text-muted">Last Hour</div><div id="ai_usage_hour" class="fw-bold">-</div></div>
|
||||
|
|
@ -204,7 +204,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
$('#ai_usage_gemini').text((usage.gemini_today || 0) + ' / ' + (usage.daily_limit || 0));
|
||||
$('#ai_usage_ai').text((usage.ai_today || 0) + ' / ' + (usage.daily_limit || 0));
|
||||
$('#ai_usage_fallback').text(usage.fallback_today || 0);
|
||||
$('#ai_usage_total').text(usage.total_today || 0);
|
||||
$('#ai_usage_hour').text(usage.last_hour || 0);
|
||||
|
|
@ -224,7 +224,7 @@
|
|||
}
|
||||
|
||||
$('#ai_usage_level').removeClass().addClass('badge ' + levelClass).text(levelText);
|
||||
$('#ai_usage_hint').text('Gemini utilization: ' + (usage.ratio || 0) + '%. Thresholds: 70% warning, 90% high.');
|
||||
$('#ai_usage_hint').text('AI utilization: ' + (usage.ratio || 0) + '%. Thresholds: 70% warning, 90% high.');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@
|
|||
$('#advice_hi').val((data.translated_text || '').toString());
|
||||
var langName = (data.target_language_name || '').toString();
|
||||
var provider = (data.provider || '').toString();
|
||||
var providerLabel = provider === 'azure' ? 'Azure OpenAI' : (provider === 'gemini' ? 'Gemini' : 'AI');
|
||||
var providerLabel = provider === 'azure' ? 'Azure OpenAI' : 'AI';
|
||||
setMsg('ok', 'Translated to ' + (langName || 'selected language') + ' using ' + providerLabel + '.');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
if (isNewMedicine && (provider === 'gemini' || provider === 'azure' || provider === 'local-brand-fallback')) {
|
||||
if (isNewMedicine && (provider === 'azure' || provider === 'local-brand-fallback')) {
|
||||
if (window.confirm('Details prepared for a new medicine. Save this new master entry now?')) {
|
||||
saveMedicine(buildMedicinePayload(), function() {
|
||||
setMsg('ok', 'New medicine saved with suggested generic/salt details.');
|
||||
|
|
|
|||
|
|
@ -12,10 +12,7 @@ $impressionInputId = 'ai_impression_' . $batchId;
|
|||
$providerKey = strtolower($provider);
|
||||
$providerLabel = $provider !== '' ? $provider : 'ai-server';
|
||||
$providerBadgeClass = 'bg-secondary';
|
||||
if ($providerKey === 'gemini') {
|
||||
$providerLabel = 'Gemini';
|
||||
$providerBadgeClass = 'bg-primary';
|
||||
} elseif ($providerKey === 'local-xray-fallback') {
|
||||
if ($providerKey === 'local-xray-fallback') {
|
||||
$providerLabel = 'Local X-ray Fallback';
|
||||
$providerBadgeClass = 'bg-warning text-dark';
|
||||
} elseif ($providerKey === 'regex-fallback') {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user